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
72 * my_mini_mktime - normalise struct tm values without the localtime()
73 * semantics (and overhead) of mktime(). Stolen shamelessly from Perl's
74 * Perl_mini_mktime() in util.c - for details on the algorithm, see that
78 my_mini_mktime(struct tm *ptm)
82 int month, mday, year, jday;
83 int odd_cent, odd_year;
85 year = 1900 + ptm->tm_year;
88 /* allow given yday with no month & mday to dominate the result */
89 if (ptm->tm_yday >= 0 && mday <= 0 && month <= 0) {
92 jday = 1 + ptm->tm_yday;
102 yearday = DAYS_PER_YEAR * year + year/4 - year/100 + year/400;
103 yearday += month*MONTH_TO_DAYS + mday + jday;
105 * Note that we don't know when leap-seconds were or will be,
106 * so we have to trust the user if we get something which looks
107 * like a sensible leap-second. Wild values for seconds will
108 * be rationalised, however.
110 if ((unsigned) ptm->tm_sec <= 60) {
117 secs += 60 * ptm->tm_min;
118 secs += SECS_PER_HOUR * ptm->tm_hour;
120 if (secs-(secs/SECS_PER_DAY*SECS_PER_DAY) < 0) {
121 /* got negative remainder, but need positive time */
122 /* back off an extra day to compensate */
123 yearday += (secs/SECS_PER_DAY)-1;
124 secs -= SECS_PER_DAY * (secs/SECS_PER_DAY - 1);
127 yearday += (secs/SECS_PER_DAY);
128 secs -= SECS_PER_DAY * (secs/SECS_PER_DAY);
131 else if (secs >= SECS_PER_DAY) {
132 yearday += (secs/SECS_PER_DAY);
133 secs %= SECS_PER_DAY;
135 ptm->tm_hour = secs/SECS_PER_HOUR;
136 secs %= SECS_PER_HOUR;
137 ptm->tm_min = secs/60;
140 /* done with time of day effects */
142 * The algorithm for yearday has (so far) left it high by 428.
143 * To avoid mistaking a legitimate Feb 29 as Mar 1, we need to
144 * bias it by 123 while trying to figure out what year it
145 * really represents. Even with this tweak, the reverse
146 * translation fails for years before A.D. 0001.
147 * It would still fail for Feb 29, but we catch that one below.
149 jday = yearday; /* save for later fixup vis-a-vis Jan 1 */
150 yearday -= YEAR_ADJUST;
151 year = (yearday / DAYS_PER_QCENT) * 400;
152 yearday %= DAYS_PER_QCENT;
153 odd_cent = yearday / DAYS_PER_CENT;
154 year += odd_cent * 100;
155 yearday %= DAYS_PER_CENT;
156 year += (yearday / DAYS_PER_QYEAR) * 4;
157 yearday %= DAYS_PER_QYEAR;
158 odd_year = yearday / DAYS_PER_YEAR;
160 yearday %= DAYS_PER_YEAR;
161 if (!yearday && (odd_cent==4 || odd_year==4)) { /* catch Feb 29 */
166 yearday += YEAR_ADJUST; /* recover March 1st crock */
167 month = yearday*DAYS_TO_MONTH;
168 yearday -= month*MONTH_TO_DAYS;
169 /* recover other leap-year adjustment */
178 ptm->tm_year = year - 1900;
180 ptm->tm_mday = yearday;
185 ptm->tm_mon = month - 1;
187 /* re-build yearday based on Jan 1 to get tm_yday */
189 yearday = year*DAYS_PER_YEAR + year/4 - year/100 + year/400;
190 yearday += 14*MONTH_TO_DAYS + 1;
191 ptm->tm_yday = jday - yearday;
192 /* fix tm_wday if not overridden by caller */
193 ptm->tm_wday = (jday + WEEKDAY_BIAS) % 7;
196 /* No strptime on Win32 or QNX4 */
197 #if defined(WIN32) || (defined(__QNX__) && defined(__WATCOMC__))
198 #define strncasecmp(x,y,n) strnicmp(x,y,n)
201 #if defined(__BORLANDC__)
202 void * __cdecl _EXPFUNC alloca(_SIZE_T __size);
204 #define alloca _alloca
208 /* strptime copied from freebsd with the following copyright: */
210 * Copyright (c) 1994 Powerdog Industries. All rights reserved.
212 * Redistribution and use in source and binary forms, with or without
213 * modification, are permitted provided that the following conditions
215 * 1. Redistributions of source code must retain the above copyright
216 * notice, this list of conditions and the following disclaimer.
217 * 2. Redistributions in binary form must reproduce the above copyright
218 * notice, this list of conditions and the following disclaimer
219 * in the documentation and/or other materials provided with the
221 * 3. All advertising materials mentioning features or use of this
222 * software must display the following acknowledgement:
223 * This product includes software developed by Powerdog Industries.
224 * 4. The name of Powerdog Industries may not be used to endorse or
225 * promote products derived from this software without specific prior
226 * written permission.
228 * THIS SOFTWARE IS PROVIDED BY POWERDOG INDUSTRIES ``AS IS'' AND ANY
229 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
230 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
231 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE POWERDOG INDUSTRIES BE
232 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
233 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
234 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
235 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
236 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
237 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
238 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
243 static char copyright[] =
244 "@(#) Copyright (c) 1994 Powerdog Industries. All rights reserved.";
245 static char sccsid[] = "@(#)strptime.c 0.1 (Powerdog) 94/03/27";
246 #endif /* !defined NOID */
247 #endif /* not lint */
254 #include "pthread_private.h"
255 #endif /* _THREAD_SAFE */
257 static char * _strptime(const char *, const char *, struct tm *);
260 static struct pthread_mutex _gotgmt_mutexd = PTHREAD_MUTEX_STATIC_INITIALIZER;
261 static pthread_mutex_t gotgmt_mutex = &_gotgmt_mutexd;
265 #define asizeof(a) (sizeof (a) / sizeof ((a)[0]))
268 const char * mon[12];
269 const char * month[12];
270 const char * wday[7];
271 const char * weekday[7];
277 const char * date_fmt;
278 const char * alt_month[12];
283 struct lc_time_T _time_localebuf;
284 int _time_using_locale;
286 const struct lc_time_T _C_time_locale = {
288 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
289 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
291 "January", "February", "March", "April", "May", "June",
292 "July", "August", "September", "October", "November", "December"
294 "Sun", "Mon", "Tue", "Wed",
297 "Sunday", "Monday", "Tuesday", "Wednesday",
298 "Thursday", "Friday", "Saturday"
306 ** Since the C language standard calls for
307 ** "date, using locale's date format," anything goes.
308 ** Using just numbers (as here) makes Quakers happier;
309 ** it's also compatible with SVR4.
314 ** c_fmt (ctime-compatible)
315 ** Not used, just compatibility placeholder.
329 "January", "February", "March", "April", "May", "June",
330 "July", "August", "September", "October", "November", "December"
334 ** To determine short months / day order
339 ** To determine long months / day order
344 #define Locale (&_C_time_locale)
347 _strptime(const char *buf, const char *fmt, struct tm *tm)
353 int Ealternative, Oalternative;
363 if (isspace((unsigned char)c))
364 while (*buf != 0 && isspace((unsigned char)*buf))
366 else if (c != *buf++)
383 buf = _strptime(buf, Locale->date_fmt, tm);
389 if (!isdigit((unsigned char)*buf))
392 /* XXX This will break for 3-digit centuries. */
394 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
402 tm->tm_year = i * 100 - 1900;
406 /* NOTE: c_fmt is intentionally ignored */
407 buf = _strptime(buf, "%a %Ef %T %Y", tm);
413 buf = _strptime(buf, "%m/%d/%y", tm);
419 if (Ealternative || Oalternative)
425 if (Ealternative || Oalternative)
434 buf = _strptime(buf, (c == 'f') ? Locale->Ef_fmt : Locale->EF_fmt, tm);
440 buf = _strptime(buf, "%H:%M", tm);
446 buf = _strptime(buf, "%I:%M:%S %p", tm);
452 buf = _strptime(buf, "%H:%M:%S", tm);
458 buf = _strptime(buf, Locale->X_fmt, tm);
464 buf = _strptime(buf, Locale->x_fmt, tm);
470 if (!isdigit((unsigned char)*buf))
474 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
479 if (i < 1 || i > 366)
487 if (*buf == 0 || isspace((unsigned char)*buf))
490 if (!isdigit((unsigned char)*buf))
494 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
510 if (*buf != 0 && isspace((unsigned char)*buf))
511 while (*ptr != 0 && !isspace((unsigned char)*ptr))
520 * Of these, %l is the only specifier explicitly
521 * documented as not being zero-padded. However,
522 * there is no harm in allowing zero-padding.
524 * XXX The %l specifier may gobble one too many
525 * digits if used incorrectly.
527 if (!isdigit((unsigned char)*buf))
531 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
536 if (c == 'H' || c == 'k') {
544 if (*buf != 0 && isspace((unsigned char)*buf))
545 while (*ptr != 0 && !isspace((unsigned char)*ptr))
551 * XXX This is bogus if parsed before hour-related
554 len = strlen(Locale->am);
555 if (strncasecmp(buf, Locale->am, len) == 0) {
556 if (tm->tm_hour > 12)
558 if (tm->tm_hour == 12)
564 len = strlen(Locale->pm);
565 if (strncasecmp(buf, Locale->pm, len) == 0) {
566 if (tm->tm_hour > 12)
568 if (tm->tm_hour != 12)
578 for (i = 0; i < asizeof(Locale->weekday); i++) {
580 len = strlen(Locale->weekday[i]);
586 len = strlen(Locale->wday[i]);
593 if (i == asizeof(Locale->weekday))
603 * XXX This is bogus, as we can not assume any valid
604 * information present in the tm structure at this
605 * point to calculate a real value, so just check the
608 if (!isdigit((unsigned char)*buf))
612 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
620 if (*buf != 0 && isspace((unsigned char)*buf))
621 while (*ptr != 0 && !isspace((unsigned char)*ptr))
626 if (!isdigit((unsigned char)*buf))
635 if (*buf != 0 && isspace((unsigned char)*buf))
636 while (*ptr != 0 && !isspace((unsigned char)*ptr))
643 * The %e specifier is explicitly documented as not
644 * being zero-padded but there is no harm in allowing
647 * XXX The %e specifier may gobble one too many
648 * digits if used incorrectly.
650 if (!isdigit((unsigned char)*buf))
654 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
664 if (*buf != 0 && isspace((unsigned char)*buf))
665 while (*ptr != 0 && !isspace((unsigned char)*ptr))
672 for (i = 0; i < asizeof(Locale->month); i++) {
675 len = strlen(Locale->alt_month[i]);
677 Locale->alt_month[i],
683 len = strlen(Locale->month[i]);
689 len = strlen(Locale->mon[i]);
697 if (i == asizeof(Locale->month))
705 if (!isdigit((unsigned char)*buf))
709 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
719 if (*buf != 0 && isspace((unsigned char)*buf))
720 while (*ptr != 0 && !isspace((unsigned char)*ptr))
726 if (*buf == 0 || isspace((unsigned char)*buf))
729 if (!isdigit((unsigned char)*buf))
732 len = (c == 'Y') ? 4 : 2;
733 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
740 if (c == 'y' && i < 69)
747 if (*buf != 0 && isspace((unsigned char)*buf))
748 while (*ptr != 0 && !isspace((unsigned char)*ptr))
757 for (cp = buf; *cp && isupper((unsigned char)*cp); ++cp)
760 zonestr = (char *)alloca(cp - buf + 1);
761 strncpy(zonestr, buf, cp - buf);
762 zonestr[cp - buf] = '\0';
764 if (0 == strcmp(zonestr, "GMT")) {
780 strptime(const char *buf, const char *fmt, struct tm *tm)
785 pthread_mutex_lock(&gotgmt_mutex);
789 ret = _strptime(buf, fmt, tm);
792 pthread_mutex_unlock(&gotgmt_mutex);
798 #endif /* Mac OS X */
800 MODULE = Time::Piece PACKAGE = Time::Piece
805 _strftime(fmt, sec, min, hour, mday, mon, year, wday = -1, yday = -1, isdst = -1)
821 memset(&mytm, 0, sizeof(mytm));
822 my_init_tm(&mytm); /* XXX workaround - see my_init_tm() above */
831 mytm.tm_isdst = isdst;
832 my_mini_mktime(&mytm);
833 len = strftime(tmpbuf, sizeof tmpbuf, fmt, &mytm);
835 ** The following is needed to handle to the situation where
836 ** tmpbuf overflows. Basically we want to allocate a buffer
837 ** and try repeatedly. The reason why it is so complicated
838 ** is that getting a return value of 0 from strftime can indicate
839 ** one of the following:
840 ** 1. buffer overflowed,
841 ** 2. illegal conversion specifier, or
842 ** 3. the format string specifies nothing to be returned(not
843 ** an error). This could be because format is an empty string
844 ** or it specifies %p that yields an empty string in some locale.
845 ** If there is a better way to make it portable, go ahead by
848 if ((len > 0 && len < sizeof(tmpbuf)) || (len == 0 && *fmt == '\0'))
849 ST(0) = sv_2mortal(newSVpv(tmpbuf, len));
851 /* Possibly buf overflowed - try again with a bigger buf */
852 int fmtlen = strlen(fmt);
853 int bufsize = fmtlen + sizeof(tmpbuf);
857 New(0, buf, bufsize, char);
859 buflen = strftime(buf, bufsize, fmt, &mytm);
860 if (buflen > 0 && buflen < bufsize)
862 /* heuristic to prevent out-of-memory errors */
863 if (bufsize > 100*fmtlen) {
869 Renew(buf, bufsize, char);
872 ST(0) = sv_2mortal(newSVpv(buf, buflen));
876 ST(0) = sv_2mortal(newSVpv(tmpbuf, len));
887 _strptime ( string, format )
898 remainder = (char *)strptime(string, format, &mytm);
900 if (remainder == NULL) {
901 croak("Error parsing time");
904 if (*remainder != '\0') {
905 warn("garbage at end of string in strptime: %s", remainder);
908 my_mini_mktime(&mytm);
910 /* 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); */
913 PUSHs(sv_2mortal(newSViv(mytm.tm_sec)));
914 PUSHs(sv_2mortal(newSViv(mytm.tm_min)));
915 PUSHs(sv_2mortal(newSViv(mytm.tm_hour)));
916 PUSHs(sv_2mortal(newSViv(mytm.tm_mday)));
917 PUSHs(sv_2mortal(newSViv(mytm.tm_mon)));
918 PUSHs(sv_2mortal(newSViv(mytm.tm_year)));
919 PUSHs(sv_2mortal(newSViv(mytm.tm_wday)));
920 PUSHs(sv_2mortal(newSViv(mytm.tm_yday)));
922 PUSHs(sv_2mortal(newSViv(0)));
924 PUSHs(sv_2mortal(newSViv(0)));
926 PUSHs(sv_2mortal(newSViv(0)));
929 _mini_mktime(int sec, int min, int hour, int mday, int mon, int year)
944 my_mini_mktime(&mytm);
947 PUSHs(sv_2mortal(newSViv(mytm.tm_sec)));
948 PUSHs(sv_2mortal(newSViv(mytm.tm_min)));
949 PUSHs(sv_2mortal(newSViv(mytm.tm_hour)));
950 PUSHs(sv_2mortal(newSViv(mytm.tm_mday)));
951 PUSHs(sv_2mortal(newSViv(mytm.tm_mon)));
952 PUSHs(sv_2mortal(newSViv(mytm.tm_year)));
953 PUSHs(sv_2mortal(newSViv(mytm.tm_wday)));
954 PUSHs(sv_2mortal(newSViv(mytm.tm_yday)));
956 PUSHs(sv_2mortal(newSViv(0)));
958 PUSHs(sv_2mortal(newSViv(0)));
960 PUSHs(sv_2mortal(newSViv(0)));