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) /* No strptime on Win32 */
245 #define strncasecmp(x,y,n) strnicmp(x,y,n)
246 #define alloca _alloca
252 #include "pthread_private.h"
253 #endif /* _THREAD_SAFE */
255 static char * _strptime(const char *, const char *, struct tm *);
258 static struct pthread_mutex _gotgmt_mutexd = PTHREAD_MUTEX_STATIC_INITIALIZER;
259 static pthread_mutex_t gotgmt_mutex = &_gotgmt_mutexd;
263 #define asizeof(a) (sizeof (a) / sizeof ((a)[0]))
266 const char * mon[12];
267 const char * month[12];
268 const char * wday[7];
269 const char * weekday[7];
275 const char * date_fmt;
276 const char * alt_month[12];
281 struct lc_time_T _time_localebuf;
282 int _time_using_locale;
284 const struct lc_time_T _C_time_locale = {
286 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
287 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
289 "January", "February", "March", "April", "May", "June",
290 "July", "August", "September", "October", "November", "December"
292 "Sun", "Mon", "Tue", "Wed",
295 "Sunday", "Monday", "Tuesday", "Wednesday",
296 "Thursday", "Friday", "Saturday"
304 ** Since the C language standard calls for
305 ** "date, using locale's date format," anything goes.
306 ** Using just numbers (as here) makes Quakers happier;
307 ** it's also compatible with SVR4.
312 ** c_fmt (ctime-compatible)
313 ** Not used, just compatibility placeholder.
327 "January", "February", "March", "April", "May", "June",
328 "July", "August", "September", "October", "November", "December"
332 ** To determine short months / day order
337 ** To determine long months / day order
342 #define Locale (&_C_time_locale)
345 _strptime(const char *buf, const char *fmt, struct tm *tm)
351 int Ealternative, Oalternative;
361 if (isspace((unsigned char)c))
362 while (*buf != 0 && isspace((unsigned char)*buf))
364 else if (c != *buf++)
381 buf = _strptime(buf, Locale->date_fmt, tm);
387 if (!isdigit((unsigned char)*buf))
390 /* XXX This will break for 3-digit centuries. */
392 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
400 tm->tm_year = i * 100 - 1900;
404 /* NOTE: c_fmt is intentionally ignored */
405 buf = _strptime(buf, "%a %Ef %T %Y", tm);
411 buf = _strptime(buf, "%m/%d/%y", tm);
417 if (Ealternative || Oalternative)
423 if (Ealternative || Oalternative)
432 buf = _strptime(buf, (c == 'f') ? Locale->Ef_fmt : Locale->EF_fmt, tm);
438 buf = _strptime(buf, "%H:%M", tm);
444 buf = _strptime(buf, "%I:%M:%S %p", tm);
450 buf = _strptime(buf, "%H:%M:%S", tm);
456 buf = _strptime(buf, Locale->X_fmt, tm);
462 buf = _strptime(buf, Locale->x_fmt, tm);
468 if (!isdigit((unsigned char)*buf))
472 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
477 if (i < 1 || i > 366)
485 if (*buf == 0 || isspace((unsigned char)*buf))
488 if (!isdigit((unsigned char)*buf))
492 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
508 if (*buf != 0 && isspace((unsigned char)*buf))
509 while (*ptr != 0 && !isspace((unsigned char)*ptr))
518 * Of these, %l is the only specifier explicitly
519 * documented as not being zero-padded. However,
520 * there is no harm in allowing zero-padding.
522 * XXX The %l specifier may gobble one too many
523 * digits if used incorrectly.
525 if (!isdigit((unsigned char)*buf))
529 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
534 if (c == 'H' || c == 'k') {
542 if (*buf != 0 && isspace((unsigned char)*buf))
543 while (*ptr != 0 && !isspace((unsigned char)*ptr))
549 * XXX This is bogus if parsed before hour-related
552 len = strlen(Locale->am);
553 if (strncasecmp(buf, Locale->am, len) == 0) {
554 if (tm->tm_hour > 12)
556 if (tm->tm_hour == 12)
562 len = strlen(Locale->pm);
563 if (strncasecmp(buf, Locale->pm, len) == 0) {
564 if (tm->tm_hour > 12)
566 if (tm->tm_hour != 12)
576 for (i = 0; i < asizeof(Locale->weekday); i++) {
578 len = strlen(Locale->weekday[i]);
584 len = strlen(Locale->wday[i]);
591 if (i == asizeof(Locale->weekday))
601 * XXX This is bogus, as we can not assume any valid
602 * information present in the tm structure at this
603 * point to calculate a real value, so just check the
606 if (!isdigit((unsigned char)*buf))
610 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
618 if (*buf != 0 && isspace((unsigned char)*buf))
619 while (*ptr != 0 && !isspace((unsigned char)*ptr))
624 if (!isdigit((unsigned char)*buf))
633 if (*buf != 0 && isspace((unsigned char)*buf))
634 while (*ptr != 0 && !isspace((unsigned char)*ptr))
641 * The %e specifier is explicitly documented as not
642 * being zero-padded but there is no harm in allowing
645 * XXX The %e specifier may gobble one too many
646 * digits if used incorrectly.
648 if (!isdigit((unsigned char)*buf))
652 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
662 if (*buf != 0 && isspace((unsigned char)*buf))
663 while (*ptr != 0 && !isspace((unsigned char)*ptr))
670 for (i = 0; i < asizeof(Locale->month); i++) {
673 len = strlen(Locale->alt_month[i]);
675 Locale->alt_month[i],
681 len = strlen(Locale->month[i]);
687 len = strlen(Locale->mon[i]);
695 if (i == asizeof(Locale->month))
703 if (!isdigit((unsigned char)*buf))
707 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
717 if (*buf != 0 && isspace((unsigned char)*buf))
718 while (*ptr != 0 && !isspace((unsigned char)*ptr))
724 if (*buf == 0 || isspace((unsigned char)*buf))
727 if (!isdigit((unsigned char)*buf))
730 len = (c == 'Y') ? 4 : 2;
731 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
738 if (c == 'y' && i < 69)
745 if (*buf != 0 && isspace((unsigned char)*buf))
746 while (*ptr != 0 && !isspace((unsigned char)*ptr))
755 for (cp = buf; *cp && isupper((unsigned char)*cp); ++cp)
758 zonestr = alloca(cp - buf + 1);
759 strncpy(zonestr, buf, cp - buf);
760 zonestr[cp - buf] = '\0';
762 if (0 == strcmp(zonestr, "GMT")) {
778 strptime(const char *buf, const char *fmt, struct tm *tm)
783 pthread_mutex_lock(&gotgmt_mutex);
787 ret = _strptime(buf, fmt, tm);
790 pthread_mutex_unlock(&gotgmt_mutex);
796 #endif /* Mac OS X */
798 MODULE = Time::Piece PACKAGE = Time::Piece
803 _strftime(fmt, sec, min, hour, mday, mon, year, wday = -1, yday = -1, isdst = -1)
819 memset(&mytm, 0, sizeof(mytm));
820 my_init_tm(&mytm); /* XXX workaround - see my_init_tm() above */
829 mytm.tm_isdst = isdst;
830 my_mini_mktime(&mytm);
831 len = strftime(tmpbuf, sizeof tmpbuf, fmt, &mytm);
833 ** The following is needed to handle to the situation where
834 ** tmpbuf overflows. Basically we want to allocate a buffer
835 ** and try repeatedly. The reason why it is so complicated
836 ** is that getting a return value of 0 from strftime can indicate
837 ** one of the following:
838 ** 1. buffer overflowed,
839 ** 2. illegal conversion specifier, or
840 ** 3. the format string specifies nothing to be returned(not
841 ** an error). This could be because format is an empty string
842 ** or it specifies %p that yields an empty string in some locale.
843 ** If there is a better way to make it portable, go ahead by
846 if ((len > 0 && len < sizeof(tmpbuf)) || (len == 0 && *fmt == '\0'))
847 ST(0) = sv_2mortal(newSVpv(tmpbuf, len));
849 /* Possibly buf overflowed - try again with a bigger buf */
850 int fmtlen = strlen(fmt);
851 int bufsize = fmtlen + sizeof(tmpbuf);
855 New(0, buf, bufsize, char);
857 buflen = strftime(buf, bufsize, fmt, &mytm);
858 if (buflen > 0 && buflen < bufsize)
860 /* heuristic to prevent out-of-memory errors */
861 if (bufsize > 100*fmtlen) {
867 Renew(buf, bufsize, char);
870 ST(0) = sv_2mortal(newSVpv(buf, buflen));
874 ST(0) = sv_2mortal(newSVpv(tmpbuf, len));
885 _strptime ( string, format )
899 remainder = (char *)strptime(string, format, &mytm);
901 if (remainder == NULL) {
902 croak("Error parsing time");
905 if (*remainder != '\0') {
906 warn("garbage at end of string in strptime: %s", remainder);
909 my_mini_mktime(&mytm);
911 /* 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); */
914 PUSHs(sv_2mortal(newSViv(mytm.tm_sec)));
915 PUSHs(sv_2mortal(newSViv(mytm.tm_min)));
916 PUSHs(sv_2mortal(newSViv(mytm.tm_hour)));
917 PUSHs(sv_2mortal(newSViv(mytm.tm_mday)));
918 PUSHs(sv_2mortal(newSViv(mytm.tm_mon)));
919 PUSHs(sv_2mortal(newSViv(mytm.tm_year)));
920 PUSHs(sv_2mortal(newSViv(mytm.tm_wday)));
921 PUSHs(sv_2mortal(newSViv(mytm.tm_yday)));
923 PUSHs(sv_2mortal(newSViv(0)));
925 PUSHs(sv_2mortal(newSViv(0)));
927 PUSHs(sv_2mortal(newSViv(0)));