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