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 #if defined(__BORLANDC__)
192 void * __cdecl _EXPFUNC alloca(_SIZE_T __size);
194 #define alloca _alloca
198 /* strptime copied from freebsd with the following copyright: */
200 * Copyright (c) 1994 Powerdog Industries. All rights reserved.
202 * Redistribution and use in source and binary forms, with or without
203 * modification, are permitted provided that the following conditions
205 * 1. Redistributions of source code must retain the above copyright
206 * notice, this list of conditions and the following disclaimer.
207 * 2. Redistributions in binary form must reproduce the above copyright
208 * notice, this list of conditions and the following disclaimer
209 * in the documentation and/or other materials provided with the
211 * 3. All advertising materials mentioning features or use of this
212 * software must display the following acknowledgement:
213 * This product includes software developed by Powerdog Industries.
214 * 4. The name of Powerdog Industries may not be used to endorse or
215 * promote products derived from this software without specific prior
216 * written permission.
218 * THIS SOFTWARE IS PROVIDED BY POWERDOG INDUSTRIES ``AS IS'' AND ANY
219 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
220 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
221 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE POWERDOG INDUSTRIES BE
222 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
223 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
224 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
225 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
226 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
227 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
228 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
233 static char copyright[] =
234 "@(#) Copyright (c) 1994 Powerdog Industries. All rights reserved.";
235 static char sccsid[] = "@(#)strptime.c 0.1 (Powerdog) 94/03/27";
236 #endif /* !defined NOID */
237 #endif /* not lint */
244 #include "pthread_private.h"
245 #endif /* _THREAD_SAFE */
247 static char * _strptime(const char *, const char *, struct tm *);
250 static struct pthread_mutex _gotgmt_mutexd = PTHREAD_MUTEX_STATIC_INITIALIZER;
251 static pthread_mutex_t gotgmt_mutex = &_gotgmt_mutexd;
255 #define asizeof(a) (sizeof (a) / sizeof ((a)[0]))
258 const char * mon[12];
259 const char * month[12];
260 const char * wday[7];
261 const char * weekday[7];
267 const char * date_fmt;
268 const char * alt_month[12];
273 struct lc_time_T _time_localebuf;
274 int _time_using_locale;
276 const struct lc_time_T _C_time_locale = {
278 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
279 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
281 "January", "February", "March", "April", "May", "June",
282 "July", "August", "September", "October", "November", "December"
284 "Sun", "Mon", "Tue", "Wed",
287 "Sunday", "Monday", "Tuesday", "Wednesday",
288 "Thursday", "Friday", "Saturday"
296 ** Since the C language standard calls for
297 ** "date, using locale's date format," anything goes.
298 ** Using just numbers (as here) makes Quakers happier;
299 ** it's also compatible with SVR4.
304 ** c_fmt (ctime-compatible)
305 ** Not used, just compatibility placeholder.
319 "January", "February", "March", "April", "May", "June",
320 "July", "August", "September", "October", "November", "December"
324 ** To determine short months / day order
329 ** To determine long months / day order
334 #define Locale (&_C_time_locale)
337 _strptime(const char *buf, const char *fmt, struct tm *tm)
343 int Ealternative, Oalternative;
353 if (isspace((unsigned char)c))
354 while (*buf != 0 && isspace((unsigned char)*buf))
356 else if (c != *buf++)
373 buf = _strptime(buf, Locale->date_fmt, tm);
379 if (!isdigit((unsigned char)*buf))
382 /* XXX This will break for 3-digit centuries. */
384 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
392 tm->tm_year = i * 100 - 1900;
396 /* NOTE: c_fmt is intentionally ignored */
397 buf = _strptime(buf, "%a %Ef %T %Y", tm);
403 buf = _strptime(buf, "%m/%d/%y", tm);
409 if (Ealternative || Oalternative)
415 if (Ealternative || Oalternative)
424 buf = _strptime(buf, (c == 'f') ? Locale->Ef_fmt : Locale->EF_fmt, tm);
430 buf = _strptime(buf, "%H:%M", tm);
436 buf = _strptime(buf, "%I:%M:%S %p", tm);
442 buf = _strptime(buf, "%H:%M:%S", tm);
448 buf = _strptime(buf, Locale->X_fmt, tm);
454 buf = _strptime(buf, Locale->x_fmt, tm);
460 if (!isdigit((unsigned char)*buf))
464 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
469 if (i < 1 || i > 366)
477 if (*buf == 0 || isspace((unsigned char)*buf))
480 if (!isdigit((unsigned char)*buf))
484 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
500 if (*buf != 0 && isspace((unsigned char)*buf))
501 while (*ptr != 0 && !isspace((unsigned char)*ptr))
510 * Of these, %l is the only specifier explicitly
511 * documented as not being zero-padded. However,
512 * there is no harm in allowing zero-padding.
514 * XXX The %l specifier may gobble one too many
515 * digits if used incorrectly.
517 if (!isdigit((unsigned char)*buf))
521 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
526 if (c == 'H' || c == 'k') {
534 if (*buf != 0 && isspace((unsigned char)*buf))
535 while (*ptr != 0 && !isspace((unsigned char)*ptr))
541 * XXX This is bogus if parsed before hour-related
544 len = strlen(Locale->am);
545 if (strncasecmp(buf, Locale->am, len) == 0) {
546 if (tm->tm_hour > 12)
548 if (tm->tm_hour == 12)
554 len = strlen(Locale->pm);
555 if (strncasecmp(buf, Locale->pm, len) == 0) {
556 if (tm->tm_hour > 12)
558 if (tm->tm_hour != 12)
568 for (i = 0; i < asizeof(Locale->weekday); i++) {
570 len = strlen(Locale->weekday[i]);
576 len = strlen(Locale->wday[i]);
583 if (i == asizeof(Locale->weekday))
593 * XXX This is bogus, as we can not assume any valid
594 * information present in the tm structure at this
595 * point to calculate a real value, so just check the
598 if (!isdigit((unsigned char)*buf))
602 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
610 if (*buf != 0 && isspace((unsigned char)*buf))
611 while (*ptr != 0 && !isspace((unsigned char)*ptr))
616 if (!isdigit((unsigned char)*buf))
625 if (*buf != 0 && isspace((unsigned char)*buf))
626 while (*ptr != 0 && !isspace((unsigned char)*ptr))
633 * The %e specifier is explicitly documented as not
634 * being zero-padded but there is no harm in allowing
637 * XXX The %e specifier may gobble one too many
638 * digits if used incorrectly.
640 if (!isdigit((unsigned char)*buf))
644 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
654 if (*buf != 0 && isspace((unsigned char)*buf))
655 while (*ptr != 0 && !isspace((unsigned char)*ptr))
662 for (i = 0; i < asizeof(Locale->month); i++) {
665 len = strlen(Locale->alt_month[i]);
667 Locale->alt_month[i],
673 len = strlen(Locale->month[i]);
679 len = strlen(Locale->mon[i]);
687 if (i == asizeof(Locale->month))
695 if (!isdigit((unsigned char)*buf))
699 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
709 if (*buf != 0 && isspace((unsigned char)*buf))
710 while (*ptr != 0 && !isspace((unsigned char)*ptr))
716 if (*buf == 0 || isspace((unsigned char)*buf))
719 if (!isdigit((unsigned char)*buf))
722 len = (c == 'Y') ? 4 : 2;
723 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
730 if (c == 'y' && i < 69)
737 if (*buf != 0 && isspace((unsigned char)*buf))
738 while (*ptr != 0 && !isspace((unsigned char)*ptr))
747 for (cp = buf; *cp && isupper((unsigned char)*cp); ++cp)
750 zonestr = (char *)alloca(cp - buf + 1);
751 strncpy(zonestr, buf, cp - buf);
752 zonestr[cp - buf] = '\0';
754 if (0 == strcmp(zonestr, "GMT")) {
770 strptime(const char *buf, const char *fmt, struct tm *tm)
775 pthread_mutex_lock(&gotgmt_mutex);
779 ret = _strptime(buf, fmt, tm);
782 pthread_mutex_unlock(&gotgmt_mutex);
788 #endif /* Mac OS X */
790 MODULE = Time::Piece PACKAGE = Time::Piece
795 _strftime(fmt, sec, min, hour, mday, mon, year, wday = -1, yday = -1, isdst = -1)
811 memset(&mytm, 0, sizeof(mytm));
812 my_init_tm(&mytm); /* XXX workaround - see my_init_tm() above */
821 mytm.tm_isdst = isdst;
822 my_mini_mktime(&mytm);
823 len = strftime(tmpbuf, sizeof tmpbuf, fmt, &mytm);
825 ** The following is needed to handle to the situation where
826 ** tmpbuf overflows. Basically we want to allocate a buffer
827 ** and try repeatedly. The reason why it is so complicated
828 ** is that getting a return value of 0 from strftime can indicate
829 ** one of the following:
830 ** 1. buffer overflowed,
831 ** 2. illegal conversion specifier, or
832 ** 3. the format string specifies nothing to be returned(not
833 ** an error). This could be because format is an empty string
834 ** or it specifies %p that yields an empty string in some locale.
835 ** If there is a better way to make it portable, go ahead by
838 if ((len > 0 && len < sizeof(tmpbuf)) || (len == 0 && *fmt == '\0'))
839 ST(0) = sv_2mortal(newSVpv(tmpbuf, len));
841 /* Possibly buf overflowed - try again with a bigger buf */
842 int fmtlen = strlen(fmt);
843 int bufsize = fmtlen + sizeof(tmpbuf);
847 New(0, buf, bufsize, char);
849 buflen = strftime(buf, bufsize, fmt, &mytm);
850 if (buflen > 0 && buflen < bufsize)
852 /* heuristic to prevent out-of-memory errors */
853 if (bufsize > 100*fmtlen) {
859 Renew(buf, bufsize, char);
862 ST(0) = sv_2mortal(newSVpv(buf, buflen));
866 ST(0) = sv_2mortal(newSVpv(tmpbuf, len));
877 _strptime ( string, format )
888 remainder = (char *)strptime(string, format, &mytm);
890 if (remainder == NULL) {
891 croak("Error parsing time");
894 if (*remainder != '\0') {
895 warn("garbage at end of string in strptime: %s", remainder);
898 my_mini_mktime(&mytm);
900 /* 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); */
903 PUSHs(sv_2mortal(newSViv(mytm.tm_sec)));
904 PUSHs(sv_2mortal(newSViv(mytm.tm_min)));
905 PUSHs(sv_2mortal(newSViv(mytm.tm_hour)));
906 PUSHs(sv_2mortal(newSViv(mytm.tm_mday)));
907 PUSHs(sv_2mortal(newSViv(mytm.tm_mon)));
908 PUSHs(sv_2mortal(newSViv(mytm.tm_year)));
909 PUSHs(sv_2mortal(newSViv(mytm.tm_wday)));
910 PUSHs(sv_2mortal(newSViv(mytm.tm_yday)));
912 PUSHs(sv_2mortal(newSViv(0)));
914 PUSHs(sv_2mortal(newSViv(0)));
916 PUSHs(sv_2mortal(newSViv(0)));
919 _mini_mktime(int sec, int min, int hour, int mday, int mon, int year)
934 my_mini_mktime(&mytm);
937 PUSHs(sv_2mortal(newSViv(mytm.tm_sec)));
938 PUSHs(sv_2mortal(newSViv(mytm.tm_min)));
939 PUSHs(sv_2mortal(newSViv(mytm.tm_hour)));
940 PUSHs(sv_2mortal(newSViv(mytm.tm_mday)));
941 PUSHs(sv_2mortal(newSViv(mytm.tm_mon)));
942 PUSHs(sv_2mortal(newSViv(mytm.tm_year)));
943 PUSHs(sv_2mortal(newSViv(mytm.tm_wday)));
944 PUSHs(sv_2mortal(newSViv(mytm.tm_yday)));
946 PUSHs(sv_2mortal(newSViv(0)));
948 PUSHs(sv_2mortal(newSViv(0)));
950 PUSHs(sv_2mortal(newSViv(0)));