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