Time::Piece & matherr on qnx4
[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)
16433e2b 248#define alloca _alloca
be8a15fc 249#endif
250
16433e2b 251#include <time.h>
252#include <ctype.h>
253#include <string.h>
254#ifdef _THREAD_SAFE
255#include <pthread.h>
256#include "pthread_private.h"
257#endif /* _THREAD_SAFE */
258
259static char * _strptime(const char *, const char *, struct tm *);
260
261#ifdef _THREAD_SAFE
262static struct pthread_mutex _gotgmt_mutexd = PTHREAD_MUTEX_STATIC_INITIALIZER;
263static pthread_mutex_t gotgmt_mutex = &_gotgmt_mutexd;
264#endif
265static int got_GMT;
266
267#define asizeof(a) (sizeof (a) / sizeof ((a)[0]))
268
269struct lc_time_T {
270 const char * mon[12];
271 const char * month[12];
272 const char * wday[7];
273 const char * weekday[7];
274 const char * X_fmt;
275 const char * x_fmt;
276 const char * c_fmt;
277 const char * am;
278 const char * pm;
279 const char * date_fmt;
280 const char * alt_month[12];
281 const char * Ef_fmt;
282 const char * EF_fmt;
283};
284
285struct lc_time_T _time_localebuf;
286int _time_using_locale;
287
288const struct lc_time_T _C_time_locale = {
289 {
290 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
291 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
292 }, {
293 "January", "February", "March", "April", "May", "June",
294 "July", "August", "September", "October", "November", "December"
295 }, {
296 "Sun", "Mon", "Tue", "Wed",
297 "Thu", "Fri", "Sat"
298 }, {
299 "Sunday", "Monday", "Tuesday", "Wednesday",
300 "Thursday", "Friday", "Saturday"
301 },
302
303 /* X_fmt */
304 "%H:%M:%S",
305
306 /*
307 ** x_fmt
308 ** Since the C language standard calls for
309 ** "date, using locale's date format," anything goes.
310 ** Using just numbers (as here) makes Quakers happier;
311 ** it's also compatible with SVR4.
312 */
313 "%m/%d/%y",
314
315 /*
316 ** c_fmt (ctime-compatible)
317 ** Not used, just compatibility placeholder.
318 */
319 NULL,
320
321 /* am */
322 "AM",
323
324 /* pm */
325 "PM",
326
327 /* date_fmt */
328 "%a %Ef %X %Z %Y",
329
330 {
331 "January", "February", "March", "April", "May", "June",
332 "July", "August", "September", "October", "November", "December"
333 },
334
335 /* Ef_fmt
336 ** To determine short months / day order
337 */
338 "%b %e",
339
340 /* EF_fmt
341 ** To determine long months / day order
342 */
343 "%B %e"
344};
345
346#define Locale (&_C_time_locale)
347
348static char *
349_strptime(const char *buf, const char *fmt, struct tm *tm)
350{
351 char c;
352 const char *ptr;
353 int i,
354 len;
355 int Ealternative, Oalternative;
356
357 ptr = fmt;
358 while (*ptr != 0) {
359 if (*buf == 0)
360 break;
361
362 c = *ptr++;
363
364 if (c != '%') {
365 if (isspace((unsigned char)c))
366 while (*buf != 0 && isspace((unsigned char)*buf))
367 buf++;
368 else if (c != *buf++)
369 return 0;
370 continue;
371 }
372
373 Ealternative = 0;
374 Oalternative = 0;
375label:
376 c = *ptr++;
377 switch (c) {
378 case 0:
379 case '%':
380 if (*buf++ != '%')
381 return 0;
382 break;
383
384 case '+':
385 buf = _strptime(buf, Locale->date_fmt, tm);
386 if (buf == 0)
387 return 0;
388 break;
389
390 case 'C':
391 if (!isdigit((unsigned char)*buf))
392 return 0;
393
394 /* XXX This will break for 3-digit centuries. */
395 len = 2;
396 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
397 i *= 10;
398 i += *buf - '0';
399 len--;
400 }
401 if (i < 19)
402 return 0;
403
404 tm->tm_year = i * 100 - 1900;
405 break;
406
407 case 'c':
408 /* NOTE: c_fmt is intentionally ignored */
409 buf = _strptime(buf, "%a %Ef %T %Y", tm);
410 if (buf == 0)
411 return 0;
412 break;
413
414 case 'D':
415 buf = _strptime(buf, "%m/%d/%y", tm);
416 if (buf == 0)
417 return 0;
418 break;
419
420 case 'E':
421 if (Ealternative || Oalternative)
422 break;
423 Ealternative++;
424 goto label;
425
426 case 'O':
427 if (Ealternative || Oalternative)
428 break;
429 Oalternative++;
430 goto label;
431
432 case 'F':
433 case 'f':
434 if (!Ealternative)
435 break;
436 buf = _strptime(buf, (c == 'f') ? Locale->Ef_fmt : Locale->EF_fmt, tm);
437 if (buf == 0)
438 return 0;
439 break;
440
441 case 'R':
442 buf = _strptime(buf, "%H:%M", tm);
443 if (buf == 0)
444 return 0;
445 break;
446
447 case 'r':
448 buf = _strptime(buf, "%I:%M:%S %p", tm);
449 if (buf == 0)
450 return 0;
451 break;
452
453 case 'T':
454 buf = _strptime(buf, "%H:%M:%S", tm);
455 if (buf == 0)
456 return 0;
457 break;
458
459 case 'X':
460 buf = _strptime(buf, Locale->X_fmt, tm);
461 if (buf == 0)
462 return 0;
463 break;
464
465 case 'x':
466 buf = _strptime(buf, Locale->x_fmt, tm);
467 if (buf == 0)
468 return 0;
469 break;
470
471 case 'j':
472 if (!isdigit((unsigned char)*buf))
473 return 0;
474
475 len = 3;
476 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
477 i *= 10;
478 i += *buf - '0';
479 len--;
480 }
481 if (i < 1 || i > 366)
482 return 0;
483
484 tm->tm_yday = i - 1;
485 break;
486
487 case 'M':
488 case 'S':
489 if (*buf == 0 || isspace((unsigned char)*buf))
490 break;
491
492 if (!isdigit((unsigned char)*buf))
493 return 0;
494
495 len = 2;
496 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
497 i *= 10;
498 i += *buf - '0';
499 len--;
500 }
501
502 if (c == 'M') {
503 if (i > 59)
504 return 0;
505 tm->tm_min = i;
506 } else {
507 if (i > 60)
508 return 0;
509 tm->tm_sec = i;
510 }
511
512 if (*buf != 0 && isspace((unsigned char)*buf))
513 while (*ptr != 0 && !isspace((unsigned char)*ptr))
514 ptr++;
515 break;
516
517 case 'H':
518 case 'I':
519 case 'k':
520 case 'l':
521 /*
522 * Of these, %l is the only specifier explicitly
523 * documented as not being zero-padded. However,
524 * there is no harm in allowing zero-padding.
525 *
526 * XXX The %l specifier may gobble one too many
527 * digits if used incorrectly.
528 */
529 if (!isdigit((unsigned char)*buf))
530 return 0;
531
532 len = 2;
533 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
534 i *= 10;
535 i += *buf - '0';
536 len--;
537 }
538 if (c == 'H' || c == 'k') {
539 if (i > 23)
540 return 0;
541 } else if (i > 12)
542 return 0;
543
544 tm->tm_hour = i;
545
546 if (*buf != 0 && isspace((unsigned char)*buf))
547 while (*ptr != 0 && !isspace((unsigned char)*ptr))
548 ptr++;
549 break;
550
551 case 'p':
552 /*
553 * XXX This is bogus if parsed before hour-related
554 * specifiers.
555 */
556 len = strlen(Locale->am);
557 if (strncasecmp(buf, Locale->am, len) == 0) {
558 if (tm->tm_hour > 12)
559 return 0;
560 if (tm->tm_hour == 12)
561 tm->tm_hour = 0;
562 buf += len;
563 break;
564 }
565
566 len = strlen(Locale->pm);
567 if (strncasecmp(buf, Locale->pm, len) == 0) {
568 if (tm->tm_hour > 12)
569 return 0;
570 if (tm->tm_hour != 12)
571 tm->tm_hour += 12;
572 buf += len;
573 break;
574 }
575
576 return 0;
577
578 case 'A':
579 case 'a':
580 for (i = 0; i < asizeof(Locale->weekday); i++) {
581 if (c == 'A') {
582 len = strlen(Locale->weekday[i]);
583 if (strncasecmp(buf,
584 Locale->weekday[i],
585 len) == 0)
586 break;
587 } else {
588 len = strlen(Locale->wday[i]);
589 if (strncasecmp(buf,
590 Locale->wday[i],
591 len) == 0)
592 break;
593 }
594 }
595 if (i == asizeof(Locale->weekday))
596 return 0;
597
598 tm->tm_wday = i;
599 buf += len;
600 break;
601
602 case 'U':
603 case 'W':
604 /*
605 * XXX This is bogus, as we can not assume any valid
606 * information present in the tm structure at this
607 * point to calculate a real value, so just check the
608 * range for now.
609 */
610 if (!isdigit((unsigned char)*buf))
611 return 0;
612
613 len = 2;
614 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
615 i *= 10;
616 i += *buf - '0';
617 len--;
618 }
619 if (i > 53)
620 return 0;
621
622 if (*buf != 0 && isspace((unsigned char)*buf))
623 while (*ptr != 0 && !isspace((unsigned char)*ptr))
624 ptr++;
625 break;
626
627 case 'w':
628 if (!isdigit((unsigned char)*buf))
629 return 0;
630
631 i = *buf - '0';
632 if (i > 6)
633 return 0;
634
635 tm->tm_wday = i;
636
637 if (*buf != 0 && isspace((unsigned char)*buf))
638 while (*ptr != 0 && !isspace((unsigned char)*ptr))
639 ptr++;
640 break;
641
642 case 'd':
643 case 'e':
644 /*
645 * The %e specifier is explicitly documented as not
646 * being zero-padded but there is no harm in allowing
647 * such padding.
648 *
649 * XXX The %e specifier may gobble one too many
650 * digits if used incorrectly.
651 */
652 if (!isdigit((unsigned char)*buf))
653 return 0;
654
655 len = 2;
656 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
657 i *= 10;
658 i += *buf - '0';
659 len--;
660 }
661 if (i > 31)
662 return 0;
663
664 tm->tm_mday = i;
665
666 if (*buf != 0 && isspace((unsigned char)*buf))
667 while (*ptr != 0 && !isspace((unsigned char)*ptr))
668 ptr++;
669 break;
670
671 case 'B':
672 case 'b':
673 case 'h':
674 for (i = 0; i < asizeof(Locale->month); i++) {
675 if (Oalternative) {
676 if (c == 'B') {
677 len = strlen(Locale->alt_month[i]);
678 if (strncasecmp(buf,
679 Locale->alt_month[i],
680 len) == 0)
681 break;
682 }
683 } else {
684 if (c == 'B') {
685 len = strlen(Locale->month[i]);
686 if (strncasecmp(buf,
687 Locale->month[i],
688 len) == 0)
689 break;
690 } else {
691 len = strlen(Locale->mon[i]);
692 if (strncasecmp(buf,
693 Locale->mon[i],
694 len) == 0)
695 break;
696 }
697 }
698 }
699 if (i == asizeof(Locale->month))
700 return 0;
701
702 tm->tm_mon = i;
703 buf += len;
704 break;
705
706 case 'm':
707 if (!isdigit((unsigned char)*buf))
708 return 0;
709
710 len = 2;
711 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
712 i *= 10;
713 i += *buf - '0';
714 len--;
715 }
716 if (i < 1 || i > 12)
717 return 0;
718
719 tm->tm_mon = i - 1;
720
721 if (*buf != 0 && isspace((unsigned char)*buf))
722 while (*ptr != 0 && !isspace((unsigned char)*ptr))
723 ptr++;
724 break;
725
726 case 'Y':
727 case 'y':
728 if (*buf == 0 || isspace((unsigned char)*buf))
729 break;
730
731 if (!isdigit((unsigned char)*buf))
732 return 0;
733
734 len = (c == 'Y') ? 4 : 2;
735 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
736 i *= 10;
737 i += *buf - '0';
738 len--;
739 }
740 if (c == 'Y')
741 i -= 1900;
742 if (c == 'y' && i < 69)
743 i += 100;
744 if (i < 0)
745 return 0;
746
747 tm->tm_year = i;
748
749 if (*buf != 0 && isspace((unsigned char)*buf))
750 while (*ptr != 0 && !isspace((unsigned char)*ptr))
751 ptr++;
752 break;
753
754 case 'Z':
755 {
756 const char *cp;
757 char *zonestr;
758
759 for (cp = buf; *cp && isupper((unsigned char)*cp); ++cp)
760 {/*empty*/}
761 if (cp - buf) {
d56c6e85 762 zonestr = (char *)alloca(cp - buf + 1);
16433e2b 763 strncpy(zonestr, buf, cp - buf);
764 zonestr[cp - buf] = '\0';
765 tzset();
766 if (0 == strcmp(zonestr, "GMT")) {
767 got_GMT = 1;
768 } else {
769 return 0;
770 }
771 buf += cp - buf;
772 }
773 }
774 break;
775 }
776 }
777 return (char *)buf;
778}
779
780
781char *
782strptime(const char *buf, const char *fmt, struct tm *tm)
783{
784 char *ret;
785
786#ifdef _THREAD_SAFE
787pthread_mutex_lock(&gotgmt_mutex);
788#endif
789
790 got_GMT = 0;
791 ret = _strptime(buf, fmt, tm);
792
793#ifdef _THREAD_SAFE
794 pthread_mutex_unlock(&gotgmt_mutex);
795#endif
796
797 return ret;
798}
799
800#endif /* Mac OS X */
801
802MODULE = Time::Piece PACKAGE = Time::Piece
803
804PROTOTYPES: ENABLE
805
9331e88f 806void
16433e2b 807_strftime(fmt, sec, min, hour, mday, mon, year, wday = -1, yday = -1, isdst = -1)
808 char * fmt
809 int sec
810 int min
811 int hour
812 int mday
813 int mon
814 int year
815 int wday
816 int yday
817 int isdst
818 CODE:
819 {
820 char tmpbuf[128];
821 struct tm mytm;
822 int len;
823 memset(&mytm, 0, sizeof(mytm));
824 my_init_tm(&mytm); /* XXX workaround - see my_init_tm() above */
825 mytm.tm_sec = sec;
826 mytm.tm_min = min;
827 mytm.tm_hour = hour;
828 mytm.tm_mday = mday;
829 mytm.tm_mon = mon;
830 mytm.tm_year = year;
831 mytm.tm_wday = wday;
832 mytm.tm_yday = yday;
833 mytm.tm_isdst = isdst;
834 my_mini_mktime(&mytm);
835 len = strftime(tmpbuf, sizeof tmpbuf, fmt, &mytm);
836 /*
837 ** The following is needed to handle to the situation where
838 ** tmpbuf overflows. Basically we want to allocate a buffer
839 ** and try repeatedly. The reason why it is so complicated
840 ** is that getting a return value of 0 from strftime can indicate
841 ** one of the following:
842 ** 1. buffer overflowed,
843 ** 2. illegal conversion specifier, or
844 ** 3. the format string specifies nothing to be returned(not
845 ** an error). This could be because format is an empty string
846 ** or it specifies %p that yields an empty string in some locale.
847 ** If there is a better way to make it portable, go ahead by
848 ** all means.
849 */
850 if ((len > 0 && len < sizeof(tmpbuf)) || (len == 0 && *fmt == '\0'))
851 ST(0) = sv_2mortal(newSVpv(tmpbuf, len));
852 else {
853 /* Possibly buf overflowed - try again with a bigger buf */
854 int fmtlen = strlen(fmt);
855 int bufsize = fmtlen + sizeof(tmpbuf);
856 char* buf;
857 int buflen;
858
859 New(0, buf, bufsize, char);
860 while (buf) {
861 buflen = strftime(buf, bufsize, fmt, &mytm);
862 if (buflen > 0 && buflen < bufsize)
863 break;
864 /* heuristic to prevent out-of-memory errors */
865 if (bufsize > 100*fmtlen) {
866 Safefree(buf);
867 buf = NULL;
868 break;
869 }
870 bufsize *= 2;
871 Renew(buf, bufsize, char);
872 }
873 if (buf) {
874 ST(0) = sv_2mortal(newSVpv(buf, buflen));
875 Safefree(buf);
876 }
877 else
878 ST(0) = sv_2mortal(newSVpv(tmpbuf, len));
879 }
880 }
881
882void
883_tzset()
884 PPCODE:
885 tzset();
886
887
888void
889_strptime ( string, format )
890 char * string
891 char * format
892 PREINIT:
16433e2b 893 struct tm mytm;
894 time_t t;
895 char * remainder;
16433e2b 896 PPCODE:
897 t = 0;
898 mytm = *gmtime(&t);
899
900 remainder = (char *)strptime(string, format, &mytm);
901
902 if (remainder == NULL) {
903 croak("Error parsing time");
904 }
905
906 if (*remainder != '\0') {
907 warn("garbage at end of string in strptime: %s", remainder);
908 }
909
910 my_mini_mktime(&mytm);
911
912 /* 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); */
913
914 EXTEND(SP, 11);
915 PUSHs(sv_2mortal(newSViv(mytm.tm_sec)));
916 PUSHs(sv_2mortal(newSViv(mytm.tm_min)));
917 PUSHs(sv_2mortal(newSViv(mytm.tm_hour)));
918 PUSHs(sv_2mortal(newSViv(mytm.tm_mday)));
919 PUSHs(sv_2mortal(newSViv(mytm.tm_mon)));
920 PUSHs(sv_2mortal(newSViv(mytm.tm_year)));
921 PUSHs(sv_2mortal(newSViv(mytm.tm_wday)));
922 PUSHs(sv_2mortal(newSViv(mytm.tm_yday)));
923 /* isdst */
924 PUSHs(sv_2mortal(newSViv(0)));
925 /* epoch */
926 PUSHs(sv_2mortal(newSViv(0)));
927 /* islocal */
928 PUSHs(sv_2mortal(newSViv(0)));