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(). Stolen shamelessly from Perl's
64 * Perl_mini_mktime() in util.c - for details on the algorithm, see that
68 my_mini_mktime(struct tm *ptm)
72 int month, mday, year, jday;
73 int odd_cent, odd_year;
75 year = 1900 + ptm->tm_year;
78 /* allow given yday with no month & mday to dominate the result */
79 if (ptm->tm_yday >= 0 && mday <= 0 && month <= 0) {
82 jday = 1 + ptm->tm_yday;
92 yearday = DAYS_PER_YEAR * year + year/4 - year/100 + year/400;
93 yearday += month*MONTH_TO_DAYS + mday + jday;
95 * Note that we don't know when leap-seconds were or will be,
96 * so we have to trust the user if we get something which looks
97 * like a sensible leap-second. Wild values for seconds will
98 * be rationalised, however.
100 if ((unsigned) ptm->tm_sec <= 60) {
107 secs += 60 * ptm->tm_min;
108 secs += SECS_PER_HOUR * ptm->tm_hour;
110 if (secs-(secs/SECS_PER_DAY*SECS_PER_DAY) < 0) {
111 /* got negative remainder, but need positive time */
112 /* back off an extra day to compensate */
113 yearday += (secs/SECS_PER_DAY)-1;
114 secs -= SECS_PER_DAY * (secs/SECS_PER_DAY - 1);
117 yearday += (secs/SECS_PER_DAY);
118 secs -= SECS_PER_DAY * (secs/SECS_PER_DAY);
121 else if (secs >= SECS_PER_DAY) {
122 yearday += (secs/SECS_PER_DAY);
123 secs %= SECS_PER_DAY;
125 ptm->tm_hour = secs/SECS_PER_HOUR;
126 secs %= SECS_PER_HOUR;
127 ptm->tm_min = secs/60;
130 /* done with time of day effects */
132 * The algorithm for yearday has (so far) left it high by 428.
133 * To avoid mistaking a legitimate Feb 29 as Mar 1, we need to
134 * bias it by 123 while trying to figure out what year it
135 * really represents. Even with this tweak, the reverse
136 * translation fails for years before A.D. 0001.
137 * It would still fail for Feb 29, but we catch that one below.
139 jday = yearday; /* save for later fixup vis-a-vis Jan 1 */
140 yearday -= YEAR_ADJUST;
141 year = (yearday / DAYS_PER_QCENT) * 400;
142 yearday %= DAYS_PER_QCENT;
143 odd_cent = yearday / DAYS_PER_CENT;
144 year += odd_cent * 100;
145 yearday %= DAYS_PER_CENT;
146 year += (yearday / DAYS_PER_QYEAR) * 4;
147 yearday %= DAYS_PER_QYEAR;
148 odd_year = yearday / DAYS_PER_YEAR;
150 yearday %= DAYS_PER_YEAR;
151 if (!yearday && (odd_cent==4 || odd_year==4)) { /* catch Feb 29 */
156 yearday += YEAR_ADJUST; /* recover March 1st crock */
157 month = yearday*DAYS_TO_MONTH;
158 yearday -= month*MONTH_TO_DAYS;
159 /* recover other leap-year adjustment */
168 ptm->tm_year = year - 1900;
170 ptm->tm_mday = yearday;
175 ptm->tm_mon = month - 1;
177 /* re-build yearday based on Jan 1 to get tm_yday */
179 yearday = year*DAYS_PER_YEAR + year/4 - year/100 + year/400;
180 yearday += 14*MONTH_TO_DAYS + 1;
181 ptm->tm_yday = jday - yearday;
182 /* fix tm_wday if not overridden by caller */
183 ptm->tm_wday = (jday + WEEKDAY_BIAS) % 7;
186 /* No strptime on Win32 or QNX4 */
187 #if defined(WIN32) || (defined(__QNX__) && defined(__WATCOMC__))
188 #define strncasecmp(x,y,n) strnicmp(x,y,n)
191 #define alloca _alloca
194 /* strptime copied from freebsd with the following copyright: */
196 * Copyright (c) 1994 Powerdog Industries. All rights reserved.
198 * Redistribution and use in source and binary forms, with or without
199 * modification, are permitted provided that the following conditions
201 * 1. Redistributions of source code must retain the above copyright
202 * notice, this list of conditions and the following disclaimer.
203 * 2. Redistributions in binary form must reproduce the above copyright
204 * notice, this list of conditions and the following disclaimer
205 * in the documentation and/or other materials provided with the
207 * 3. All advertising materials mentioning features or use of this
208 * software must display the following acknowledgement:
209 * This product includes software developed by Powerdog Industries.
210 * 4. The name of Powerdog Industries may not be used to endorse or
211 * promote products derived from this software without specific prior
212 * written permission.
214 * THIS SOFTWARE IS PROVIDED BY POWERDOG INDUSTRIES ``AS IS'' AND ANY
215 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
216 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
217 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE POWERDOG INDUSTRIES BE
218 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
219 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
220 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
221 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
222 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
223 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
224 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
229 static char copyright[] =
230 "@(#) Copyright (c) 1994 Powerdog Industries. All rights reserved.";
231 static char sccsid[] = "@(#)strptime.c 0.1 (Powerdog) 94/03/27";
232 #endif /* !defined NOID */
233 #endif /* not lint */
240 #include "pthread_private.h"
241 #endif /* _THREAD_SAFE */
243 static char * _strptime(const char *, const char *, struct tm *);
246 static struct pthread_mutex _gotgmt_mutexd = PTHREAD_MUTEX_STATIC_INITIALIZER;
247 static pthread_mutex_t gotgmt_mutex = &_gotgmt_mutexd;
251 #define asizeof(a) (sizeof (a) / sizeof ((a)[0]))
254 const char * mon[12];
255 const char * month[12];
256 const char * wday[7];
257 const char * weekday[7];
263 const char * date_fmt;
264 const char * alt_month[12];
269 struct lc_time_T _time_localebuf;
270 int _time_using_locale;
272 const struct lc_time_T _C_time_locale = {
274 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
275 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
277 "January", "February", "March", "April", "May", "June",
278 "July", "August", "September", "October", "November", "December"
280 "Sun", "Mon", "Tue", "Wed",
283 "Sunday", "Monday", "Tuesday", "Wednesday",
284 "Thursday", "Friday", "Saturday"
292 ** Since the C language standard calls for
293 ** "date, using locale's date format," anything goes.
294 ** Using just numbers (as here) makes Quakers happier;
295 ** it's also compatible with SVR4.
300 ** c_fmt (ctime-compatible)
301 ** Not used, just compatibility placeholder.
315 "January", "February", "March", "April", "May", "June",
316 "July", "August", "September", "October", "November", "December"
320 ** To determine short months / day order
325 ** To determine long months / day order
330 #define Locale (&_C_time_locale)
333 _strptime(const char *buf, const char *fmt, struct tm *tm)
339 int Ealternative, Oalternative;
349 if (isspace((unsigned char)c))
350 while (*buf != 0 && isspace((unsigned char)*buf))
352 else if (c != *buf++)
369 buf = _strptime(buf, Locale->date_fmt, tm);
375 if (!isdigit((unsigned char)*buf))
378 /* XXX This will break for 3-digit centuries. */
380 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
388 tm->tm_year = i * 100 - 1900;
392 /* NOTE: c_fmt is intentionally ignored */
393 buf = _strptime(buf, "%a %Ef %T %Y", tm);
399 buf = _strptime(buf, "%m/%d/%y", tm);
405 if (Ealternative || Oalternative)
411 if (Ealternative || Oalternative)
420 buf = _strptime(buf, (c == 'f') ? Locale->Ef_fmt : Locale->EF_fmt, tm);
426 buf = _strptime(buf, "%H:%M", tm);
432 buf = _strptime(buf, "%I:%M:%S %p", tm);
438 buf = _strptime(buf, "%H:%M:%S", tm);
444 buf = _strptime(buf, Locale->X_fmt, tm);
450 buf = _strptime(buf, Locale->x_fmt, tm);
456 if (!isdigit((unsigned char)*buf))
460 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
465 if (i < 1 || i > 366)
473 if (*buf == 0 || isspace((unsigned char)*buf))
476 if (!isdigit((unsigned char)*buf))
480 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
496 if (*buf != 0 && isspace((unsigned char)*buf))
497 while (*ptr != 0 && !isspace((unsigned char)*ptr))
506 * Of these, %l is the only specifier explicitly
507 * documented as not being zero-padded. However,
508 * there is no harm in allowing zero-padding.
510 * XXX The %l specifier may gobble one too many
511 * digits if used incorrectly.
513 if (!isdigit((unsigned char)*buf))
517 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
522 if (c == 'H' || c == 'k') {
530 if (*buf != 0 && isspace((unsigned char)*buf))
531 while (*ptr != 0 && !isspace((unsigned char)*ptr))
537 * XXX This is bogus if parsed before hour-related
540 len = strlen(Locale->am);
541 if (strncasecmp(buf, Locale->am, len) == 0) {
542 if (tm->tm_hour > 12)
544 if (tm->tm_hour == 12)
550 len = strlen(Locale->pm);
551 if (strncasecmp(buf, Locale->pm, len) == 0) {
552 if (tm->tm_hour > 12)
554 if (tm->tm_hour != 12)
564 for (i = 0; i < asizeof(Locale->weekday); i++) {
566 len = strlen(Locale->weekday[i]);
572 len = strlen(Locale->wday[i]);
579 if (i == asizeof(Locale->weekday))
589 * XXX This is bogus, as we can not assume any valid
590 * information present in the tm structure at this
591 * point to calculate a real value, so just check the
594 if (!isdigit((unsigned char)*buf))
598 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
606 if (*buf != 0 && isspace((unsigned char)*buf))
607 while (*ptr != 0 && !isspace((unsigned char)*ptr))
612 if (!isdigit((unsigned char)*buf))
621 if (*buf != 0 && isspace((unsigned char)*buf))
622 while (*ptr != 0 && !isspace((unsigned char)*ptr))
629 * The %e specifier is explicitly documented as not
630 * being zero-padded but there is no harm in allowing
633 * XXX The %e specifier may gobble one too many
634 * digits if used incorrectly.
636 if (!isdigit((unsigned char)*buf))
640 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
650 if (*buf != 0 && isspace((unsigned char)*buf))
651 while (*ptr != 0 && !isspace((unsigned char)*ptr))
658 for (i = 0; i < asizeof(Locale->month); i++) {
661 len = strlen(Locale->alt_month[i]);
663 Locale->alt_month[i],
669 len = strlen(Locale->month[i]);
675 len = strlen(Locale->mon[i]);
683 if (i == asizeof(Locale->month))
691 if (!isdigit((unsigned char)*buf))
695 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
705 if (*buf != 0 && isspace((unsigned char)*buf))
706 while (*ptr != 0 && !isspace((unsigned char)*ptr))
712 if (*buf == 0 || isspace((unsigned char)*buf))
715 if (!isdigit((unsigned char)*buf))
718 len = (c == 'Y') ? 4 : 2;
719 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
726 if (c == 'y' && i < 69)
733 if (*buf != 0 && isspace((unsigned char)*buf))
734 while (*ptr != 0 && !isspace((unsigned char)*ptr))
743 for (cp = buf; *cp && isupper((unsigned char)*cp); ++cp)
746 zonestr = (char *)alloca(cp - buf + 1);
747 strncpy(zonestr, buf, cp - buf);
748 zonestr[cp - buf] = '\0';
750 if (0 == strcmp(zonestr, "GMT")) {
766 strptime(const char *buf, const char *fmt, struct tm *tm)
771 pthread_mutex_lock(&gotgmt_mutex);
775 ret = _strptime(buf, fmt, tm);
778 pthread_mutex_unlock(&gotgmt_mutex);
784 #endif /* Mac OS X */
786 MODULE = Time::Piece PACKAGE = Time::Piece
791 _strftime(fmt, sec, min, hour, mday, mon, year, wday = -1, yday = -1, isdst = -1)
807 memset(&mytm, 0, sizeof(mytm));
808 my_init_tm(&mytm); /* XXX workaround - see my_init_tm() above */
817 mytm.tm_isdst = isdst;
818 my_mini_mktime(&mytm);
819 len = strftime(tmpbuf, sizeof tmpbuf, fmt, &mytm);
821 ** The following is needed to handle to the situation where
822 ** tmpbuf overflows. Basically we want to allocate a buffer
823 ** and try repeatedly. The reason why it is so complicated
824 ** is that getting a return value of 0 from strftime can indicate
825 ** one of the following:
826 ** 1. buffer overflowed,
827 ** 2. illegal conversion specifier, or
828 ** 3. the format string specifies nothing to be returned(not
829 ** an error). This could be because format is an empty string
830 ** or it specifies %p that yields an empty string in some locale.
831 ** If there is a better way to make it portable, go ahead by
834 if ((len > 0 && len < sizeof(tmpbuf)) || (len == 0 && *fmt == '\0'))
835 ST(0) = sv_2mortal(newSVpv(tmpbuf, len));
837 /* Possibly buf overflowed - try again with a bigger buf */
838 int fmtlen = strlen(fmt);
839 int bufsize = fmtlen + sizeof(tmpbuf);
843 New(0, buf, bufsize, char);
845 buflen = strftime(buf, bufsize, fmt, &mytm);
846 if (buflen > 0 && buflen < bufsize)
848 /* heuristic to prevent out-of-memory errors */
849 if (bufsize > 100*fmtlen) {
855 Renew(buf, bufsize, char);
858 ST(0) = sv_2mortal(newSVpv(buf, buflen));
862 ST(0) = sv_2mortal(newSVpv(tmpbuf, len));
873 _strptime ( string, format )
884 remainder = (char *)strptime(string, format, &mytm);
886 if (remainder == NULL) {
887 croak("Error parsing time");
890 if (*remainder != '\0') {
891 warn("garbage at end of string in strptime: %s", remainder);
894 my_mini_mktime(&mytm);
896 /* 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); */
899 PUSHs(sv_2mortal(newSViv(mytm.tm_sec)));
900 PUSHs(sv_2mortal(newSViv(mytm.tm_min)));
901 PUSHs(sv_2mortal(newSViv(mytm.tm_hour)));
902 PUSHs(sv_2mortal(newSViv(mytm.tm_mday)));
903 PUSHs(sv_2mortal(newSViv(mytm.tm_mon)));
904 PUSHs(sv_2mortal(newSViv(mytm.tm_year)));
905 PUSHs(sv_2mortal(newSViv(mytm.tm_wday)));
906 PUSHs(sv_2mortal(newSViv(mytm.tm_yday)));
908 PUSHs(sv_2mortal(newSViv(0)));
910 PUSHs(sv_2mortal(newSViv(0)));
912 PUSHs(sv_2mortal(newSViv(0)));