Make Time::Piece use the init_tm() in util.c, bump to 1.13_03.
[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)
d4d72fe6 13 * fields for which we don't have Configure support prior to Perl 5.8.0:
16433e2b 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 *
d4d72fe6 22 * This is a vestigial workaround for Perls prior to 5.8.0. We now
23 * rely on the initialization (still likely a workaround) in util.c.
16433e2b 24 */
d4d72fe6 25#if !defined(PERL_VERSION) || PERL_VERSION < 8
26
036055ae 27#if defined(HAS_GNULIBC)
16433e2b 28# ifndef STRUCT_TM_HASZONE
29# define STRUCT_TM_HASZONE
30# else
31# define USE_TM_GMTOFF
32# endif
33#endif
34
d4d72fe6 35#endif /* end of pre-5.8 */
36
16433e2b 37#define DAYS_PER_YEAR 365
38#define DAYS_PER_QYEAR (4*DAYS_PER_YEAR+1)
39#define DAYS_PER_CENT (25*DAYS_PER_QYEAR-1)
40#define DAYS_PER_QCENT (4*DAYS_PER_CENT+1)
41#define SECS_PER_HOUR (60*60)
42#define SECS_PER_DAY (24*SECS_PER_HOUR)
43/* parentheses deliberately absent on these two, otherwise they don't work */
44#define MONTH_TO_DAYS 153/5
45#define DAYS_TO_MONTH 5/153
46/* offset to bias by March (month 4) 1st between month/mday & year finding */
47#define YEAR_ADJUST (4*MONTH_TO_DAYS+1)
48/* as used here, the algorithm leaves Sunday as day 1 unless we adjust it */
49#define WEEKDAY_BIAS 6 /* (1+6)%7 makes Sunday 0 again */
50
d4d72fe6 51#if !defined(PERL_VERSION) || PERL_VERSION < 8
52
16433e2b 53#ifdef STRUCT_TM_HASZONE
54static void
55my_init_tm(struct tm *ptm) /* see mktime, strftime and asctime */
56{
57 Time_t now;
58 (void)time(&now);
59 Copy(localtime(&now), ptm, 1, struct tm);
60}
61
62#else
63# define my_init_tm(ptm)
64#endif
65
d4d72fe6 66#else
67/* use core version from util.c in 5.8.0 and later */
68# define my_init_tm init_tm
69#endif
70
16433e2b 71/*
72 * my_mini_mktime - normalise struct tm values without the localtime()
124e6c84 73 * semantics (and overhead) of mktime(). Stolen shamelessly from Perl's
74 * Perl_mini_mktime() in util.c - for details on the algorithm, see that
75 * file.
16433e2b 76 */
77static void
78my_mini_mktime(struct tm *ptm)
79{
80 int yearday;
81 int secs;
82 int month, mday, year, jday;
83 int odd_cent, odd_year;
84
16433e2b 85 year = 1900 + ptm->tm_year;
86 month = ptm->tm_mon;
87 mday = ptm->tm_mday;
88 /* allow given yday with no month & mday to dominate the result */
89 if (ptm->tm_yday >= 0 && mday <= 0 && month <= 0) {
90 month = 0;
91 mday = 0;
92 jday = 1 + ptm->tm_yday;
93 }
94 else {
95 jday = 0;
96 }
97 if (month >= 2)
98 month+=2;
99 else
100 month+=14, year--;
101
102 yearday = DAYS_PER_YEAR * year + year/4 - year/100 + year/400;
103 yearday += month*MONTH_TO_DAYS + mday + jday;
104 /*
105 * Note that we don't know when leap-seconds were or will be,
106 * so we have to trust the user if we get something which looks
107 * like a sensible leap-second. Wild values for seconds will
108 * be rationalised, however.
109 */
110 if ((unsigned) ptm->tm_sec <= 60) {
111 secs = 0;
112 }
113 else {
114 secs = ptm->tm_sec;
115 ptm->tm_sec = 0;
116 }
117 secs += 60 * ptm->tm_min;
118 secs += SECS_PER_HOUR * ptm->tm_hour;
119 if (secs < 0) {
120 if (secs-(secs/SECS_PER_DAY*SECS_PER_DAY) < 0) {
121 /* got negative remainder, but need positive time */
122 /* back off an extra day to compensate */
123 yearday += (secs/SECS_PER_DAY)-1;
124 secs -= SECS_PER_DAY * (secs/SECS_PER_DAY - 1);
125 }
126 else {
127 yearday += (secs/SECS_PER_DAY);
128 secs -= SECS_PER_DAY * (secs/SECS_PER_DAY);
129 }
130 }
131 else if (secs >= SECS_PER_DAY) {
132 yearday += (secs/SECS_PER_DAY);
133 secs %= SECS_PER_DAY;
134 }
135 ptm->tm_hour = secs/SECS_PER_HOUR;
136 secs %= SECS_PER_HOUR;
137 ptm->tm_min = secs/60;
138 secs %= 60;
139 ptm->tm_sec += secs;
140 /* done with time of day effects */
141 /*
142 * The algorithm for yearday has (so far) left it high by 428.
143 * To avoid mistaking a legitimate Feb 29 as Mar 1, we need to
144 * bias it by 123 while trying to figure out what year it
145 * really represents. Even with this tweak, the reverse
146 * translation fails for years before A.D. 0001.
147 * It would still fail for Feb 29, but we catch that one below.
148 */
149 jday = yearday; /* save for later fixup vis-a-vis Jan 1 */
150 yearday -= YEAR_ADJUST;
151 year = (yearday / DAYS_PER_QCENT) * 400;
152 yearday %= DAYS_PER_QCENT;
153 odd_cent = yearday / DAYS_PER_CENT;
154 year += odd_cent * 100;
155 yearday %= DAYS_PER_CENT;
156 year += (yearday / DAYS_PER_QYEAR) * 4;
157 yearday %= DAYS_PER_QYEAR;
158 odd_year = yearday / DAYS_PER_YEAR;
159 year += odd_year;
160 yearday %= DAYS_PER_YEAR;
161 if (!yearday && (odd_cent==4 || odd_year==4)) { /* catch Feb 29 */
162 month = 1;
163 yearday = 29;
164 }
165 else {
166 yearday += YEAR_ADJUST; /* recover March 1st crock */
167 month = yearday*DAYS_TO_MONTH;
168 yearday -= month*MONTH_TO_DAYS;
169 /* recover other leap-year adjustment */
170 if (month > 13) {
171 month-=14;
172 year++;
173 }
174 else {
175 month-=2;
176 }
177 }
178 ptm->tm_year = year - 1900;
179 if (yearday) {
180 ptm->tm_mday = yearday;
181 ptm->tm_mon = month;
182 }
183 else {
184 ptm->tm_mday = 31;
185 ptm->tm_mon = month - 1;
186 }
187 /* re-build yearday based on Jan 1 to get tm_yday */
188 year--;
189 yearday = year*DAYS_PER_YEAR + year/4 - year/100 + year/400;
190 yearday += 14*MONTH_TO_DAYS + 1;
191 ptm->tm_yday = jday - yearday;
192 /* fix tm_wday if not overridden by caller */
193 ptm->tm_wday = (jday + WEEKDAY_BIAS) % 7;
194}
195
124e6c84 196/* No strptime on Win32 or QNX4 */
197#if defined(WIN32) || (defined(__QNX__) && defined(__WATCOMC__))
16433e2b 198#define strncasecmp(x,y,n) strnicmp(x,y,n)
be8a15fc 199
200#if defined(WIN32)
0db9c0cf 201#if defined(__BORLANDC__)
202void * __cdecl _EXPFUNC alloca(_SIZE_T __size);
203#else
16433e2b 204#define alloca _alloca
be8a15fc 205#endif
0db9c0cf 206#endif
124e6c84 207
208/* strptime copied from freebsd with the following copyright: */
209/*
210 * Copyright (c) 1994 Powerdog Industries. All rights reserved.
211 *
212 * Redistribution and use in source and binary forms, with or without
213 * modification, are permitted provided that the following conditions
214 * are met:
215 * 1. Redistributions of source code must retain the above copyright
216 * notice, this list of conditions and the following disclaimer.
217 * 2. Redistributions in binary form must reproduce the above copyright
218 * notice, this list of conditions and the following disclaimer
219 * in the documentation and/or other materials provided with the
220 * distribution.
221 * 3. All advertising materials mentioning features or use of this
222 * software must display the following acknowledgement:
223 * This product includes software developed by Powerdog Industries.
224 * 4. The name of Powerdog Industries may not be used to endorse or
225 * promote products derived from this software without specific prior
226 * written permission.
227 *
228 * THIS SOFTWARE IS PROVIDED BY POWERDOG INDUSTRIES ``AS IS'' AND ANY
229 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
230 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
231 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE POWERDOG INDUSTRIES BE
232 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
233 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
234 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
235 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
236 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
237 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
238 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
239 */
240
241#ifndef lint
242#ifndef NOID
243static char copyright[] =
244"@(#) Copyright (c) 1994 Powerdog Industries. All rights reserved.";
245static char sccsid[] = "@(#)strptime.c 0.1 (Powerdog) 94/03/27";
246#endif /* !defined NOID */
247#endif /* not lint */
be8a15fc 248
16433e2b 249#include <time.h>
250#include <ctype.h>
251#include <string.h>
252#ifdef _THREAD_SAFE
253#include <pthread.h>
254#include "pthread_private.h"
255#endif /* _THREAD_SAFE */
256
257static char * _strptime(const char *, const char *, struct tm *);
258
259#ifdef _THREAD_SAFE
260static struct pthread_mutex _gotgmt_mutexd = PTHREAD_MUTEX_STATIC_INITIALIZER;
261static pthread_mutex_t gotgmt_mutex = &_gotgmt_mutexd;
262#endif
263static int got_GMT;
264
265#define asizeof(a) (sizeof (a) / sizeof ((a)[0]))
266
267struct lc_time_T {
268 const char * mon[12];
269 const char * month[12];
270 const char * wday[7];
271 const char * weekday[7];
272 const char * X_fmt;
273 const char * x_fmt;
274 const char * c_fmt;
275 const char * am;
276 const char * pm;
277 const char * date_fmt;
278 const char * alt_month[12];
279 const char * Ef_fmt;
280 const char * EF_fmt;
281};
282
283struct lc_time_T _time_localebuf;
284int _time_using_locale;
285
286const struct lc_time_T _C_time_locale = {
287 {
288 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
289 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
290 }, {
291 "January", "February", "March", "April", "May", "June",
292 "July", "August", "September", "October", "November", "December"
293 }, {
294 "Sun", "Mon", "Tue", "Wed",
295 "Thu", "Fri", "Sat"
296 }, {
297 "Sunday", "Monday", "Tuesday", "Wednesday",
298 "Thursday", "Friday", "Saturday"
299 },
300
301 /* X_fmt */
302 "%H:%M:%S",
303
304 /*
305 ** x_fmt
306 ** Since the C language standard calls for
307 ** "date, using locale's date format," anything goes.
308 ** Using just numbers (as here) makes Quakers happier;
309 ** it's also compatible with SVR4.
310 */
311 "%m/%d/%y",
312
313 /*
314 ** c_fmt (ctime-compatible)
315 ** Not used, just compatibility placeholder.
316 */
317 NULL,
318
319 /* am */
320 "AM",
321
322 /* pm */
323 "PM",
324
325 /* date_fmt */
326 "%a %Ef %X %Z %Y",
327
328 {
329 "January", "February", "March", "April", "May", "June",
330 "July", "August", "September", "October", "November", "December"
331 },
332
333 /* Ef_fmt
334 ** To determine short months / day order
335 */
336 "%b %e",
337
338 /* EF_fmt
339 ** To determine long months / day order
340 */
341 "%B %e"
342};
343
344#define Locale (&_C_time_locale)
345
346static char *
347_strptime(const char *buf, const char *fmt, struct tm *tm)
348{
349 char c;
350 const char *ptr;
351 int i,
352 len;
353 int Ealternative, Oalternative;
354
355 ptr = fmt;
356 while (*ptr != 0) {
357 if (*buf == 0)
358 break;
359
360 c = *ptr++;
361
362 if (c != '%') {
363 if (isspace((unsigned char)c))
364 while (*buf != 0 && isspace((unsigned char)*buf))
365 buf++;
366 else if (c != *buf++)
367 return 0;
368 continue;
369 }
370
371 Ealternative = 0;
372 Oalternative = 0;
373label:
374 c = *ptr++;
375 switch (c) {
376 case 0:
377 case '%':
378 if (*buf++ != '%')
379 return 0;
380 break;
381
382 case '+':
383 buf = _strptime(buf, Locale->date_fmt, tm);
384 if (buf == 0)
385 return 0;
386 break;
387
388 case 'C':
389 if (!isdigit((unsigned char)*buf))
390 return 0;
391
392 /* XXX This will break for 3-digit centuries. */
393 len = 2;
394 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
395 i *= 10;
396 i += *buf - '0';
397 len--;
398 }
399 if (i < 19)
400 return 0;
401
402 tm->tm_year = i * 100 - 1900;
403 break;
404
405 case 'c':
406 /* NOTE: c_fmt is intentionally ignored */
407 buf = _strptime(buf, "%a %Ef %T %Y", tm);
408 if (buf == 0)
409 return 0;
410 break;
411
412 case 'D':
413 buf = _strptime(buf, "%m/%d/%y", tm);
414 if (buf == 0)
415 return 0;
416 break;
417
418 case 'E':
419 if (Ealternative || Oalternative)
420 break;
421 Ealternative++;
422 goto label;
423
424 case 'O':
425 if (Ealternative || Oalternative)
426 break;
427 Oalternative++;
428 goto label;
429
430 case 'F':
431 case 'f':
432 if (!Ealternative)
433 break;
434 buf = _strptime(buf, (c == 'f') ? Locale->Ef_fmt : Locale->EF_fmt, tm);
435 if (buf == 0)
436 return 0;
437 break;
438
439 case 'R':
440 buf = _strptime(buf, "%H:%M", tm);
441 if (buf == 0)
442 return 0;
443 break;
444
445 case 'r':
446 buf = _strptime(buf, "%I:%M:%S %p", tm);
447 if (buf == 0)
448 return 0;
449 break;
450
451 case 'T':
452 buf = _strptime(buf, "%H:%M:%S", tm);
453 if (buf == 0)
454 return 0;
455 break;
456
457 case 'X':
458 buf = _strptime(buf, Locale->X_fmt, 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 'j':
470 if (!isdigit((unsigned char)*buf))
471 return 0;
472
473 len = 3;
474 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
475 i *= 10;
476 i += *buf - '0';
477 len--;
478 }
479 if (i < 1 || i > 366)
480 return 0;
481
482 tm->tm_yday = i - 1;
483 break;
484
485 case 'M':
486 case 'S':
487 if (*buf == 0 || isspace((unsigned char)*buf))
488 break;
489
490 if (!isdigit((unsigned char)*buf))
491 return 0;
492
493 len = 2;
494 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
495 i *= 10;
496 i += *buf - '0';
497 len--;
498 }
499
500 if (c == 'M') {
501 if (i > 59)
502 return 0;
503 tm->tm_min = i;
504 } else {
505 if (i > 60)
506 return 0;
507 tm->tm_sec = i;
508 }
509
510 if (*buf != 0 && isspace((unsigned char)*buf))
511 while (*ptr != 0 && !isspace((unsigned char)*ptr))
512 ptr++;
513 break;
514
515 case 'H':
516 case 'I':
517 case 'k':
518 case 'l':
519 /*
520 * Of these, %l is the only specifier explicitly
521 * documented as not being zero-padded. However,
522 * there is no harm in allowing zero-padding.
523 *
524 * XXX The %l specifier may gobble one too many
525 * digits if used incorrectly.
526 */
527 if (!isdigit((unsigned char)*buf))
528 return 0;
529
530 len = 2;
531 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
532 i *= 10;
533 i += *buf - '0';
534 len--;
535 }
536 if (c == 'H' || c == 'k') {
537 if (i > 23)
538 return 0;
539 } else if (i > 12)
540 return 0;
541
542 tm->tm_hour = i;
543
544 if (*buf != 0 && isspace((unsigned char)*buf))
545 while (*ptr != 0 && !isspace((unsigned char)*ptr))
546 ptr++;
547 break;
548
549 case 'p':
550 /*
551 * XXX This is bogus if parsed before hour-related
552 * specifiers.
553 */
554 len = strlen(Locale->am);
555 if (strncasecmp(buf, Locale->am, len) == 0) {
556 if (tm->tm_hour > 12)
557 return 0;
558 if (tm->tm_hour == 12)
559 tm->tm_hour = 0;
560 buf += len;
561 break;
562 }
563
564 len = strlen(Locale->pm);
565 if (strncasecmp(buf, Locale->pm, len) == 0) {
566 if (tm->tm_hour > 12)
567 return 0;
568 if (tm->tm_hour != 12)
569 tm->tm_hour += 12;
570 buf += len;
571 break;
572 }
573
574 return 0;
575
576 case 'A':
577 case 'a':
578 for (i = 0; i < asizeof(Locale->weekday); i++) {
579 if (c == 'A') {
580 len = strlen(Locale->weekday[i]);
581 if (strncasecmp(buf,
582 Locale->weekday[i],
583 len) == 0)
584 break;
585 } else {
586 len = strlen(Locale->wday[i]);
587 if (strncasecmp(buf,
588 Locale->wday[i],
589 len) == 0)
590 break;
591 }
592 }
593 if (i == asizeof(Locale->weekday))
594 return 0;
595
596 tm->tm_wday = i;
597 buf += len;
598 break;
599
600 case 'U':
601 case 'W':
602 /*
603 * XXX This is bogus, as we can not assume any valid
604 * information present in the tm structure at this
605 * point to calculate a real value, so just check the
606 * range for now.
607 */
608 if (!isdigit((unsigned char)*buf))
609 return 0;
610
611 len = 2;
612 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
613 i *= 10;
614 i += *buf - '0';
615 len--;
616 }
617 if (i > 53)
618 return 0;
619
620 if (*buf != 0 && isspace((unsigned char)*buf))
621 while (*ptr != 0 && !isspace((unsigned char)*ptr))
622 ptr++;
623 break;
624
625 case 'w':
626 if (!isdigit((unsigned char)*buf))
627 return 0;
628
629 i = *buf - '0';
630 if (i > 6)
631 return 0;
632
633 tm->tm_wday = i;
634
635 if (*buf != 0 && isspace((unsigned char)*buf))
636 while (*ptr != 0 && !isspace((unsigned char)*ptr))
637 ptr++;
638 break;
639
640 case 'd':
641 case 'e':
642 /*
643 * The %e specifier is explicitly documented as not
644 * being zero-padded but there is no harm in allowing
645 * such padding.
646 *
647 * XXX The %e specifier may gobble one too many
648 * digits if used incorrectly.
649 */
650 if (!isdigit((unsigned char)*buf))
651 return 0;
652
653 len = 2;
654 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
655 i *= 10;
656 i += *buf - '0';
657 len--;
658 }
659 if (i > 31)
660 return 0;
661
662 tm->tm_mday = i;
663
664 if (*buf != 0 && isspace((unsigned char)*buf))
665 while (*ptr != 0 && !isspace((unsigned char)*ptr))
666 ptr++;
667 break;
668
669 case 'B':
670 case 'b':
671 case 'h':
672 for (i = 0; i < asizeof(Locale->month); i++) {
673 if (Oalternative) {
674 if (c == 'B') {
675 len = strlen(Locale->alt_month[i]);
676 if (strncasecmp(buf,
677 Locale->alt_month[i],
678 len) == 0)
679 break;
680 }
681 } else {
682 if (c == 'B') {
683 len = strlen(Locale->month[i]);
684 if (strncasecmp(buf,
685 Locale->month[i],
686 len) == 0)
687 break;
688 } else {
689 len = strlen(Locale->mon[i]);
690 if (strncasecmp(buf,
691 Locale->mon[i],
692 len) == 0)
693 break;
694 }
695 }
696 }
697 if (i == asizeof(Locale->month))
698 return 0;
699
700 tm->tm_mon = i;
701 buf += len;
702 break;
703
704 case 'm':
705 if (!isdigit((unsigned char)*buf))
706 return 0;
707
708 len = 2;
709 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
710 i *= 10;
711 i += *buf - '0';
712 len--;
713 }
714 if (i < 1 || i > 12)
715 return 0;
716
717 tm->tm_mon = i - 1;
718
719 if (*buf != 0 && isspace((unsigned char)*buf))
720 while (*ptr != 0 && !isspace((unsigned char)*ptr))
721 ptr++;
722 break;
723
724 case 'Y':
725 case 'y':
726 if (*buf == 0 || isspace((unsigned char)*buf))
727 break;
728
729 if (!isdigit((unsigned char)*buf))
730 return 0;
731
732 len = (c == 'Y') ? 4 : 2;
733 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
734 i *= 10;
735 i += *buf - '0';
736 len--;
737 }
738 if (c == 'Y')
739 i -= 1900;
740 if (c == 'y' && i < 69)
741 i += 100;
742 if (i < 0)
743 return 0;
744
745 tm->tm_year = i;
746
747 if (*buf != 0 && isspace((unsigned char)*buf))
748 while (*ptr != 0 && !isspace((unsigned char)*ptr))
749 ptr++;
750 break;
751
752 case 'Z':
753 {
754 const char *cp;
755 char *zonestr;
756
757 for (cp = buf; *cp && isupper((unsigned char)*cp); ++cp)
758 {/*empty*/}
759 if (cp - buf) {
d56c6e85 760 zonestr = (char *)alloca(cp - buf + 1);
16433e2b 761 strncpy(zonestr, buf, cp - buf);
762 zonestr[cp - buf] = '\0';
763 tzset();
764 if (0 == strcmp(zonestr, "GMT")) {
765 got_GMT = 1;
766 } else {
767 return 0;
768 }
769 buf += cp - buf;
770 }
771 }
772 break;
773 }
774 }
775 return (char *)buf;
776}
777
778
779char *
780strptime(const char *buf, const char *fmt, struct tm *tm)
781{
782 char *ret;
783
784#ifdef _THREAD_SAFE
785pthread_mutex_lock(&gotgmt_mutex);
786#endif
787
788 got_GMT = 0;
789 ret = _strptime(buf, fmt, tm);
790
791#ifdef _THREAD_SAFE
792 pthread_mutex_unlock(&gotgmt_mutex);
793#endif
794
795 return ret;
796}
797
798#endif /* Mac OS X */
799
800MODULE = Time::Piece PACKAGE = Time::Piece
801
802PROTOTYPES: ENABLE
803
9331e88f 804void
16433e2b 805_strftime(fmt, sec, min, hour, mday, mon, year, wday = -1, yday = -1, isdst = -1)
806 char * fmt
807 int sec
808 int min
809 int hour
810 int mday
811 int mon
812 int year
813 int wday
814 int yday
815 int isdst
816 CODE:
817 {
818 char tmpbuf[128];
819 struct tm mytm;
820 int len;
821 memset(&mytm, 0, sizeof(mytm));
822 my_init_tm(&mytm); /* XXX workaround - see my_init_tm() above */
823 mytm.tm_sec = sec;
824 mytm.tm_min = min;
825 mytm.tm_hour = hour;
826 mytm.tm_mday = mday;
827 mytm.tm_mon = mon;
828 mytm.tm_year = year;
829 mytm.tm_wday = wday;
830 mytm.tm_yday = yday;
831 mytm.tm_isdst = isdst;
832 my_mini_mktime(&mytm);
833 len = strftime(tmpbuf, sizeof tmpbuf, fmt, &mytm);
834 /*
835 ** The following is needed to handle to the situation where
836 ** tmpbuf overflows. Basically we want to allocate a buffer
837 ** and try repeatedly. The reason why it is so complicated
838 ** is that getting a return value of 0 from strftime can indicate
839 ** one of the following:
840 ** 1. buffer overflowed,
841 ** 2. illegal conversion specifier, or
842 ** 3. the format string specifies nothing to be returned(not
843 ** an error). This could be because format is an empty string
844 ** or it specifies %p that yields an empty string in some locale.
845 ** If there is a better way to make it portable, go ahead by
846 ** all means.
847 */
848 if ((len > 0 && len < sizeof(tmpbuf)) || (len == 0 && *fmt == '\0'))
849 ST(0) = sv_2mortal(newSVpv(tmpbuf, len));
850 else {
851 /* Possibly buf overflowed - try again with a bigger buf */
852 int fmtlen = strlen(fmt);
853 int bufsize = fmtlen + sizeof(tmpbuf);
854 char* buf;
855 int buflen;
856
857 New(0, buf, bufsize, char);
858 while (buf) {
859 buflen = strftime(buf, bufsize, fmt, &mytm);
860 if (buflen > 0 && buflen < bufsize)
861 break;
862 /* heuristic to prevent out-of-memory errors */
863 if (bufsize > 100*fmtlen) {
864 Safefree(buf);
865 buf = NULL;
866 break;
867 }
868 bufsize *= 2;
869 Renew(buf, bufsize, char);
870 }
871 if (buf) {
872 ST(0) = sv_2mortal(newSVpv(buf, buflen));
873 Safefree(buf);
874 }
875 else
876 ST(0) = sv_2mortal(newSVpv(tmpbuf, len));
877 }
878 }
879
880void
881_tzset()
882 PPCODE:
883 tzset();
884
885
886void
887_strptime ( string, format )
888 char * string
889 char * format
890 PREINIT:
16433e2b 891 struct tm mytm;
892 time_t t;
893 char * remainder;
16433e2b 894 PPCODE:
895 t = 0;
896 mytm = *gmtime(&t);
897
898 remainder = (char *)strptime(string, format, &mytm);
899
900 if (remainder == NULL) {
901 croak("Error parsing time");
902 }
903
904 if (*remainder != '\0') {
905 warn("garbage at end of string in strptime: %s", remainder);
906 }
907
908 my_mini_mktime(&mytm);
909
910 /* 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); */
911
912 EXTEND(SP, 11);
913 PUSHs(sv_2mortal(newSViv(mytm.tm_sec)));
914 PUSHs(sv_2mortal(newSViv(mytm.tm_min)));
915 PUSHs(sv_2mortal(newSViv(mytm.tm_hour)));
916 PUSHs(sv_2mortal(newSViv(mytm.tm_mday)));
917 PUSHs(sv_2mortal(newSViv(mytm.tm_mon)));
918 PUSHs(sv_2mortal(newSViv(mytm.tm_year)));
919 PUSHs(sv_2mortal(newSViv(mytm.tm_wday)));
920 PUSHs(sv_2mortal(newSViv(mytm.tm_yday)));
921 /* isdst */
922 PUSHs(sv_2mortal(newSViv(0)));
923 /* epoch */
924 PUSHs(sv_2mortal(newSViv(0)));
925 /* islocal */
926 PUSHs(sv_2mortal(newSViv(0)));
3df1a9e2 927
928void
929_mini_mktime(int sec, int min, int hour, int mday, int mon, int year)
930 PREINIT:
931 struct tm mytm;
932 time_t t;
933 PPCODE:
934 t = 0;
935 mytm = *gmtime(&t);
936
937 mytm.tm_sec = sec;
938 mytm.tm_min = min;
939 mytm.tm_hour = hour;
940 mytm.tm_mday = mday;
941 mytm.tm_mon = mon;
942 mytm.tm_year = year;
943
944 my_mini_mktime(&mytm);
945
946 EXTEND(SP, 11);
947 PUSHs(sv_2mortal(newSViv(mytm.tm_sec)));
948 PUSHs(sv_2mortal(newSViv(mytm.tm_min)));
949 PUSHs(sv_2mortal(newSViv(mytm.tm_hour)));
950 PUSHs(sv_2mortal(newSViv(mytm.tm_mday)));
951 PUSHs(sv_2mortal(newSViv(mytm.tm_mon)));
952 PUSHs(sv_2mortal(newSViv(mytm.tm_year)));
953 PUSHs(sv_2mortal(newSViv(mytm.tm_wday)));
954 PUSHs(sv_2mortal(newSViv(mytm.tm_yday)));
955 /* isdst */
956 PUSHs(sv_2mortal(newSViv(0)));
957 /* epoch */
958 PUSHs(sv_2mortal(newSViv(0)));
959 /* islocal */
960 PUSHs(sv_2mortal(newSViv(0)));