Silence Borland compiler warnings (except for warnings from zlib) here:
[p5sagit/p5-mst-13.2.git] / ext / Time / Piece / Piece.xs
CommitLineData
16433e2b 1#ifdef __cplusplus
c944940b 2extern "C" {
16433e2b 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
49static void
50my_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().
64 */
65static void
66my_mini_mktime(struct tm *ptm)
67{
68 int yearday;
69 int secs;
70 int month, mday, year, jday;
71 int odd_cent, odd_year;
72
73/*
74 * Year/day algorithm notes:
75 *
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.
83 *
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.
93 *
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.
116 *
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.
127 *
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
130 * care.
131 */
132
133 year = 1900 + ptm->tm_year;
134 month = ptm->tm_mon;
135 mday = ptm->tm_mday;
136 /* allow given yday with no month & mday to dominate the result */
137 if (ptm->tm_yday >= 0 && mday <= 0 && month <= 0) {
138 month = 0;
139 mday = 0;
140 jday = 1 + ptm->tm_yday;
141 }
142 else {
143 jday = 0;
144 }
145 if (month >= 2)
146 month+=2;
147 else
148 month+=14, year--;
149
150 yearday = DAYS_PER_YEAR * year + year/4 - year/100 + year/400;
151 yearday += month*MONTH_TO_DAYS + mday + jday;
152 /*
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.
157 */
158 if ((unsigned) ptm->tm_sec <= 60) {
159 secs = 0;
160 }
161 else {
162 secs = ptm->tm_sec;
163 ptm->tm_sec = 0;
164 }
165 secs += 60 * ptm->tm_min;
166 secs += SECS_PER_HOUR * ptm->tm_hour;
167 if (secs < 0) {
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);
173 }
174 else {
175 yearday += (secs/SECS_PER_DAY);
176 secs -= SECS_PER_DAY * (secs/SECS_PER_DAY);
177 }
178 }
179 else if (secs >= SECS_PER_DAY) {
180 yearday += (secs/SECS_PER_DAY);
181 secs %= SECS_PER_DAY;
182 }
183 ptm->tm_hour = secs/SECS_PER_HOUR;
184 secs %= SECS_PER_HOUR;
185 ptm->tm_min = secs/60;
186 secs %= 60;
187 ptm->tm_sec += secs;
188 /* done with time of day effects */
189 /*
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.
196 */
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;
207 year += odd_year;
208 yearday %= DAYS_PER_YEAR;
209 if (!yearday && (odd_cent==4 || odd_year==4)) { /* catch Feb 29 */
210 month = 1;
211 yearday = 29;
212 }
213 else {
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 */
218 if (month > 13) {
219 month-=14;
220 year++;
221 }
222 else {
223 month-=2;
224 }
225 }
226 ptm->tm_year = year - 1900;
227 if (yearday) {
228 ptm->tm_mday = yearday;
229 ptm->tm_mon = month;
230 }
231 else {
232 ptm->tm_mday = 31;
233 ptm->tm_mon = month - 1;
234 }
235 /* re-build yearday based on Jan 1 to get tm_yday */
236 year--;
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;
242}
243
be8a15fc 244#if defined(WIN32) || (defined(__QNX__) && defined(__WATCOMC__)) /* No strptime on Win32 or QNX4 */
16433e2b 245#define strncasecmp(x,y,n) strnicmp(x,y,n)
be8a15fc 246
247#if defined(WIN32)
0934c9d9 248#if defined(__BORLANDC__)
249void * __cdecl _EXPFUNC alloca(_SIZE_T __size);
250#else
16433e2b 251#define alloca _alloca
be8a15fc 252#endif
0934c9d9 253#endif
be8a15fc 254
16433e2b 255#include <time.h>
256#include <ctype.h>
257#include <string.h>
258#ifdef _THREAD_SAFE
259#include <pthread.h>
260#include "pthread_private.h"
261#endif /* _THREAD_SAFE */
262
263static char * _strptime(const char *, const char *, struct tm *);
264
265#ifdef _THREAD_SAFE
266static struct pthread_mutex _gotgmt_mutexd = PTHREAD_MUTEX_STATIC_INITIALIZER;
267static pthread_mutex_t gotgmt_mutex = &_gotgmt_mutexd;
268#endif
269static int got_GMT;
270
271#define asizeof(a) (sizeof (a) / sizeof ((a)[0]))
272
273struct lc_time_T {
274 const char * mon[12];
275 const char * month[12];
276 const char * wday[7];
277 const char * weekday[7];
278 const char * X_fmt;
279 const char * x_fmt;
280 const char * c_fmt;
281 const char * am;
282 const char * pm;
283 const char * date_fmt;
284 const char * alt_month[12];
285 const char * Ef_fmt;
286 const char * EF_fmt;
287};
288
289struct lc_time_T _time_localebuf;
290int _time_using_locale;
291
292const struct lc_time_T _C_time_locale = {
293 {
294 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
295 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
296 }, {
297 "January", "February", "March", "April", "May", "June",
298 "July", "August", "September", "October", "November", "December"
299 }, {
300 "Sun", "Mon", "Tue", "Wed",
301 "Thu", "Fri", "Sat"
302 }, {
303 "Sunday", "Monday", "Tuesday", "Wednesday",
304 "Thursday", "Friday", "Saturday"
305 },
306
307 /* X_fmt */
308 "%H:%M:%S",
309
310 /*
311 ** x_fmt
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.
316 */
317 "%m/%d/%y",
318
319 /*
320 ** c_fmt (ctime-compatible)
321 ** Not used, just compatibility placeholder.
322 */
323 NULL,
324
325 /* am */
326 "AM",
327
328 /* pm */
329 "PM",
330
331 /* date_fmt */
332 "%a %Ef %X %Z %Y",
333
334 {
335 "January", "February", "March", "April", "May", "June",
336 "July", "August", "September", "October", "November", "December"
337 },
338
339 /* Ef_fmt
340 ** To determine short months / day order
341 */
342 "%b %e",
343
344 /* EF_fmt
345 ** To determine long months / day order
346 */
347 "%B %e"
348};
349
350#define Locale (&_C_time_locale)
351
352static char *
353_strptime(const char *buf, const char *fmt, struct tm *tm)
354{
355 char c;
356 const char *ptr;
357 int i,
358 len;
359 int Ealternative, Oalternative;
360
361 ptr = fmt;
362 while (*ptr != 0) {
363 if (*buf == 0)
364 break;
365
366 c = *ptr++;
367
368 if (c != '%') {
369 if (isspace((unsigned char)c))
370 while (*buf != 0 && isspace((unsigned char)*buf))
371 buf++;
372 else if (c != *buf++)
373 return 0;
374 continue;
375 }
376
377 Ealternative = 0;
378 Oalternative = 0;
379label:
380 c = *ptr++;
381 switch (c) {
382 case 0:
383 case '%':
384 if (*buf++ != '%')
385 return 0;
386 break;
387
388 case '+':
389 buf = _strptime(buf, Locale->date_fmt, tm);
390 if (buf == 0)
391 return 0;
392 break;
393
394 case 'C':
395 if (!isdigit((unsigned char)*buf))
396 return 0;
397
398 /* XXX This will break for 3-digit centuries. */
399 len = 2;
400 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
401 i *= 10;
402 i += *buf - '0';
403 len--;
404 }
405 if (i < 19)
406 return 0;
407
408 tm->tm_year = i * 100 - 1900;
409 break;
410
411 case 'c':
412 /* NOTE: c_fmt is intentionally ignored */
413 buf = _strptime(buf, "%a %Ef %T %Y", tm);
414 if (buf == 0)
415 return 0;
416 break;
417
418 case 'D':
419 buf = _strptime(buf, "%m/%d/%y", tm);
420 if (buf == 0)
421 return 0;
422 break;
423
424 case 'E':
425 if (Ealternative || Oalternative)
426 break;
427 Ealternative++;
428 goto label;
429
430 case 'O':
431 if (Ealternative || Oalternative)
432 break;
433 Oalternative++;
434 goto label;
435
436 case 'F':
437 case 'f':
438 if (!Ealternative)
439 break;
440 buf = _strptime(buf, (c == 'f') ? Locale->Ef_fmt : Locale->EF_fmt, tm);
441 if (buf == 0)
442 return 0;
443 break;
444
445 case 'R':
446 buf = _strptime(buf, "%H:%M", tm);
447 if (buf == 0)
448 return 0;
449 break;
450
451 case 'r':
452 buf = _strptime(buf, "%I:%M:%S %p", tm);
453 if (buf == 0)
454 return 0;
455 break;
456
457 case 'T':
458 buf = _strptime(buf, "%H:%M:%S", tm);
459 if (buf == 0)
460 return 0;
461 break;
462
463 case 'X':
464 buf = _strptime(buf, Locale->X_fmt, tm);
465 if (buf == 0)
466 return 0;
467 break;
468
469 case 'x':
470 buf = _strptime(buf, Locale->x_fmt, tm);
471 if (buf == 0)
472 return 0;
473 break;
474
475 case 'j':
476 if (!isdigit((unsigned char)*buf))
477 return 0;
478
479 len = 3;
480 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
481 i *= 10;
482 i += *buf - '0';
483 len--;
484 }
485 if (i < 1 || i > 366)
486 return 0;
487
488 tm->tm_yday = i - 1;
489 break;
490
491 case 'M':
492 case 'S':
493 if (*buf == 0 || isspace((unsigned char)*buf))
494 break;
495
496 if (!isdigit((unsigned char)*buf))
497 return 0;
498
499 len = 2;
500 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
501 i *= 10;
502 i += *buf - '0';
503 len--;
504 }
505
506 if (c == 'M') {
507 if (i > 59)
508 return 0;
509 tm->tm_min = i;
510 } else {
511 if (i > 60)
512 return 0;
513 tm->tm_sec = i;
514 }
515
516 if (*buf != 0 && isspace((unsigned char)*buf))
517 while (*ptr != 0 && !isspace((unsigned char)*ptr))
518 ptr++;
519 break;
520
521 case 'H':
522 case 'I':
523 case 'k':
524 case 'l':
525 /*
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.
529 *
530 * XXX The %l specifier may gobble one too many
531 * digits if used incorrectly.
532 */
533 if (!isdigit((unsigned char)*buf))
534 return 0;
535
536 len = 2;
537 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
538 i *= 10;
539 i += *buf - '0';
540 len--;
541 }
542 if (c == 'H' || c == 'k') {
543 if (i > 23)
544 return 0;
545 } else if (i > 12)
546 return 0;
547
548 tm->tm_hour = i;
549
550 if (*buf != 0 && isspace((unsigned char)*buf))
551 while (*ptr != 0 && !isspace((unsigned char)*ptr))
552 ptr++;
553 break;
554
555 case 'p':
556 /*
557 * XXX This is bogus if parsed before hour-related
558 * specifiers.
559 */
560 len = strlen(Locale->am);
561 if (strncasecmp(buf, Locale->am, len) == 0) {
562 if (tm->tm_hour > 12)
563 return 0;
564 if (tm->tm_hour == 12)
565 tm->tm_hour = 0;
566 buf += len;
567 break;
568 }
569
570 len = strlen(Locale->pm);
571 if (strncasecmp(buf, Locale->pm, len) == 0) {
572 if (tm->tm_hour > 12)
573 return 0;
574 if (tm->tm_hour != 12)
575 tm->tm_hour += 12;
576 buf += len;
577 break;
578 }
579
580 return 0;
581
582 case 'A':
583 case 'a':
584 for (i = 0; i < asizeof(Locale->weekday); i++) {
585 if (c == 'A') {
586 len = strlen(Locale->weekday[i]);
587 if (strncasecmp(buf,
588 Locale->weekday[i],
589 len) == 0)
590 break;
591 } else {
592 len = strlen(Locale->wday[i]);
593 if (strncasecmp(buf,
594 Locale->wday[i],
595 len) == 0)
596 break;
597 }
598 }
599 if (i == asizeof(Locale->weekday))
600 return 0;
601
602 tm->tm_wday = i;
603 buf += len;
604 break;
605
606 case 'U':
607 case 'W':
608 /*
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
612 * range for now.
613 */
614 if (!isdigit((unsigned char)*buf))
615 return 0;
616
617 len = 2;
618 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
619 i *= 10;
620 i += *buf - '0';
621 len--;
622 }
623 if (i > 53)
624 return 0;
625
626 if (*buf != 0 && isspace((unsigned char)*buf))
627 while (*ptr != 0 && !isspace((unsigned char)*ptr))
628 ptr++;
629 break;
630
631 case 'w':
632 if (!isdigit((unsigned char)*buf))
633 return 0;
634
635 i = *buf - '0';
636 if (i > 6)
637 return 0;
638
639 tm->tm_wday = i;
640
641 if (*buf != 0 && isspace((unsigned char)*buf))
642 while (*ptr != 0 && !isspace((unsigned char)*ptr))
643 ptr++;
644 break;
645
646 case 'd':
647 case 'e':
648 /*
649 * The %e specifier is explicitly documented as not
650 * being zero-padded but there is no harm in allowing
651 * such padding.
652 *
653 * XXX The %e specifier may gobble one too many
654 * digits if used incorrectly.
655 */
656 if (!isdigit((unsigned char)*buf))
657 return 0;
658
659 len = 2;
660 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
661 i *= 10;
662 i += *buf - '0';
663 len--;
664 }
665 if (i > 31)
666 return 0;
667
668 tm->tm_mday = i;
669
670 if (*buf != 0 && isspace((unsigned char)*buf))
671 while (*ptr != 0 && !isspace((unsigned char)*ptr))
672 ptr++;
673 break;
674
675 case 'B':
676 case 'b':
677 case 'h':
678 for (i = 0; i < asizeof(Locale->month); i++) {
679 if (Oalternative) {
680 if (c == 'B') {
681 len = strlen(Locale->alt_month[i]);
682 if (strncasecmp(buf,
683 Locale->alt_month[i],
684 len) == 0)
685 break;
686 }
687 } else {
688 if (c == 'B') {
689 len = strlen(Locale->month[i]);
690 if (strncasecmp(buf,
691 Locale->month[i],
692 len) == 0)
693 break;
694 } else {
695 len = strlen(Locale->mon[i]);
696 if (strncasecmp(buf,
697 Locale->mon[i],
698 len) == 0)
699 break;
700 }
701 }
702 }
703 if (i == asizeof(Locale->month))
704 return 0;
705
706 tm->tm_mon = i;
707 buf += len;
708 break;
709
710 case 'm':
711 if (!isdigit((unsigned char)*buf))
712 return 0;
713
714 len = 2;
715 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
716 i *= 10;
717 i += *buf - '0';
718 len--;
719 }
720 if (i < 1 || i > 12)
721 return 0;
722
723 tm->tm_mon = i - 1;
724
725 if (*buf != 0 && isspace((unsigned char)*buf))
726 while (*ptr != 0 && !isspace((unsigned char)*ptr))
727 ptr++;
728 break;
729
730 case 'Y':
731 case 'y':
732 if (*buf == 0 || isspace((unsigned char)*buf))
733 break;
734
735 if (!isdigit((unsigned char)*buf))
736 return 0;
737
738 len = (c == 'Y') ? 4 : 2;
739 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
740 i *= 10;
741 i += *buf - '0';
742 len--;
743 }
744 if (c == 'Y')
745 i -= 1900;
746 if (c == 'y' && i < 69)
747 i += 100;
748 if (i < 0)
749 return 0;
750
751 tm->tm_year = i;
752
753 if (*buf != 0 && isspace((unsigned char)*buf))
754 while (*ptr != 0 && !isspace((unsigned char)*ptr))
755 ptr++;
756 break;
757
758 case 'Z':
759 {
760 const char *cp;
761 char *zonestr;
762
763 for (cp = buf; *cp && isupper((unsigned char)*cp); ++cp)
764 {/*empty*/}
765 if (cp - buf) {
d56c6e85 766 zonestr = (char *)alloca(cp - buf + 1);
16433e2b 767 strncpy(zonestr, buf, cp - buf);
768 zonestr[cp - buf] = '\0';
769 tzset();
770 if (0 == strcmp(zonestr, "GMT")) {
771 got_GMT = 1;
772 } else {
773 return 0;
774 }
775 buf += cp - buf;
776 }
777 }
778 break;
779 }
780 }
781 return (char *)buf;
782}
783
784
785char *
786strptime(const char *buf, const char *fmt, struct tm *tm)
787{
788 char *ret;
789
790#ifdef _THREAD_SAFE
791pthread_mutex_lock(&gotgmt_mutex);
792#endif
793
794 got_GMT = 0;
795 ret = _strptime(buf, fmt, tm);
796
797#ifdef _THREAD_SAFE
798 pthread_mutex_unlock(&gotgmt_mutex);
799#endif
800
801 return ret;
802}
803
804#endif /* Mac OS X */
805
806MODULE = Time::Piece PACKAGE = Time::Piece
807
808PROTOTYPES: ENABLE
809
9331e88f 810void
16433e2b 811_strftime(fmt, sec, min, hour, mday, mon, year, wday = -1, yday = -1, isdst = -1)
812 char * fmt
813 int sec
814 int min
815 int hour
816 int mday
817 int mon
818 int year
819 int wday
820 int yday
821 int isdst
822 CODE:
823 {
824 char tmpbuf[128];
825 struct tm mytm;
826 int len;
827 memset(&mytm, 0, sizeof(mytm));
828 my_init_tm(&mytm); /* XXX workaround - see my_init_tm() above */
829 mytm.tm_sec = sec;
830 mytm.tm_min = min;
831 mytm.tm_hour = hour;
832 mytm.tm_mday = mday;
833 mytm.tm_mon = mon;
834 mytm.tm_year = year;
835 mytm.tm_wday = wday;
836 mytm.tm_yday = yday;
837 mytm.tm_isdst = isdst;
838 my_mini_mktime(&mytm);
839 len = strftime(tmpbuf, sizeof tmpbuf, fmt, &mytm);
840 /*
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
852 ** all means.
853 */
854 if ((len > 0 && len < sizeof(tmpbuf)) || (len == 0 && *fmt == '\0'))
855 ST(0) = sv_2mortal(newSVpv(tmpbuf, len));
856 else {
857 /* Possibly buf overflowed - try again with a bigger buf */
858 int fmtlen = strlen(fmt);
859 int bufsize = fmtlen + sizeof(tmpbuf);
860 char* buf;
861 int buflen;
862
863 New(0, buf, bufsize, char);
864 while (buf) {
865 buflen = strftime(buf, bufsize, fmt, &mytm);
866 if (buflen > 0 && buflen < bufsize)
867 break;
868 /* heuristic to prevent out-of-memory errors */
869 if (bufsize > 100*fmtlen) {
870 Safefree(buf);
871 buf = NULL;
872 break;
873 }
874 bufsize *= 2;
875 Renew(buf, bufsize, char);
876 }
877 if (buf) {
878 ST(0) = sv_2mortal(newSVpv(buf, buflen));
879 Safefree(buf);
880 }
881 else
882 ST(0) = sv_2mortal(newSVpv(tmpbuf, len));
883 }
884 }
885
886void
887_tzset()
888 PPCODE:
889 tzset();
890
891
892void
893_strptime ( string, format )
894 char * string
895 char * format
896 PREINIT:
16433e2b 897 struct tm mytm;
898 time_t t;
899 char * remainder;
16433e2b 900 PPCODE:
901 t = 0;
902 mytm = *gmtime(&t);
903
904 remainder = (char *)strptime(string, format, &mytm);
905
906 if (remainder == NULL) {
907 croak("Error parsing time");
908 }
909
910 if (*remainder != '\0') {
911 warn("garbage at end of string in strptime: %s", remainder);
912 }
913
914 my_mini_mktime(&mytm);
915
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); */
917
918 EXTEND(SP, 11);
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)));
927 /* isdst */
928 PUSHs(sv_2mortal(newSViv(0)));
929 /* epoch */
930 PUSHs(sv_2mortal(newSViv(0)));
931 /* islocal */
932 PUSHs(sv_2mortal(newSViv(0)));