12 /* XXX struct tm on some systems (SunOS4/BSD) contains extra (non POSIX)
13 * fields for which we don't have Configure support yet:
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 temporary workaround to be removed once Configure
23 * support is added and NETaa14816 is considered in full.
24 * It does not address tzname aspects of NETaa14816.
26 #if !defined(HAS_GNULIBC)
27 # ifndef STRUCT_TM_HASZONE
28 # define STRUCT_TM_HASZONE
30 # define USE_TM_GMTOFF
34 #define DAYS_PER_YEAR 365
35 #define DAYS_PER_QYEAR (4*DAYS_PER_YEAR+1)
36 #define DAYS_PER_CENT (25*DAYS_PER_QYEAR-1)
37 #define DAYS_PER_QCENT (4*DAYS_PER_CENT+1)
38 #define SECS_PER_HOUR (60*60)
39 #define SECS_PER_DAY (24*SECS_PER_HOUR)
40 /* parentheses deliberately absent on these two, otherwise they don't work */
41 #define MONTH_TO_DAYS 153/5
42 #define DAYS_TO_MONTH 5/153
43 /* offset to bias by March (month 4) 1st between month/mday & year finding */
44 #define YEAR_ADJUST (4*MONTH_TO_DAYS+1)
45 /* as used here, the algorithm leaves Sunday as day 1 unless we adjust it */
46 #define WEEKDAY_BIAS 6 /* (1+6)%7 makes Sunday 0 again */
48 #ifdef STRUCT_TM_HASZONE
50 my_init_tm(struct tm *ptm) /* see mktime, strftime and asctime */
54 Copy(localtime(&now), ptm, 1, struct tm);
58 # define my_init_tm(ptm)
62 * my_mini_mktime - normalise struct tm values without the localtime()
63 * semantics (and overhead) of mktime().
66 my_mini_mktime(struct tm *ptm)
70 int month, mday, year, jday;
71 int odd_cent, odd_year;
74 * Year/day algorithm notes:
76 * With a suitable offset for numeric value of the month, one can find
77 * an offset into the year by considering months to have 30.6 (153/5) days,
78 * using integer arithmetic (i.e., with truncation). To avoid too much
79 * messing about with leap days, we consider January and February to be
80 * the 13th and 14th month of the previous year. After that transformation,
81 * we need the month index we use to be high by 1 from 'normal human' usage,
82 * so the month index values we use run from 4 through 15.
84 * Given that, and the rules for the Gregorian calendar (leap years are those
85 * divisible by 4 unless also divisible by 100, when they must be divisible
86 * by 400 instead), we can simply calculate the number of days since some
87 * arbitrary 'beginning of time' by futzing with the (adjusted) year number,
88 * the days we derive from our month index, and adding in the day of the
89 * month. The value used here is not adjusted for the actual origin which
90 * it normally would use (1 January A.D. 1), since we're not exposing it.
91 * We're only building the value so we can turn around and get the
92 * normalised values for the year, month, day-of-month, and day-of-year.
94 * For going backward, we need to bias the value we're using so that we find
95 * the right year value. (Basically, we don't want the contribution of
96 * March 1st to the number to apply while deriving the year). Having done
97 * that, we 'count up' the contribution to the year number by accounting for
98 * full quadracenturies (400-year periods) with their extra leap days, plus
99 * the contribution from full centuries (to avoid counting in the lost leap
100 * days), plus the contribution from full quad-years (to count in the normal
101 * leap days), plus the leftover contribution from any non-leap years.
102 * At this point, if we were working with an actual leap day, we'll have 0
103 * days left over. This is also true for March 1st, however. So, we have
104 * to special-case that result, and (earlier) keep track of the 'odd'
105 * century and year contributions. If we got 4 extra centuries in a qcent,
106 * or 4 extra years in a qyear, then it's a leap day and we call it 29 Feb.
107 * Otherwise, we add back in the earlier bias we removed (the 123 from
108 * figuring in March 1st), find the month index (integer division by 30.6),
109 * and the remainder is the day-of-month. We then have to convert back to
110 * 'real' months (including fixing January and February from being 14/15 in
111 * the previous year to being in the proper year). After that, to get
112 * tm_yday, we work with the normalised year and get a new yearday value for
113 * January 1st, which we subtract from the yearday value we had earlier,
114 * representing the date we've re-built. This is done from January 1
115 * because tm_yday is 0-origin.
117 * Since POSIX time routines are only guaranteed to work for times since the
118 * UNIX epoch (00:00:00 1 Jan 1970 UTC), the fact that this algorithm
119 * applies Gregorian calendar rules even to dates before the 16th century
120 * doesn't bother me. Besides, you'd need cultural context for a given
121 * date to know whether it was Julian or Gregorian calendar, and that's
122 * outside the scope for this routine. Since we convert back based on the
123 * same rules we used to build the yearday, you'll only get strange results
124 * for input which needed normalising, or for the 'odd' century years which
125 * were leap years in the Julian calander but not in the Gregorian one.
126 * I can live with that.
128 * This algorithm also fails to handle years before A.D. 1 gracefully, but
129 * that's still outside the scope for POSIX time manipulation, so I don't
133 year = 1900 + ptm->tm_year;
136 /* allow given yday with no month & mday to dominate the result */
137 if (ptm->tm_yday >= 0 && mday <= 0 && month <= 0) {
140 jday = 1 + ptm->tm_yday;
150 yearday = DAYS_PER_YEAR * year + year/4 - year/100 + year/400;
151 yearday += month*MONTH_TO_DAYS + mday + jday;
153 * Note that we don't know when leap-seconds were or will be,
154 * so we have to trust the user if we get something which looks
155 * like a sensible leap-second. Wild values for seconds will
156 * be rationalised, however.
158 if ((unsigned) ptm->tm_sec <= 60) {
165 secs += 60 * ptm->tm_min;
166 secs += SECS_PER_HOUR * ptm->tm_hour;
168 if (secs-(secs/SECS_PER_DAY*SECS_PER_DAY) < 0) {
169 /* got negative remainder, but need positive time */
170 /* back off an extra day to compensate */
171 yearday += (secs/SECS_PER_DAY)-1;
172 secs -= SECS_PER_DAY * (secs/SECS_PER_DAY - 1);
175 yearday += (secs/SECS_PER_DAY);
176 secs -= SECS_PER_DAY * (secs/SECS_PER_DAY);
179 else if (secs >= SECS_PER_DAY) {
180 yearday += (secs/SECS_PER_DAY);
181 secs %= SECS_PER_DAY;
183 ptm->tm_hour = secs/SECS_PER_HOUR;
184 secs %= SECS_PER_HOUR;
185 ptm->tm_min = secs/60;
188 /* done with time of day effects */
190 * The algorithm for yearday has (so far) left it high by 428.
191 * To avoid mistaking a legitimate Feb 29 as Mar 1, we need to
192 * bias it by 123 while trying to figure out what year it
193 * really represents. Even with this tweak, the reverse
194 * translation fails for years before A.D. 0001.
195 * It would still fail for Feb 29, but we catch that one below.
197 jday = yearday; /* save for later fixup vis-a-vis Jan 1 */
198 yearday -= YEAR_ADJUST;
199 year = (yearday / DAYS_PER_QCENT) * 400;
200 yearday %= DAYS_PER_QCENT;
201 odd_cent = yearday / DAYS_PER_CENT;
202 year += odd_cent * 100;
203 yearday %= DAYS_PER_CENT;
204 year += (yearday / DAYS_PER_QYEAR) * 4;
205 yearday %= DAYS_PER_QYEAR;
206 odd_year = yearday / DAYS_PER_YEAR;
208 yearday %= DAYS_PER_YEAR;
209 if (!yearday && (odd_cent==4 || odd_year==4)) { /* catch Feb 29 */
214 yearday += YEAR_ADJUST; /* recover March 1st crock */
215 month = yearday*DAYS_TO_MONTH;
216 yearday -= month*MONTH_TO_DAYS;
217 /* recover other leap-year adjustment */
226 ptm->tm_year = year - 1900;
228 ptm->tm_mday = yearday;
233 ptm->tm_mon = month - 1;
235 /* re-build yearday based on Jan 1 to get tm_yday */
237 yearday = year*DAYS_PER_YEAR + year/4 - year/100 + year/400;
238 yearday += 14*MONTH_TO_DAYS + 1;
239 ptm->tm_yday = jday - yearday;
240 /* fix tm_wday if not overridden by caller */
241 ptm->tm_wday = (jday + WEEKDAY_BIAS) % 7;
244 #if defined(WIN32) || (defined(__QNX__) && defined(__WATCOMC__)) /* No strptime on Win32 or QNX4 */
245 #define strncasecmp(x,y,n) strnicmp(x,y,n)
248 #if defined(__BORLANDC__)
249 void * __cdecl _EXPFUNC alloca(_SIZE_T __size);
251 #define alloca _alloca
260 #include "pthread_private.h"
261 #endif /* _THREAD_SAFE */
263 static char * _strptime(const char *, const char *, struct tm *);
266 static struct pthread_mutex _gotgmt_mutexd = PTHREAD_MUTEX_STATIC_INITIALIZER;
267 static pthread_mutex_t gotgmt_mutex = &_gotgmt_mutexd;
271 #define asizeof(a) (sizeof (a) / sizeof ((a)[0]))
274 const char * mon[12];
275 const char * month[12];
276 const char * wday[7];
277 const char * weekday[7];
283 const char * date_fmt;
284 const char * alt_month[12];
289 struct lc_time_T _time_localebuf;
290 int _time_using_locale;
292 const struct lc_time_T _C_time_locale = {
294 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
295 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
297 "January", "February", "March", "April", "May", "June",
298 "July", "August", "September", "October", "November", "December"
300 "Sun", "Mon", "Tue", "Wed",
303 "Sunday", "Monday", "Tuesday", "Wednesday",
304 "Thursday", "Friday", "Saturday"
312 ** Since the C language standard calls for
313 ** "date, using locale's date format," anything goes.
314 ** Using just numbers (as here) makes Quakers happier;
315 ** it's also compatible with SVR4.
320 ** c_fmt (ctime-compatible)
321 ** Not used, just compatibility placeholder.
335 "January", "February", "March", "April", "May", "June",
336 "July", "August", "September", "October", "November", "December"
340 ** To determine short months / day order
345 ** To determine long months / day order
350 #define Locale (&_C_time_locale)
353 _strptime(const char *buf, const char *fmt, struct tm *tm)
359 int Ealternative, Oalternative;
369 if (isspace((unsigned char)c))
370 while (*buf != 0 && isspace((unsigned char)*buf))
372 else if (c != *buf++)
389 buf = _strptime(buf, Locale->date_fmt, tm);
395 if (!isdigit((unsigned char)*buf))
398 /* XXX This will break for 3-digit centuries. */
400 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
408 tm->tm_year = i * 100 - 1900;
412 /* NOTE: c_fmt is intentionally ignored */
413 buf = _strptime(buf, "%a %Ef %T %Y", tm);
419 buf = _strptime(buf, "%m/%d/%y", tm);
425 if (Ealternative || Oalternative)
431 if (Ealternative || Oalternative)
440 buf = _strptime(buf, (c == 'f') ? Locale->Ef_fmt : Locale->EF_fmt, tm);
446 buf = _strptime(buf, "%H:%M", tm);
452 buf = _strptime(buf, "%I:%M:%S %p", tm);
458 buf = _strptime(buf, "%H:%M:%S", tm);
464 buf = _strptime(buf, Locale->X_fmt, tm);
470 buf = _strptime(buf, Locale->x_fmt, tm);
476 if (!isdigit((unsigned char)*buf))
480 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
485 if (i < 1 || i > 366)
493 if (*buf == 0 || isspace((unsigned char)*buf))
496 if (!isdigit((unsigned char)*buf))
500 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
516 if (*buf != 0 && isspace((unsigned char)*buf))
517 while (*ptr != 0 && !isspace((unsigned char)*ptr))
526 * Of these, %l is the only specifier explicitly
527 * documented as not being zero-padded. However,
528 * there is no harm in allowing zero-padding.
530 * XXX The %l specifier may gobble one too many
531 * digits if used incorrectly.
533 if (!isdigit((unsigned char)*buf))
537 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
542 if (c == 'H' || c == 'k') {
550 if (*buf != 0 && isspace((unsigned char)*buf))
551 while (*ptr != 0 && !isspace((unsigned char)*ptr))
557 * XXX This is bogus if parsed before hour-related
560 len = strlen(Locale->am);
561 if (strncasecmp(buf, Locale->am, len) == 0) {
562 if (tm->tm_hour > 12)
564 if (tm->tm_hour == 12)
570 len = strlen(Locale->pm);
571 if (strncasecmp(buf, Locale->pm, len) == 0) {
572 if (tm->tm_hour > 12)
574 if (tm->tm_hour != 12)
584 for (i = 0; i < asizeof(Locale->weekday); i++) {
586 len = strlen(Locale->weekday[i]);
592 len = strlen(Locale->wday[i]);
599 if (i == asizeof(Locale->weekday))
609 * XXX This is bogus, as we can not assume any valid
610 * information present in the tm structure at this
611 * point to calculate a real value, so just check the
614 if (!isdigit((unsigned char)*buf))
618 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
626 if (*buf != 0 && isspace((unsigned char)*buf))
627 while (*ptr != 0 && !isspace((unsigned char)*ptr))
632 if (!isdigit((unsigned char)*buf))
641 if (*buf != 0 && isspace((unsigned char)*buf))
642 while (*ptr != 0 && !isspace((unsigned char)*ptr))
649 * The %e specifier is explicitly documented as not
650 * being zero-padded but there is no harm in allowing
653 * XXX The %e specifier may gobble one too many
654 * digits if used incorrectly.
656 if (!isdigit((unsigned char)*buf))
660 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
670 if (*buf != 0 && isspace((unsigned char)*buf))
671 while (*ptr != 0 && !isspace((unsigned char)*ptr))
678 for (i = 0; i < asizeof(Locale->month); i++) {
681 len = strlen(Locale->alt_month[i]);
683 Locale->alt_month[i],
689 len = strlen(Locale->month[i]);
695 len = strlen(Locale->mon[i]);
703 if (i == asizeof(Locale->month))
711 if (!isdigit((unsigned char)*buf))
715 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
725 if (*buf != 0 && isspace((unsigned char)*buf))
726 while (*ptr != 0 && !isspace((unsigned char)*ptr))
732 if (*buf == 0 || isspace((unsigned char)*buf))
735 if (!isdigit((unsigned char)*buf))
738 len = (c == 'Y') ? 4 : 2;
739 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
746 if (c == 'y' && i < 69)
753 if (*buf != 0 && isspace((unsigned char)*buf))
754 while (*ptr != 0 && !isspace((unsigned char)*ptr))
763 for (cp = buf; *cp && isupper((unsigned char)*cp); ++cp)
766 zonestr = (char *)alloca(cp - buf + 1);
767 strncpy(zonestr, buf, cp - buf);
768 zonestr[cp - buf] = '\0';
770 if (0 == strcmp(zonestr, "GMT")) {
786 strptime(const char *buf, const char *fmt, struct tm *tm)
791 pthread_mutex_lock(&gotgmt_mutex);
795 ret = _strptime(buf, fmt, tm);
798 pthread_mutex_unlock(&gotgmt_mutex);
804 #endif /* Mac OS X */
806 MODULE = Time::Piece PACKAGE = Time::Piece
811 _strftime(fmt, sec, min, hour, mday, mon, year, wday = -1, yday = -1, isdst = -1)
827 memset(&mytm, 0, sizeof(mytm));
828 my_init_tm(&mytm); /* XXX workaround - see my_init_tm() above */
837 mytm.tm_isdst = isdst;
838 my_mini_mktime(&mytm);
839 len = strftime(tmpbuf, sizeof tmpbuf, fmt, &mytm);
841 ** The following is needed to handle to the situation where
842 ** tmpbuf overflows. Basically we want to allocate a buffer
843 ** and try repeatedly. The reason why it is so complicated
844 ** is that getting a return value of 0 from strftime can indicate
845 ** one of the following:
846 ** 1. buffer overflowed,
847 ** 2. illegal conversion specifier, or
848 ** 3. the format string specifies nothing to be returned(not
849 ** an error). This could be because format is an empty string
850 ** or it specifies %p that yields an empty string in some locale.
851 ** If there is a better way to make it portable, go ahead by
854 if ((len > 0 && len < sizeof(tmpbuf)) || (len == 0 && *fmt == '\0'))
855 ST(0) = sv_2mortal(newSVpv(tmpbuf, len));
857 /* Possibly buf overflowed - try again with a bigger buf */
858 int fmtlen = strlen(fmt);
859 int bufsize = fmtlen + sizeof(tmpbuf);
863 New(0, buf, bufsize, char);
865 buflen = strftime(buf, bufsize, fmt, &mytm);
866 if (buflen > 0 && buflen < bufsize)
868 /* heuristic to prevent out-of-memory errors */
869 if (bufsize > 100*fmtlen) {
875 Renew(buf, bufsize, char);
878 ST(0) = sv_2mortal(newSVpv(buf, buflen));
882 ST(0) = sv_2mortal(newSVpv(tmpbuf, len));
893 _strptime ( string, format )
904 remainder = (char *)strptime(string, format, &mytm);
906 if (remainder == NULL) {
907 croak("Error parsing time");
910 if (*remainder != '\0') {
911 warn("garbage at end of string in strptime: %s", remainder);
914 my_mini_mktime(&mytm);
916 /* 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); */
919 PUSHs(sv_2mortal(newSViv(mytm.tm_sec)));
920 PUSHs(sv_2mortal(newSViv(mytm.tm_min)));
921 PUSHs(sv_2mortal(newSViv(mytm.tm_hour)));
922 PUSHs(sv_2mortal(newSViv(mytm.tm_mday)));
923 PUSHs(sv_2mortal(newSViv(mytm.tm_mon)));
924 PUSHs(sv_2mortal(newSViv(mytm.tm_year)));
925 PUSHs(sv_2mortal(newSViv(mytm.tm_wday)));
926 PUSHs(sv_2mortal(newSViv(mytm.tm_yday)));
928 PUSHs(sv_2mortal(newSViv(0)));
930 PUSHs(sv_2mortal(newSViv(0)));
932 PUSHs(sv_2mortal(newSViv(0)));