12 /* XXX struct tm on some systems (SunOS4/BSD) contains extra (non POSIX)
13 * fields for which we don't have Configure support prior to Perl 5.8.0:
14 * char *tm_zone; -- abbreviation of timezone name
15 * long tm_gmtoff; -- offset from GMT in seconds
16 * To workaround core dumps from the uninitialised tm_zone we get the
17 * system to give us a reasonable struct to copy. This fix means that
18 * strftime uses the tm_zone and tm_gmtoff values returned by
19 * localtime(time()). That should give the desired result most of the
20 * time. But probably not always!
22 * This is a vestigial workaround for Perls prior to 5.8.0. We now
23 * rely on the initialization (still likely a workaround) in util.c.
25 #if !defined(PERL_VERSION) || PERL_VERSION < 8
27 #if defined(HAS_GNULIBC)
28 # ifndef STRUCT_TM_HASZONE
29 # define STRUCT_TM_HASZONE
31 # define USE_TM_GMTOFF
35 #endif /* end of pre-5.8 */
37 #define DAYS_PER_YEAR 365
38 #define DAYS_PER_QYEAR (4*DAYS_PER_YEAR+1)
39 #define DAYS_PER_CENT (25*DAYS_PER_QYEAR-1)
40 #define DAYS_PER_QCENT (4*DAYS_PER_CENT+1)
41 #define SECS_PER_HOUR (60*60)
42 #define SECS_PER_DAY (24*SECS_PER_HOUR)
43 /* parentheses deliberately absent on these two, otherwise they don't work */
44 #define MONTH_TO_DAYS 153/5
45 #define DAYS_TO_MONTH 5/153
46 /* offset to bias by March (month 4) 1st between month/mday & year finding */
47 #define YEAR_ADJUST (4*MONTH_TO_DAYS+1)
48 /* as used here, the algorithm leaves Sunday as day 1 unless we adjust it */
49 #define WEEKDAY_BIAS 6 /* (1+6)%7 makes Sunday 0 again */
51 #if !defined(PERL_VERSION) || PERL_VERSION < 8
53 #ifdef STRUCT_TM_HASZONE
55 my_init_tm(struct tm *ptm) /* see mktime, strftime and asctime */
59 Copy(localtime(&now), ptm, 1, struct tm);
63 # define my_init_tm(ptm)
67 /* use core version from util.c in 5.8.0 and later */
68 # define my_init_tm init_tm
74 * (1) The CRT maintains its own copy of the environment, separate from
77 * (2) CRT getenv() retrieves from this copy. CRT putenv() updates this
78 * copy, and then calls SetEnvironmentVariableA() to update the Win32API
81 * (3) win32_getenv() and win32_putenv() call GetEnvironmentVariableA() and
82 * SetEnvironmentVariableA() directly, bypassing the CRT copy of the
85 * (4) The CRT strftime() "%Z" implementation calls __tzset(). That
86 * calls CRT tzset(), but only the first time it is called, and in turn
87 * that uses CRT getenv("TZ") to retrieve the timezone info from the CRT
88 * local copy of the environment and hence gets the original setting as
89 * perl never updates the CRT copy when assigning to $ENV{TZ}.
91 * Therefore, we need to retrieve the value of $ENV{TZ} and call CRT
92 * putenv() to update the CRT copy of the environment whenever we're about
97 win32_crt_getenv(const char* name)
100 const char* value = getenv(name);
101 #define getenv win32_getenv
106 win32_crt_putenv(const char* name, const char* value)
109 (char*)malloc((strlen(name) + strlen(value) + 2) * sizeof(char));
110 if (envstr != NULL) {
111 sprintf(envstr, "%s=%s", name, value);
114 #define putenv win32_putenv
120 fix_win32_tzenv(void)
122 const char* perl_tz_env = getenv("TZ");
123 const char* crt_tz_env = win32_crt_getenv("TZ");
124 if (perl_tz_env != NULL && crt_tz_env != NULL) {
125 if (strcmp(perl_tz_env, crt_tz_env) != 0)
126 win32_crt_putenv("TZ", perl_tz_env);
128 else if (perl_tz_env != NULL && crt_tz_env == NULL)
129 win32_crt_putenv("TZ", perl_tz_env);
130 else if (perl_tz_env == NULL && crt_tz_env != NULL)
131 win32_crt_putenv("TZ", "");
146 * my_mini_mktime - normalise struct tm values without the localtime()
147 * semantics (and overhead) of mktime(). Stolen shamelessly from Perl's
148 * Perl_mini_mktime() in util.c - for details on the algorithm, see that
152 my_mini_mktime(struct tm *ptm)
156 int month, mday, year, jday;
157 int odd_cent, odd_year;
159 year = 1900 + ptm->tm_year;
162 /* allow given yday with no month & mday to dominate the result */
163 if (ptm->tm_yday >= 0 && mday <= 0 && month <= 0) {
166 jday = 1 + ptm->tm_yday;
176 yearday = DAYS_PER_YEAR * year + year/4 - year/100 + year/400;
177 yearday += month*MONTH_TO_DAYS + mday + jday;
179 * Note that we don't know when leap-seconds were or will be,
180 * so we have to trust the user if we get something which looks
181 * like a sensible leap-second. Wild values for seconds will
182 * be rationalised, however.
184 if ((unsigned) ptm->tm_sec <= 60) {
191 secs += 60 * ptm->tm_min;
192 secs += SECS_PER_HOUR * ptm->tm_hour;
194 if (secs-(secs/SECS_PER_DAY*SECS_PER_DAY) < 0) {
195 /* got negative remainder, but need positive time */
196 /* back off an extra day to compensate */
197 yearday += (secs/SECS_PER_DAY)-1;
198 secs -= SECS_PER_DAY * (secs/SECS_PER_DAY - 1);
201 yearday += (secs/SECS_PER_DAY);
202 secs -= SECS_PER_DAY * (secs/SECS_PER_DAY);
205 else if (secs >= SECS_PER_DAY) {
206 yearday += (secs/SECS_PER_DAY);
207 secs %= SECS_PER_DAY;
209 ptm->tm_hour = secs/SECS_PER_HOUR;
210 secs %= SECS_PER_HOUR;
211 ptm->tm_min = secs/60;
214 /* done with time of day effects */
216 * The algorithm for yearday has (so far) left it high by 428.
217 * To avoid mistaking a legitimate Feb 29 as Mar 1, we need to
218 * bias it by 123 while trying to figure out what year it
219 * really represents. Even with this tweak, the reverse
220 * translation fails for years before A.D. 0001.
221 * It would still fail for Feb 29, but we catch that one below.
223 jday = yearday; /* save for later fixup vis-a-vis Jan 1 */
224 yearday -= YEAR_ADJUST;
225 year = (yearday / DAYS_PER_QCENT) * 400;
226 yearday %= DAYS_PER_QCENT;
227 odd_cent = yearday / DAYS_PER_CENT;
228 year += odd_cent * 100;
229 yearday %= DAYS_PER_CENT;
230 year += (yearday / DAYS_PER_QYEAR) * 4;
231 yearday %= DAYS_PER_QYEAR;
232 odd_year = yearday / DAYS_PER_YEAR;
234 yearday %= DAYS_PER_YEAR;
235 if (!yearday && (odd_cent==4 || odd_year==4)) { /* catch Feb 29 */
240 yearday += YEAR_ADJUST; /* recover March 1st crock */
241 month = yearday*DAYS_TO_MONTH;
242 yearday -= month*MONTH_TO_DAYS;
243 /* recover other leap-year adjustment */
252 ptm->tm_year = year - 1900;
254 ptm->tm_mday = yearday;
259 ptm->tm_mon = month - 1;
261 /* re-build yearday based on Jan 1 to get tm_yday */
263 yearday = year*DAYS_PER_YEAR + year/4 - year/100 + year/400;
264 yearday += 14*MONTH_TO_DAYS + 1;
265 ptm->tm_yday = jday - yearday;
266 /* fix tm_wday if not overridden by caller */
267 ptm->tm_wday = (jday + WEEKDAY_BIAS) % 7;
270 /* No strptime on Win32 or QNX4 */
271 #if defined(WIN32) || (defined(__QNX__) && defined(__WATCOMC__))
272 #define strncasecmp(x,y,n) strnicmp(x,y,n)
275 #if defined(__BORLANDC__)
276 void * __cdecl _EXPFUNC alloca(_SIZE_T __size);
278 #define alloca _alloca
282 /* strptime copied from freebsd with the following copyright: */
284 * Copyright (c) 1994 Powerdog Industries. All rights reserved.
286 * Redistribution and use in source and binary forms, with or without
287 * modification, are permitted provided that the following conditions
289 * 1. Redistributions of source code must retain the above copyright
290 * notice, this list of conditions and the following disclaimer.
291 * 2. Redistributions in binary form must reproduce the above copyright
292 * notice, this list of conditions and the following disclaimer
293 * in the documentation and/or other materials provided with the
295 * 3. All advertising materials mentioning features or use of this
296 * software must display the following acknowledgement:
297 * This product includes software developed by Powerdog Industries.
298 * 4. The name of Powerdog Industries may not be used to endorse or
299 * promote products derived from this software without specific prior
300 * written permission.
302 * THIS SOFTWARE IS PROVIDED BY POWERDOG INDUSTRIES ``AS IS'' AND ANY
303 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
304 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
305 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE POWERDOG INDUSTRIES BE
306 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
307 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
308 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
309 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
310 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
311 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
312 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
317 static char copyright[] =
318 "@(#) Copyright (c) 1994 Powerdog Industries. All rights reserved.";
319 static char sccsid[] = "@(#)strptime.c 0.1 (Powerdog) 94/03/27";
320 #endif /* !defined NOID */
321 #endif /* not lint */
328 #include "pthread_private.h"
329 #endif /* _THREAD_SAFE */
331 static char * _strptime(const char *, const char *, struct tm *);
334 static struct pthread_mutex _gotgmt_mutexd = PTHREAD_MUTEX_STATIC_INITIALIZER;
335 static pthread_mutex_t gotgmt_mutex = &_gotgmt_mutexd;
339 #define asizeof(a) (sizeof (a) / sizeof ((a)[0]))
342 const char * mon[12];
343 const char * month[12];
344 const char * wday[7];
345 const char * weekday[7];
351 const char * date_fmt;
352 const char * alt_month[12];
357 struct lc_time_T _time_localebuf;
358 int _time_using_locale;
360 const struct lc_time_T _C_time_locale = {
362 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
363 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
365 "January", "February", "March", "April", "May", "June",
366 "July", "August", "September", "October", "November", "December"
368 "Sun", "Mon", "Tue", "Wed",
371 "Sunday", "Monday", "Tuesday", "Wednesday",
372 "Thursday", "Friday", "Saturday"
380 ** Since the C language standard calls for
381 ** "date, using locale's date format," anything goes.
382 ** Using just numbers (as here) makes Quakers happier;
383 ** it's also compatible with SVR4.
388 ** c_fmt (ctime-compatible)
389 ** Not used, just compatibility placeholder.
403 "January", "February", "March", "April", "May", "June",
404 "July", "August", "September", "October", "November", "December"
408 ** To determine short months / day order
413 ** To determine long months / day order
418 #define Locale (&_C_time_locale)
421 _strptime(const char *buf, const char *fmt, struct tm *tm)
427 int Ealternative, Oalternative;
437 if (isspace((unsigned char)c))
438 while (*buf != 0 && isspace((unsigned char)*buf))
440 else if (c != *buf++)
457 buf = _strptime(buf, Locale->date_fmt, tm);
463 if (!isdigit((unsigned char)*buf))
466 /* XXX This will break for 3-digit centuries. */
468 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
476 tm->tm_year = i * 100 - 1900;
480 /* NOTE: c_fmt is intentionally ignored */
481 buf = _strptime(buf, "%a %Ef %T %Y", tm);
487 buf = _strptime(buf, "%m/%d/%y", tm);
493 if (Ealternative || Oalternative)
499 if (Ealternative || Oalternative)
508 buf = _strptime(buf, (c == 'f') ? Locale->Ef_fmt : Locale->EF_fmt, tm);
514 buf = _strptime(buf, "%H:%M", tm);
520 buf = _strptime(buf, "%I:%M:%S %p", tm);
526 buf = _strptime(buf, "%H:%M:%S", tm);
532 buf = _strptime(buf, Locale->X_fmt, tm);
538 buf = _strptime(buf, Locale->x_fmt, tm);
544 if (!isdigit((unsigned char)*buf))
548 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
553 if (i < 1 || i > 366)
561 if (*buf == 0 || isspace((unsigned char)*buf))
564 if (!isdigit((unsigned char)*buf))
568 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
584 if (*buf != 0 && isspace((unsigned char)*buf))
585 while (*ptr != 0 && !isspace((unsigned char)*ptr))
594 * Of these, %l is the only specifier explicitly
595 * documented as not being zero-padded. However,
596 * there is no harm in allowing zero-padding.
598 * XXX The %l specifier may gobble one too many
599 * digits if used incorrectly.
601 if (!isdigit((unsigned char)*buf))
605 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
610 if (c == 'H' || c == 'k') {
618 if (*buf != 0 && isspace((unsigned char)*buf))
619 while (*ptr != 0 && !isspace((unsigned char)*ptr))
625 * XXX This is bogus if parsed before hour-related
628 len = strlen(Locale->am);
629 if (strncasecmp(buf, Locale->am, len) == 0) {
630 if (tm->tm_hour > 12)
632 if (tm->tm_hour == 12)
638 len = strlen(Locale->pm);
639 if (strncasecmp(buf, Locale->pm, len) == 0) {
640 if (tm->tm_hour > 12)
642 if (tm->tm_hour != 12)
652 for (i = 0; i < asizeof(Locale->weekday); i++) {
654 len = strlen(Locale->weekday[i]);
660 len = strlen(Locale->wday[i]);
667 if (i == asizeof(Locale->weekday))
677 * XXX This is bogus, as we can not assume any valid
678 * information present in the tm structure at this
679 * point to calculate a real value, so just check the
682 if (!isdigit((unsigned char)*buf))
686 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
694 if (*buf != 0 && isspace((unsigned char)*buf))
695 while (*ptr != 0 && !isspace((unsigned char)*ptr))
700 if (!isdigit((unsigned char)*buf))
709 if (*buf != 0 && isspace((unsigned char)*buf))
710 while (*ptr != 0 && !isspace((unsigned char)*ptr))
717 * The %e specifier is explicitly documented as not
718 * being zero-padded but there is no harm in allowing
721 * XXX The %e specifier may gobble one too many
722 * digits if used incorrectly.
724 if (!isdigit((unsigned char)*buf))
728 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
738 if (*buf != 0 && isspace((unsigned char)*buf))
739 while (*ptr != 0 && !isspace((unsigned char)*ptr))
746 for (i = 0; i < asizeof(Locale->month); i++) {
749 len = strlen(Locale->alt_month[i]);
751 Locale->alt_month[i],
757 len = strlen(Locale->month[i]);
763 len = strlen(Locale->mon[i]);
771 if (i == asizeof(Locale->month))
779 if (!isdigit((unsigned char)*buf))
783 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
793 if (*buf != 0 && isspace((unsigned char)*buf))
794 while (*ptr != 0 && !isspace((unsigned char)*ptr))
800 if (*buf == 0 || isspace((unsigned char)*buf))
803 if (!isdigit((unsigned char)*buf))
806 len = (c == 'Y') ? 4 : 2;
807 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
814 if (c == 'y' && i < 69)
821 if (*buf != 0 && isspace((unsigned char)*buf))
822 while (*ptr != 0 && !isspace((unsigned char)*ptr))
831 for (cp = buf; *cp && isupper((unsigned char)*cp); ++cp)
834 zonestr = (char *)alloca(cp - buf + 1);
835 strncpy(zonestr, buf, cp - buf);
836 zonestr[cp - buf] = '\0';
838 if (0 == strcmp(zonestr, "GMT")) {
854 strptime(const char *buf, const char *fmt, struct tm *tm)
859 pthread_mutex_lock(&gotgmt_mutex);
863 ret = _strptime(buf, fmt, tm);
866 pthread_mutex_unlock(&gotgmt_mutex);
872 #endif /* Mac OS X */
874 MODULE = Time::Piece PACKAGE = Time::Piece
879 _strftime(fmt, sec, min, hour, mday, mon, year, wday = -1, yday = -1, isdst = -1)
895 memset(&mytm, 0, sizeof(mytm));
896 my_init_tm(&mytm); /* XXX workaround - see my_init_tm() above */
905 mytm.tm_isdst = isdst;
906 my_mini_mktime(&mytm);
907 len = strftime(tmpbuf, sizeof tmpbuf, fmt, &mytm);
909 ** The following is needed to handle to the situation where
910 ** tmpbuf overflows. Basically we want to allocate a buffer
911 ** and try repeatedly. The reason why it is so complicated
912 ** is that getting a return value of 0 from strftime can indicate
913 ** one of the following:
914 ** 1. buffer overflowed,
915 ** 2. illegal conversion specifier, or
916 ** 3. the format string specifies nothing to be returned(not
917 ** an error). This could be because format is an empty string
918 ** or it specifies %p that yields an empty string in some locale.
919 ** If there is a better way to make it portable, go ahead by
922 if ((len > 0 && len < sizeof(tmpbuf)) || (len == 0 && *fmt == '\0'))
923 ST(0) = sv_2mortal(newSVpv(tmpbuf, len));
925 /* Possibly buf overflowed - try again with a bigger buf */
926 int fmtlen = strlen(fmt);
927 int bufsize = fmtlen + sizeof(tmpbuf);
931 New(0, buf, bufsize, char);
933 buflen = strftime(buf, bufsize, fmt, &mytm);
934 if (buflen > 0 && buflen < bufsize)
936 /* heuristic to prevent out-of-memory errors */
937 if (bufsize > 100*fmtlen) {
943 Renew(buf, bufsize, char);
946 ST(0) = sv_2mortal(newSVpv(buf, buflen));
950 ST(0) = sv_2mortal(newSVpv(tmpbuf, len));
961 _strptime ( string, format )
972 remainder = (char *)strptime(string, format, &mytm);
974 if (remainder == NULL) {
975 croak("Error parsing time");
978 if (*remainder != '\0') {
979 warn("garbage at end of string in strptime: %s", remainder);
982 my_mini_mktime(&mytm);
984 /* warn("tm: %d-%d-%d %d:%d:%d\n", mytm.tm_year, mytm.tm_mon, mytm.tm_mday, mytm.tm_hour, mytm.tm_min, mytm.tm_sec); */
987 PUSHs(sv_2mortal(newSViv(mytm.tm_sec)));
988 PUSHs(sv_2mortal(newSViv(mytm.tm_min)));
989 PUSHs(sv_2mortal(newSViv(mytm.tm_hour)));
990 PUSHs(sv_2mortal(newSViv(mytm.tm_mday)));
991 PUSHs(sv_2mortal(newSViv(mytm.tm_mon)));
992 PUSHs(sv_2mortal(newSViv(mytm.tm_year)));
993 PUSHs(sv_2mortal(newSViv(mytm.tm_wday)));
994 PUSHs(sv_2mortal(newSViv(mytm.tm_yday)));
996 PUSHs(sv_2mortal(newSViv(0)));
998 PUSHs(sv_2mortal(newSViv(0)));
1000 PUSHs(sv_2mortal(newSViv(0)));
1003 _mini_mktime(int sec, int min, int hour, int mday, int mon, int year)
1013 mytm.tm_hour = hour;
1014 mytm.tm_mday = mday;
1016 mytm.tm_year = year;
1018 my_mini_mktime(&mytm);
1021 PUSHs(sv_2mortal(newSViv(mytm.tm_sec)));
1022 PUSHs(sv_2mortal(newSViv(mytm.tm_min)));
1023 PUSHs(sv_2mortal(newSViv(mytm.tm_hour)));
1024 PUSHs(sv_2mortal(newSViv(mytm.tm_mday)));
1025 PUSHs(sv_2mortal(newSViv(mytm.tm_mon)));
1026 PUSHs(sv_2mortal(newSViv(mytm.tm_year)));
1027 PUSHs(sv_2mortal(newSViv(mytm.tm_wday)));
1028 PUSHs(sv_2mortal(newSViv(mytm.tm_yday)));
1030 PUSHs(sv_2mortal(newSViv(0)));
1032 PUSHs(sv_2mortal(newSViv(0)));
1034 PUSHs(sv_2mortal(newSViv(0)));