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