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