Time-Piece-1.13 test failures on HP-UX
[p5sagit/p5-mst-13.2.git] / ext / Time / Piece / Piece.xs
1 #ifdef __cplusplus
2 extern "C" {
3 #endif
4 #include "EXTERN.h"
5 #include "perl.h"
6 #include "XSUB.h"
7 #include <time.h>
8 #ifdef __cplusplus
9 }
10 #endif
11
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!
21  *
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.
25  */
26 #if !defined(HAS_GNULIBC)
27 # ifndef STRUCT_TM_HASZONE
28 #    define STRUCT_TM_HASZONE
29 # else
30 #    define USE_TM_GMTOFF
31 # endif
32 #endif
33
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 */
47
48 #ifdef STRUCT_TM_HASZONE
49 static void
50 my_init_tm(struct tm *ptm)        /* see mktime, strftime and asctime    */
51 {
52     Time_t now;
53     (void)time(&now);
54     Copy(localtime(&now), ptm, 1, struct tm);
55 }
56
57 #else
58 # define my_init_tm(ptm)
59 #endif
60
61 /*
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
65  * file.
66  */
67 static void
68 my_mini_mktime(struct tm *ptm)
69 {
70     int yearday;
71     int secs;
72     int month, mday, year, jday;
73     int odd_cent, odd_year;
74
75     year = 1900 + ptm->tm_year;
76     month = ptm->tm_mon;
77     mday = ptm->tm_mday;
78     /* allow given yday with no month & mday to dominate the result */
79     if (ptm->tm_yday >= 0 && mday <= 0 && month <= 0) {
80         month = 0;
81         mday = 0;
82         jday = 1 + ptm->tm_yday;
83     }
84     else {
85         jday = 0;
86     }
87     if (month >= 2)
88         month+=2;
89     else
90         month+=14, year--;
91
92     yearday = DAYS_PER_YEAR * year + year/4 - year/100 + year/400;
93     yearday += month*MONTH_TO_DAYS + mday + jday;
94     /*
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.
99      */
100     if ((unsigned) ptm->tm_sec <= 60) {
101         secs = 0;
102     }
103     else {
104         secs = ptm->tm_sec;
105         ptm->tm_sec = 0;
106     }
107     secs += 60 * ptm->tm_min;
108     secs += SECS_PER_HOUR * ptm->tm_hour;
109     if (secs < 0) {
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);
115         }
116         else {
117             yearday += (secs/SECS_PER_DAY);
118             secs -= SECS_PER_DAY * (secs/SECS_PER_DAY);
119         }
120     }
121     else if (secs >= SECS_PER_DAY) {
122         yearday += (secs/SECS_PER_DAY);
123         secs %= SECS_PER_DAY;
124     }
125     ptm->tm_hour = secs/SECS_PER_HOUR;
126     secs %= SECS_PER_HOUR;
127     ptm->tm_min = secs/60;
128     secs %= 60;
129     ptm->tm_sec += secs;
130     /* done with time of day effects */
131     /*
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.
138      */
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;
149     year += odd_year;
150     yearday %= DAYS_PER_YEAR;
151     if (!yearday && (odd_cent==4 || odd_year==4)) { /* catch Feb 29 */
152         month = 1;
153         yearday = 29;
154     }
155     else {
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 */
160         if (month > 13) {
161             month-=14;
162             year++;
163         }
164         else {
165             month-=2;
166         }
167     }
168     ptm->tm_year = year - 1900;
169     if (yearday) {
170       ptm->tm_mday = yearday;
171       ptm->tm_mon = month;
172     }
173     else {
174       ptm->tm_mday = 31;
175       ptm->tm_mon = month - 1;
176     }
177     /* re-build yearday based on Jan 1 to get tm_yday */
178     year--;
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;
184 }
185
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)
189
190 #if defined(WIN32)
191 #if defined(__BORLANDC__)
192 void * __cdecl _EXPFUNC alloca(_SIZE_T __size);
193 #else
194 #define alloca _alloca
195 #endif
196 #endif
197
198 /* strptime copied from freebsd with the following copyright: */
199 /*
200  * Copyright (c) 1994 Powerdog Industries.  All rights reserved.
201  *
202  * Redistribution and use in source and binary forms, with or without
203  * modification, are permitted provided that the following conditions
204  * are met:
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
210  *    distribution.
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.
217  *
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.
229  */
230  
231 #ifndef lint
232 #ifndef NOID
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 */
238
239 #include <time.h>
240 #include <ctype.h>
241 #include <string.h>
242 #ifdef _THREAD_SAFE
243 #include <pthread.h>
244 #include "pthread_private.h"
245 #endif /* _THREAD_SAFE */
246
247 static char * _strptime(const char *, const char *, struct tm *);
248
249 #ifdef _THREAD_SAFE
250 static struct pthread_mutex     _gotgmt_mutexd = PTHREAD_MUTEX_STATIC_INITIALIZER;
251 static pthread_mutex_t          gotgmt_mutex   = &_gotgmt_mutexd;
252 #endif
253 static int got_GMT;
254
255 #define asizeof(a)      (sizeof (a) / sizeof ((a)[0]))
256
257 struct lc_time_T {
258     const char *    mon[12];
259     const char *    month[12];
260     const char *    wday[7];
261     const char *    weekday[7];
262     const char *    X_fmt;     
263     const char *    x_fmt;
264     const char *    c_fmt;
265     const char *    am;
266     const char *    pm;
267     const char *    date_fmt;
268     const char *    alt_month[12];
269     const char *    Ef_fmt;
270     const char *    EF_fmt;
271 };
272
273 struct lc_time_T _time_localebuf;
274 int _time_using_locale;
275
276 const struct lc_time_T  _C_time_locale = {
277         {
278                 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
279                 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
280         }, {
281                 "January", "February", "March", "April", "May", "June",
282                 "July", "August", "September", "October", "November", "December"
283         }, {
284                 "Sun", "Mon", "Tue", "Wed",
285                 "Thu", "Fri", "Sat"
286         }, {
287                 "Sunday", "Monday", "Tuesday", "Wednesday",
288                 "Thursday", "Friday", "Saturday"
289         },
290
291         /* X_fmt */
292         "%H:%M:%S",
293
294         /*
295         ** x_fmt
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.
300         */
301         "%m/%d/%y",
302
303         /*
304         ** c_fmt (ctime-compatible)
305         ** Not used, just compatibility placeholder.
306         */
307         NULL,
308
309         /* am */
310         "AM",
311
312         /* pm */
313         "PM",
314
315         /* date_fmt */
316         "%a %Ef %X %Z %Y",
317         
318         {
319                 "January", "February", "March", "April", "May", "June",
320                 "July", "August", "September", "October", "November", "December"
321         },
322
323         /* Ef_fmt
324         ** To determine short months / day order
325         */
326         "%b %e",
327
328         /* EF_fmt
329         ** To determine long months / day order
330         */
331         "%B %e"
332 };
333
334 #define Locale (&_C_time_locale)
335
336 static char *
337 _strptime(const char *buf, const char *fmt, struct tm *tm)
338 {
339         char c;
340         const char *ptr;
341         int i,
342                 len;
343         int Ealternative, Oalternative;
344
345         ptr = fmt;
346         while (*ptr != 0) {
347                 if (*buf == 0)
348                         break;
349
350                 c = *ptr++;
351
352                 if (c != '%') {
353                         if (isspace((unsigned char)c))
354                                 while (*buf != 0 && isspace((unsigned char)*buf))
355                                         buf++;
356                         else if (c != *buf++)
357                                 return 0;
358                         continue;
359                 }
360
361                 Ealternative = 0;
362                 Oalternative = 0;
363 label:
364                 c = *ptr++;
365                 switch (c) {
366                 case 0:
367                 case '%':
368                         if (*buf++ != '%')
369                                 return 0;
370                         break;
371
372                 case '+':
373                         buf = _strptime(buf, Locale->date_fmt, tm);
374                         if (buf == 0)
375                                 return 0;
376                         break;
377
378                 case 'C':
379                         if (!isdigit((unsigned char)*buf))
380                                 return 0;
381
382                         /* XXX This will break for 3-digit centuries. */
383                         len = 2;
384                         for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
385                                 i *= 10;
386                                 i += *buf - '0';
387                                 len--;
388                         }
389                         if (i < 19)
390                                 return 0;
391
392                         tm->tm_year = i * 100 - 1900;
393                         break;
394
395                 case 'c':
396                         /* NOTE: c_fmt is intentionally ignored */
397                         buf = _strptime(buf, "%a %Ef %T %Y", tm);
398                         if (buf == 0)
399                                 return 0;
400                         break;
401
402                 case 'D':
403                         buf = _strptime(buf, "%m/%d/%y", tm);
404                         if (buf == 0)
405                                 return 0;
406                         break;
407
408                 case 'E':
409                         if (Ealternative || Oalternative)
410                                 break;
411                         Ealternative++;
412                         goto label;
413
414                 case 'O':
415                         if (Ealternative || Oalternative)
416                                 break;
417                         Oalternative++;
418                         goto label;
419
420                 case 'F':
421                 case 'f':
422                         if (!Ealternative)
423                                 break;
424                         buf = _strptime(buf, (c == 'f') ? Locale->Ef_fmt : Locale->EF_fmt, tm);
425                         if (buf == 0)
426                                 return 0;
427                         break;
428
429                 case 'R':
430                         buf = _strptime(buf, "%H:%M", tm);
431                         if (buf == 0)
432                                 return 0;
433                         break;
434
435                 case 'r':
436                         buf = _strptime(buf, "%I:%M:%S %p", tm);
437                         if (buf == 0)
438                                 return 0;
439                         break;
440
441                 case 'T':
442                         buf = _strptime(buf, "%H:%M:%S", tm);
443                         if (buf == 0)
444                                 return 0;
445                         break;
446
447                 case 'X':
448                         buf = _strptime(buf, Locale->X_fmt, tm);
449                         if (buf == 0)
450                                 return 0;
451                         break;
452
453                 case 'x':
454                         buf = _strptime(buf, Locale->x_fmt, tm);
455                         if (buf == 0)
456                                 return 0;
457                         break;
458
459                 case 'j':
460                         if (!isdigit((unsigned char)*buf))
461                                 return 0;
462
463                         len = 3;
464                         for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
465                                 i *= 10;
466                                 i += *buf - '0';
467                                 len--;
468                         }
469                         if (i < 1 || i > 366)
470                                 return 0;
471
472                         tm->tm_yday = i - 1;
473                         break;
474
475                 case 'M':
476                 case 'S':
477                         if (*buf == 0 || isspace((unsigned char)*buf))
478                                 break;
479
480                         if (!isdigit((unsigned char)*buf))
481                                 return 0;
482
483                         len = 2;
484                         for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
485                                 i *= 10;
486                                 i += *buf - '0';
487                                 len--;
488                         }
489
490                         if (c == 'M') {
491                                 if (i > 59)
492                                         return 0;
493                                 tm->tm_min = i;
494                         } else {
495                                 if (i > 60)
496                                         return 0;
497                                 tm->tm_sec = i;
498                         }
499
500                         if (*buf != 0 && isspace((unsigned char)*buf))
501                                 while (*ptr != 0 && !isspace((unsigned char)*ptr))
502                                         ptr++;
503                         break;
504
505                 case 'H':
506                 case 'I':
507                 case 'k':
508                 case 'l':
509                         /*
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.
513                          *
514                          * XXX The %l specifier may gobble one too many
515                          * digits if used incorrectly.
516                          */
517                         if (!isdigit((unsigned char)*buf))
518                                 return 0;
519
520                         len = 2;
521                         for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
522                                 i *= 10;
523                                 i += *buf - '0';
524                                 len--;
525                         }
526                         if (c == 'H' || c == 'k') {
527                                 if (i > 23)
528                                         return 0;
529                         } else if (i > 12)
530                                 return 0;
531
532                         tm->tm_hour = i;
533
534                         if (*buf != 0 && isspace((unsigned char)*buf))
535                                 while (*ptr != 0 && !isspace((unsigned char)*ptr))
536                                         ptr++;
537                         break;
538
539                 case 'p':
540                         /*
541                          * XXX This is bogus if parsed before hour-related
542                          * specifiers.
543                          */
544                         len = strlen(Locale->am);
545                         if (strncasecmp(buf, Locale->am, len) == 0) {
546                                 if (tm->tm_hour > 12)
547                                         return 0;
548                                 if (tm->tm_hour == 12)
549                                         tm->tm_hour = 0;
550                                 buf += len;
551                                 break;
552                         }
553
554                         len = strlen(Locale->pm);
555                         if (strncasecmp(buf, Locale->pm, len) == 0) {
556                                 if (tm->tm_hour > 12)
557                                         return 0;
558                                 if (tm->tm_hour != 12)
559                                         tm->tm_hour += 12;
560                                 buf += len;
561                                 break;
562                         }
563
564                         return 0;
565
566                 case 'A':
567                 case 'a':
568                         for (i = 0; i < asizeof(Locale->weekday); i++) {
569                                 if (c == 'A') {
570                                         len = strlen(Locale->weekday[i]);
571                                         if (strncasecmp(buf,
572                                                         Locale->weekday[i],
573                                                         len) == 0)
574                                                 break;
575                                 } else {
576                                         len = strlen(Locale->wday[i]);
577                                         if (strncasecmp(buf,
578                                                         Locale->wday[i],
579                                                         len) == 0)
580                                                 break;
581                                 }
582                         }
583                         if (i == asizeof(Locale->weekday))
584                                 return 0;
585
586                         tm->tm_wday = i;
587                         buf += len;
588                         break;
589
590                 case 'U':
591                 case 'W':
592                         /*
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
596                          * range for now.
597                          */
598                         if (!isdigit((unsigned char)*buf))
599                                 return 0;
600
601                         len = 2;
602                         for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
603                                 i *= 10;
604                                 i += *buf - '0';
605                                 len--;
606                         }
607                         if (i > 53)
608                                 return 0;
609
610                         if (*buf != 0 && isspace((unsigned char)*buf))
611                                 while (*ptr != 0 && !isspace((unsigned char)*ptr))
612                                         ptr++;
613                         break;
614
615                 case 'w':
616                         if (!isdigit((unsigned char)*buf))
617                                 return 0;
618
619                         i = *buf - '0';
620                         if (i > 6)
621                                 return 0;
622
623                         tm->tm_wday = i;
624
625                         if (*buf != 0 && isspace((unsigned char)*buf))
626                                 while (*ptr != 0 && !isspace((unsigned char)*ptr))
627                                         ptr++;
628                         break;
629
630                 case 'd':
631                 case 'e':
632                         /*
633                          * The %e specifier is explicitly documented as not
634                          * being zero-padded but there is no harm in allowing
635                          * such padding.
636                          *
637                          * XXX The %e specifier may gobble one too many
638                          * digits if used incorrectly.
639                          */
640                         if (!isdigit((unsigned char)*buf))
641                                 return 0;
642
643                         len = 2;
644                         for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
645                                 i *= 10;
646                                 i += *buf - '0';
647                                 len--;
648                         }
649                         if (i > 31)
650                                 return 0;
651
652                         tm->tm_mday = i;
653
654                         if (*buf != 0 && isspace((unsigned char)*buf))
655                                 while (*ptr != 0 && !isspace((unsigned char)*ptr))
656                                         ptr++;
657                         break;
658
659                 case 'B':
660                 case 'b':
661                 case 'h':
662                         for (i = 0; i < asizeof(Locale->month); i++) {
663                                 if (Oalternative) {
664                                         if (c == 'B') {
665                                                 len = strlen(Locale->alt_month[i]);
666                                                 if (strncasecmp(buf,
667                                                                 Locale->alt_month[i],
668                                                                 len) == 0)
669                                                         break;
670                                         }
671                                 } else {
672                                         if (c == 'B') {
673                                                 len = strlen(Locale->month[i]);
674                                                 if (strncasecmp(buf,
675                                                                 Locale->month[i],
676                                                                 len) == 0)
677                                                         break;
678                                         } else {
679                                                 len = strlen(Locale->mon[i]);
680                                                 if (strncasecmp(buf,
681                                                                 Locale->mon[i],
682                                                                 len) == 0)
683                                                         break;
684                                         }
685                                 }
686                         }
687                         if (i == asizeof(Locale->month))
688                                 return 0;
689
690                         tm->tm_mon = i;
691                         buf += len;
692                         break;
693
694                 case 'm':
695                         if (!isdigit((unsigned char)*buf))
696                                 return 0;
697
698                         len = 2;
699                         for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
700                                 i *= 10;
701                                 i += *buf - '0';
702                                 len--;
703                         }
704                         if (i < 1 || i > 12)
705                                 return 0;
706
707                         tm->tm_mon = i - 1;
708
709                         if (*buf != 0 && isspace((unsigned char)*buf))
710                                 while (*ptr != 0 && !isspace((unsigned char)*ptr))
711                                         ptr++;
712                         break;
713
714                 case 'Y':
715                 case 'y':
716                         if (*buf == 0 || isspace((unsigned char)*buf))
717                                 break;
718
719                         if (!isdigit((unsigned char)*buf))
720                                 return 0;
721
722                         len = (c == 'Y') ? 4 : 2;
723                         for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
724                                 i *= 10;
725                                 i += *buf - '0';
726                                 len--;
727                         }
728                         if (c == 'Y')
729                                 i -= 1900;
730                         if (c == 'y' && i < 69)
731                                 i += 100;
732                         if (i < 0)
733                                 return 0;
734
735                         tm->tm_year = i;
736
737                         if (*buf != 0 && isspace((unsigned char)*buf))
738                                 while (*ptr != 0 && !isspace((unsigned char)*ptr))
739                                         ptr++;
740                         break;
741
742                 case 'Z':
743                         {
744                         const char *cp;
745                         char *zonestr;
746
747                         for (cp = buf; *cp && isupper((unsigned char)*cp); ++cp) 
748                             {/*empty*/}
749                         if (cp - buf) {
750                                 zonestr = (char *)alloca(cp - buf + 1);
751                                 strncpy(zonestr, buf, cp - buf);
752                                 zonestr[cp - buf] = '\0';
753                                 tzset();
754                                 if (0 == strcmp(zonestr, "GMT")) {
755                                     got_GMT = 1;
756                                 } else {
757                                     return 0;
758                                 }
759                                 buf += cp - buf;
760                         }
761                         }
762                         break;
763                 }
764         }
765         return (char *)buf;
766 }
767
768
769 char *
770 strptime(const char *buf, const char *fmt, struct tm *tm)
771 {
772         char *ret;
773
774 #ifdef _THREAD_SAFE
775 pthread_mutex_lock(&gotgmt_mutex);
776 #endif
777
778         got_GMT = 0;
779         ret = _strptime(buf, fmt, tm);
780
781 #ifdef _THREAD_SAFE
782         pthread_mutex_unlock(&gotgmt_mutex);
783 #endif
784
785         return ret;
786 }
787
788 #endif /* Mac OS X */
789
790 MODULE = Time::Piece     PACKAGE = Time::Piece
791
792 PROTOTYPES: ENABLE
793
794 void
795 _strftime(fmt, sec, min, hour, mday, mon, year, wday = -1, yday = -1, isdst = -1)
796     char *        fmt
797     int        sec
798     int        min
799     int        hour
800     int        mday
801     int        mon
802     int        year
803     int        wday
804     int        yday
805     int        isdst
806     CODE:
807     {
808         char tmpbuf[128];
809         struct tm mytm;
810         int len;
811         memset(&mytm, 0, sizeof(mytm));
812         my_init_tm(&mytm);    /* XXX workaround - see my_init_tm() above */
813         mytm.tm_sec = sec;
814         mytm.tm_min = min;
815         mytm.tm_hour = hour;
816         mytm.tm_mday = mday;
817         mytm.tm_mon = mon;
818         mytm.tm_year = year;
819         mytm.tm_wday = wday;
820         mytm.tm_yday = yday;
821         mytm.tm_isdst = isdst;
822         my_mini_mktime(&mytm);
823         len = strftime(tmpbuf, sizeof tmpbuf, fmt, &mytm);
824         /*
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
836         ** all means.
837         */
838         if ((len > 0 && len < sizeof(tmpbuf)) || (len == 0 && *fmt == '\0'))
839         ST(0) = sv_2mortal(newSVpv(tmpbuf, len));
840         else {
841         /* Possibly buf overflowed - try again with a bigger buf */
842         int     fmtlen = strlen(fmt);
843         int    bufsize = fmtlen + sizeof(tmpbuf);
844         char*     buf;
845         int    buflen;
846
847         New(0, buf, bufsize, char);
848         while (buf) {
849             buflen = strftime(buf, bufsize, fmt, &mytm);
850             if (buflen > 0 && buflen < bufsize)
851             break;
852             /* heuristic to prevent out-of-memory errors */
853             if (bufsize > 100*fmtlen) {
854             Safefree(buf);
855             buf = NULL;
856             break;
857             }
858             bufsize *= 2;
859             Renew(buf, bufsize, char);
860         }
861         if (buf) {
862             ST(0) = sv_2mortal(newSVpv(buf, buflen));
863             Safefree(buf);
864         }
865         else
866             ST(0) = sv_2mortal(newSVpv(tmpbuf, len));
867         }
868     }
869
870 void
871 _tzset()
872   PPCODE:
873     tzset();
874
875
876 void
877 _strptime ( string, format )
878         char * string
879         char * format
880   PREINIT:
881        struct tm mytm;
882        time_t t;
883        char * remainder;
884   PPCODE:
885        t = 0;
886        mytm = *gmtime(&t);
887        
888        remainder = (char *)strptime(string, format, &mytm);
889        
890        if (remainder == NULL) {
891           croak("Error parsing time");
892        }
893
894        if (*remainder != '\0') {
895            warn("garbage at end of string in strptime: %s", remainder);
896        }
897           
898        my_mini_mktime(&mytm);
899
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); */
901           
902        EXTEND(SP, 11);
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)));
911        /* isdst */
912        PUSHs(sv_2mortal(newSViv(0)));
913        /* epoch */
914        PUSHs(sv_2mortal(newSViv(0)));
915        /* islocal */
916        PUSHs(sv_2mortal(newSViv(0)));
917
918 void
919 _mini_mktime(int sec, int min, int hour, int mday, int mon, int year)
920   PREINIT:
921        struct tm mytm;
922        time_t t;
923   PPCODE:
924        t = 0;
925        mytm = *gmtime(&t);
926
927        mytm.tm_sec = sec;
928        mytm.tm_min = min;
929        mytm.tm_hour = hour;
930        mytm.tm_mday = mday;
931        mytm.tm_mon = mon;
932        mytm.tm_year = year;
933        
934        my_mini_mktime(&mytm);
935
936        EXTEND(SP, 11);
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)));
945        /* isdst */
946        PUSHs(sv_2mortal(newSViv(0)));
947        /* epoch */
948        PUSHs(sv_2mortal(newSViv(0)));
949        /* islocal */
950        PUSHs(sv_2mortal(newSViv(0)));