This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
amigaos4: flock unimplemented
[perl5.git] / cpan / Time-Piece / Piece.xs
CommitLineData
16433e2b 1#ifdef __cplusplus
c944940b 2extern "C" {
16433e2b 3#endif
d93e3b8c 4#define PERL_NO_GET_CONTEXT
16433e2b
SP
5#include "EXTERN.h"
6#include "perl.h"
7#include "XSUB.h"
8#include <time.h>
9#ifdef __cplusplus
10}
11#endif
12
13/* XXX struct tm on some systems (SunOS4/BSD) contains extra (non POSIX)
d4d72fe6 14 * fields for which we don't have Configure support prior to Perl 5.8.0:
16433e2b
SP
15 * char *tm_zone; -- abbreviation of timezone name
16 * long tm_gmtoff; -- offset from GMT in seconds
17 * To workaround core dumps from the uninitialised tm_zone we get the
18 * system to give us a reasonable struct to copy. This fix means that
19 * strftime uses the tm_zone and tm_gmtoff values returned by
20 * localtime(time()). That should give the desired result most of the
21 * time. But probably not always!
22 *
d4d72fe6
CB
23 * This is a vestigial workaround for Perls prior to 5.8.0. We now
24 * rely on the initialization (still likely a workaround) in util.c.
16433e2b 25 */
d4d72fe6
CB
26#if !defined(PERL_VERSION) || PERL_VERSION < 8
27
036055ae 28#if defined(HAS_GNULIBC)
16433e2b
SP
29# ifndef STRUCT_TM_HASZONE
30# define STRUCT_TM_HASZONE
31# else
32# define USE_TM_GMTOFF
33# endif
34#endif
35
d4d72fe6
CB
36#endif /* end of pre-5.8 */
37
16433e2b
SP
38#define DAYS_PER_YEAR 365
39#define DAYS_PER_QYEAR (4*DAYS_PER_YEAR+1)
40#define DAYS_PER_CENT (25*DAYS_PER_QYEAR-1)
41#define DAYS_PER_QCENT (4*DAYS_PER_CENT+1)
42#define SECS_PER_HOUR (60*60)
43#define SECS_PER_DAY (24*SECS_PER_HOUR)
44/* parentheses deliberately absent on these two, otherwise they don't work */
45#define MONTH_TO_DAYS 153/5
46#define DAYS_TO_MONTH 5/153
47/* offset to bias by March (month 4) 1st between month/mday & year finding */
48#define YEAR_ADJUST (4*MONTH_TO_DAYS+1)
49/* as used here, the algorithm leaves Sunday as day 1 unless we adjust it */
50#define WEEKDAY_BIAS 6 /* (1+6)%7 makes Sunday 0 again */
51
d4d72fe6
CB
52#if !defined(PERL_VERSION) || PERL_VERSION < 8
53
16433e2b
SP
54#ifdef STRUCT_TM_HASZONE
55static void
56my_init_tm(struct tm *ptm) /* see mktime, strftime and asctime */
57{
58 Time_t now;
59 (void)time(&now);
60 Copy(localtime(&now), ptm, 1, struct tm);
61}
62
63#else
64# define my_init_tm(ptm)
65#endif
66
d4d72fe6
CB
67#else
68/* use core version from util.c in 5.8.0 and later */
69# define my_init_tm init_tm
12bbe8b8 70#endif
d4d72fe6 71
6e073399
SH
72#ifdef WIN32
73
74/*
75 * (1) The CRT maintains its own copy of the environment, separate from
76 * the Win32API copy.
77 *
78 * (2) CRT getenv() retrieves from this copy. CRT putenv() updates this
79 * copy, and then calls SetEnvironmentVariableA() to update the Win32API
80 * copy.
81 *
82 * (3) win32_getenv() and win32_putenv() call GetEnvironmentVariableA() and
83 * SetEnvironmentVariableA() directly, bypassing the CRT copy of the
84 * environment.
85 *
86 * (4) The CRT strftime() "%Z" implementation calls __tzset(). That
87 * calls CRT tzset(), but only the first time it is called, and in turn
88 * that uses CRT getenv("TZ") to retrieve the timezone info from the CRT
89 * local copy of the environment and hence gets the original setting as
90 * perl never updates the CRT copy when assigning to $ENV{TZ}.
91 *
92 * Therefore, we need to retrieve the value of $ENV{TZ} and call CRT
12016aad
SH
93 * putenv() to update the CRT copy of the environment (if it is different)
94 * whenever we're about to call tzset().
95 *
96 * In addition to all that, when perl is built with PERL_IMPLICIT_SYS
97 * defined:
98 *
99 * (a) Each interpreter has its own copy of the environment inside the
100 * perlhost structure. That allows applications that host multiple
101 * independent Perl interpreters to isolate environment changes from
102 * each other. (This is similar to how the perlhost mechanism keeps a
103 * separate working directory for each Perl interpreter, so that calling
104 * chdir() will not affect other interpreters.)
105 *
106 * (b) Only the first Perl interpreter instantiated within a process will
107 * "write through" environment changes to the process environment.
108 *
109 * (c) Even the primary Perl interpreter won't update the CRT copy of the
110 * the environment, only the Win32API copy (it calls win32_putenv()).
111 *
112 * As with CPerlHost::Getenv() and CPerlHost::Putenv() themselves, it makes
113 * sense to only update the process environment when inside the main
114 * interpreter, but we don't have access to CPerlHost's m_bTopLevel member
115 * from here so we'll just have to check PL_curinterp instead.
116 *
117 * Therefore, we can simply #undef getenv() and putenv() so that those names
118 * always refer to the CRT functions, and explicitly call win32_getenv() to
119 * access perl's %ENV.
120 *
121 * We also #undef malloc() and free() to be sure we are using the CRT
122 * functions otherwise under PERL_IMPLICIT_SYS they are redefined to calls
123 * into VMem::Malloc() and VMem::Free() and all allocations will be freed
124 * when the Perl interpreter is being destroyed so we'd end up with a pointer
125 * into deallocated memory in environ[] if a program embedding a Perl
126 * interpreter continues to operate even after the main Perl interpreter has
127 * been destroyed.
128 *
129 * Note that we don't free() the malloc()ed memory unless and until we call
130 * malloc() again ourselves because the CRT putenv() function simply puts its
131 * pointer argument into the environ[] arrary (it doesn't make a copy of it)
132 * so this memory must otherwise be leaked.
6e073399
SH
133 */
134
6e073399 135#undef getenv
6e073399 136#undef putenv
d93e3b8c
CBW
137# ifdef UNDER_CE
138# define getenv xcegetenv
139# define putenv xceputenv
140# endif
12016aad
SH
141#undef malloc
142#undef free
6e073399
SH
143
144static void
145fix_win32_tzenv(void)
146{
12016aad
SH
147 static char* oldenv = NULL;
148 char* newenv;
149 const char* perl_tz_env = win32_getenv("TZ");
150 const char* crt_tz_env = getenv("TZ");
151 if (perl_tz_env == NULL)
152 perl_tz_env = "";
153 if (crt_tz_env == NULL)
154 crt_tz_env = "";
155 if (strcmp(perl_tz_env, crt_tz_env) != 0) {
156 newenv = (char*)malloc((strlen(perl_tz_env) + 4) * sizeof(char));
157 if (newenv != NULL) {
158 sprintf(newenv, "TZ=%s", perl_tz_env);
159 putenv(newenv);
160 if (oldenv != NULL)
161 free(oldenv);
162 oldenv = newenv;
163 }
6e073399 164 }
6e073399
SH
165}
166
167#endif
168
81ab4c44
SH
169/*
170 * my_tzset - wrapper to tzset() with a fix to make it work (better) on Win32.
171 * This code is duplicated in the POSIX module, so any changes made here
172 * should be made there too.
173 */
6e073399 174static void
12016aad 175my_tzset(pTHX)
6e073399
SH
176{
177#ifdef WIN32
12016aad
SH
178#if defined(USE_ITHREADS) && defined(PERL_IMPLICIT_SYS)
179 if (PL_curinterp == aTHX)
180#endif
181 fix_win32_tzenv();
6e073399
SH
182#endif
183 tzset();
184}
185
16433e2b
SP
186/*
187 * my_mini_mktime - normalise struct tm values without the localtime()
124e6c84
RGS
188 * semantics (and overhead) of mktime(). Stolen shamelessly from Perl's
189 * Perl_mini_mktime() in util.c - for details on the algorithm, see that
190 * file.
16433e2b
SP
191 */
192static void
193my_mini_mktime(struct tm *ptm)
194{
195 int yearday;
196 int secs;
197 int month, mday, year, jday;
198 int odd_cent, odd_year;
199
16433e2b
SP
200 year = 1900 + ptm->tm_year;
201 month = ptm->tm_mon;
202 mday = ptm->tm_mday;
203 /* allow given yday with no month & mday to dominate the result */
204 if (ptm->tm_yday >= 0 && mday <= 0 && month <= 0) {
205 month = 0;
206 mday = 0;
207 jday = 1 + ptm->tm_yday;
208 }
209 else {
210 jday = 0;
211 }
212 if (month >= 2)
213 month+=2;
214 else
215 month+=14, year--;
216
217 yearday = DAYS_PER_YEAR * year + year/4 - year/100 + year/400;
218 yearday += month*MONTH_TO_DAYS + mday + jday;
219 /*
220 * Note that we don't know when leap-seconds were or will be,
221 * so we have to trust the user if we get something which looks
222 * like a sensible leap-second. Wild values for seconds will
223 * be rationalised, however.
224 */
225 if ((unsigned) ptm->tm_sec <= 60) {
226 secs = 0;
227 }
228 else {
229 secs = ptm->tm_sec;
230 ptm->tm_sec = 0;
231 }
232 secs += 60 * ptm->tm_min;
233 secs += SECS_PER_HOUR * ptm->tm_hour;
234 if (secs < 0) {
235 if (secs-(secs/SECS_PER_DAY*SECS_PER_DAY) < 0) {
236 /* got negative remainder, but need positive time */
237 /* back off an extra day to compensate */
238 yearday += (secs/SECS_PER_DAY)-1;
239 secs -= SECS_PER_DAY * (secs/SECS_PER_DAY - 1);
240 }
241 else {
242 yearday += (secs/SECS_PER_DAY);
243 secs -= SECS_PER_DAY * (secs/SECS_PER_DAY);
244 }
245 }
246 else if (secs >= SECS_PER_DAY) {
247 yearday += (secs/SECS_PER_DAY);
248 secs %= SECS_PER_DAY;
249 }
250 ptm->tm_hour = secs/SECS_PER_HOUR;
251 secs %= SECS_PER_HOUR;
252 ptm->tm_min = secs/60;
253 secs %= 60;
254 ptm->tm_sec += secs;
255 /* done with time of day effects */
256 /*
257 * The algorithm for yearday has (so far) left it high by 428.
258 * To avoid mistaking a legitimate Feb 29 as Mar 1, we need to
259 * bias it by 123 while trying to figure out what year it
260 * really represents. Even with this tweak, the reverse
261 * translation fails for years before A.D. 0001.
262 * It would still fail for Feb 29, but we catch that one below.
263 */
264 jday = yearday; /* save for later fixup vis-a-vis Jan 1 */
265 yearday -= YEAR_ADJUST;
266 year = (yearday / DAYS_PER_QCENT) * 400;
267 yearday %= DAYS_PER_QCENT;
268 odd_cent = yearday / DAYS_PER_CENT;
269 year += odd_cent * 100;
270 yearday %= DAYS_PER_CENT;
271 year += (yearday / DAYS_PER_QYEAR) * 4;
272 yearday %= DAYS_PER_QYEAR;
273 odd_year = yearday / DAYS_PER_YEAR;
274 year += odd_year;
275 yearday %= DAYS_PER_YEAR;
276 if (!yearday && (odd_cent==4 || odd_year==4)) { /* catch Feb 29 */
277 month = 1;
278 yearday = 29;
279 }
280 else {
281 yearday += YEAR_ADJUST; /* recover March 1st crock */
282 month = yearday*DAYS_TO_MONTH;
283 yearday -= month*MONTH_TO_DAYS;
284 /* recover other leap-year adjustment */
285 if (month > 13) {
286 month-=14;
287 year++;
288 }
289 else {
290 month-=2;
291 }
292 }
293 ptm->tm_year = year - 1900;
294 if (yearday) {
295 ptm->tm_mday = yearday;
296 ptm->tm_mon = month;
297 }
298 else {
299 ptm->tm_mday = 31;
300 ptm->tm_mon = month - 1;
301 }
302 /* re-build yearday based on Jan 1 to get tm_yday */
303 year--;
304 yearday = year*DAYS_PER_YEAR + year/4 - year/100 + year/400;
305 yearday += 14*MONTH_TO_DAYS + 1;
306 ptm->tm_yday = jday - yearday;
307 /* fix tm_wday if not overridden by caller */
308 ptm->tm_wday = (jday + WEEKDAY_BIAS) % 7;
309}
310
12016aad 311# if defined(WIN32) || (defined(__QNX__) && defined(__WATCOMC__))
90d55c29 312# define strncasecmp(x,y,n) strnicmp(x,y,n)
12016aad 313# endif
be8a15fc 314
12bbe8b8 315/* strptime.c 0.1 (Powerdog) 94/03/27 */
124e6c84
RGS
316/* strptime copied from freebsd with the following copyright: */
317/*
318 * Copyright (c) 1994 Powerdog Industries. All rights reserved.
319 *
320 * Redistribution and use in source and binary forms, with or without
321 * modification, are permitted provided that the following conditions
322 * are met:
12bbe8b8 323 *
124e6c84
RGS
324 * 1. Redistributions of source code must retain the above copyright
325 * notice, this list of conditions and the following disclaimer.
12bbe8b8 326 *
124e6c84
RGS
327 * 2. Redistributions in binary form must reproduce the above copyright
328 * notice, this list of conditions and the following disclaimer
329 * in the documentation and/or other materials provided with the
330 * distribution.
124e6c84
RGS
331 *
332 * THIS SOFTWARE IS PROVIDED BY POWERDOG INDUSTRIES ``AS IS'' AND ANY
333 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
334 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
335 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE POWERDOG INDUSTRIES BE
336 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
337 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
338 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
339 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
340 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
341 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
342 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
12bbe8b8
CBW
343 *
344 * The views and conclusions contained in the software and documentation
345 * are those of the authors and should not be interpreted as representing
346 * official policies, either expressed or implied, of Powerdog Industries.
124e6c84 347 */
be8a15fc 348
16433e2b
SP
349#include <time.h>
350#include <ctype.h>
351#include <string.h>
e9f284c9
MB
352static char * _strptime(pTHX_ const char *, const char *, struct tm *,
353 int *got_GMT);
16433e2b
SP
354
355#define asizeof(a) (sizeof (a) / sizeof ((a)[0]))
356
357struct lc_time_T {
358 const char * mon[12];
359 const char * month[12];
360 const char * wday[7];
361 const char * weekday[7];
12bbe8b8 362 const char * X_fmt;
16433e2b
SP
363 const char * x_fmt;
364 const char * c_fmt;
365 const char * am;
366 const char * pm;
367 const char * date_fmt;
368 const char * alt_month[12];
369 const char * Ef_fmt;
370 const char * EF_fmt;
371};
372
373struct lc_time_T _time_localebuf;
374int _time_using_locale;
375
376const struct lc_time_T _C_time_locale = {
377 {
378 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
379 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
380 }, {
381 "January", "February", "March", "April", "May", "June",
382 "July", "August", "September", "October", "November", "December"
383 }, {
384 "Sun", "Mon", "Tue", "Wed",
385 "Thu", "Fri", "Sat"
386 }, {
387 "Sunday", "Monday", "Tuesday", "Wednesday",
388 "Thursday", "Friday", "Saturday"
389 },
390
391 /* X_fmt */
392 "%H:%M:%S",
393
394 /*
395 ** x_fmt
396 ** Since the C language standard calls for
397 ** "date, using locale's date format," anything goes.
398 ** Using just numbers (as here) makes Quakers happier;
399 ** it's also compatible with SVR4.
400 */
401 "%m/%d/%y",
402
403 /*
404 ** c_fmt (ctime-compatible)
405 ** Not used, just compatibility placeholder.
406 */
407 NULL,
408
409 /* am */
410 "AM",
411
412 /* pm */
413 "PM",
414
415 /* date_fmt */
416 "%a %Ef %X %Z %Y",
417
418 {
419 "January", "February", "March", "April", "May", "June",
420 "July", "August", "September", "October", "November", "December"
421 },
422
423 /* Ef_fmt
424 ** To determine short months / day order
425 */
426 "%b %e",
427
428 /* EF_fmt
429 ** To determine long months / day order
430 */
431 "%B %e"
432};
433
434#define Locale (&_C_time_locale)
435
436static char *
e9f284c9 437_strptime(pTHX_ const char *buf, const char *fmt, struct tm *tm, int *got_GMT)
16433e2b
SP
438{
439 char c;
440 const char *ptr;
441 int i,
442 len;
443 int Ealternative, Oalternative;
444
90d55c29
CBW
445 /* There seems to be a slightly improved version at
446 * http://www.opensource.apple.com/source/Libc/Libc-583/stdtime/strptime-fbsd.c
447 * which we may end up borrowing more from
448 */
16433e2b
SP
449 ptr = fmt;
450 while (*ptr != 0) {
451 if (*buf == 0)
452 break;
453
454 c = *ptr++;
90d55c29 455
16433e2b
SP
456 if (c != '%') {
457 if (isspace((unsigned char)c))
458 while (*buf != 0 && isspace((unsigned char)*buf))
459 buf++;
460 else if (c != *buf++)
461 return 0;
462 continue;
463 }
464
465 Ealternative = 0;
466 Oalternative = 0;
467label:
468 c = *ptr++;
469 switch (c) {
470 case 0:
471 case '%':
472 if (*buf++ != '%')
473 return 0;
474 break;
475
476 case '+':
e9f284c9 477 buf = _strptime(aTHX_ buf, Locale->date_fmt, tm, got_GMT);
16433e2b
SP
478 if (buf == 0)
479 return 0;
480 break;
481
482 case 'C':
483 if (!isdigit((unsigned char)*buf))
484 return 0;
485
486 /* XXX This will break for 3-digit centuries. */
487 len = 2;
488 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
489 i *= 10;
490 i += *buf - '0';
491 len--;
492 }
493 if (i < 19)
494 return 0;
495
496 tm->tm_year = i * 100 - 1900;
497 break;
498
499 case 'c':
500 /* NOTE: c_fmt is intentionally ignored */
e9f284c9 501 buf = _strptime(aTHX_ buf, "%a %Ef %T %Y", tm, got_GMT);
16433e2b
SP
502 if (buf == 0)
503 return 0;
504 break;
505
506 case 'D':
e9f284c9 507 buf = _strptime(aTHX_ buf, "%m/%d/%y", tm, got_GMT);
16433e2b
SP
508 if (buf == 0)
509 return 0;
510 break;
511
512 case 'E':
513 if (Ealternative || Oalternative)
514 break;
515 Ealternative++;
516 goto label;
517
518 case 'O':
519 if (Ealternative || Oalternative)
520 break;
521 Oalternative++;
522 goto label;
523
524 case 'F':
525 case 'f':
526 if (!Ealternative)
527 break;
e9f284c9 528 buf = _strptime(aTHX_ buf, (c == 'f') ? Locale->Ef_fmt : Locale->EF_fmt, tm, got_GMT);
16433e2b
SP
529 if (buf == 0)
530 return 0;
531 break;
532
533 case 'R':
e9f284c9 534 buf = _strptime(aTHX_ buf, "%H:%M", tm, got_GMT);
16433e2b
SP
535 if (buf == 0)
536 return 0;
537 break;
538
539 case 'r':
e9f284c9 540 buf = _strptime(aTHX_ buf, "%I:%M:%S %p", tm, got_GMT);
16433e2b
SP
541 if (buf == 0)
542 return 0;
543 break;
544
90d55c29
CBW
545 case 'n': /* whitespace */
546 case 't':
547 if (!isspace((unsigned char)*buf))
548 return 0;
549 while (isspace((unsigned char)*buf))
550 buf++;
551 break;
552
16433e2b 553 case 'T':
e9f284c9 554 buf = _strptime(aTHX_ buf, "%H:%M:%S", tm, got_GMT);
16433e2b
SP
555 if (buf == 0)
556 return 0;
557 break;
558
559 case 'X':
e9f284c9 560 buf = _strptime(aTHX_ buf, Locale->X_fmt, tm, got_GMT);
16433e2b
SP
561 if (buf == 0)
562 return 0;
563 break;
564
565 case 'x':
e9f284c9 566 buf = _strptime(aTHX_ buf, Locale->x_fmt, tm, got_GMT);
16433e2b
SP
567 if (buf == 0)
568 return 0;
569 break;
570
571 case 'j':
572 if (!isdigit((unsigned char)*buf))
573 return 0;
574
575 len = 3;
576 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
577 i *= 10;
578 i += *buf - '0';
579 len--;
580 }
581 if (i < 1 || i > 366)
582 return 0;
583
584 tm->tm_yday = i - 1;
9bc7f50b 585 tm->tm_mday = 0;
16433e2b
SP
586 break;
587
588 case 'M':
589 case 'S':
590 if (*buf == 0 || isspace((unsigned char)*buf))
591 break;
592
593 if (!isdigit((unsigned char)*buf))
594 return 0;
595
596 len = 2;
597 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
598 i *= 10;
599 i += *buf - '0';
600 len--;
601 }
602
603 if (c == 'M') {
604 if (i > 59)
605 return 0;
606 tm->tm_min = i;
607 } else {
608 if (i > 60)
609 return 0;
610 tm->tm_sec = i;
611 }
612
613 if (*buf != 0 && isspace((unsigned char)*buf))
614 while (*ptr != 0 && !isspace((unsigned char)*ptr))
615 ptr++;
616 break;
617
618 case 'H':
619 case 'I':
620 case 'k':
621 case 'l':
622 /*
623 * Of these, %l is the only specifier explicitly
624 * documented as not being zero-padded. However,
625 * there is no harm in allowing zero-padding.
626 *
627 * XXX The %l specifier may gobble one too many
628 * digits if used incorrectly.
629 */
90d55c29 630 if (!isdigit((unsigned char)*buf))
16433e2b
SP
631 return 0;
632
633 len = 2;
634 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
635 i *= 10;
636 i += *buf - '0';
637 len--;
638 }
639 if (c == 'H' || c == 'k') {
640 if (i > 23)
641 return 0;
642 } else if (i > 12)
643 return 0;
644
645 tm->tm_hour = i;
646
647 if (*buf != 0 && isspace((unsigned char)*buf))
648 while (*ptr != 0 && !isspace((unsigned char)*ptr))
649 ptr++;
650 break;
651
652 case 'p':
653 /*
654 * XXX This is bogus if parsed before hour-related
655 * specifiers.
656 */
90d55c29 657 len = strlen(Locale->am);
16433e2b
SP
658 if (strncasecmp(buf, Locale->am, len) == 0) {
659 if (tm->tm_hour > 12)
660 return 0;
661 if (tm->tm_hour == 12)
662 tm->tm_hour = 0;
663 buf += len;
664 break;
665 }
666
667 len = strlen(Locale->pm);
668 if (strncasecmp(buf, Locale->pm, len) == 0) {
669 if (tm->tm_hour > 12)
670 return 0;
671 if (tm->tm_hour != 12)
672 tm->tm_hour += 12;
673 buf += len;
674 break;
675 }
676
677 return 0;
678
679 case 'A':
680 case 'a':
12bbe8b8 681 for (i = 0; i < (int)asizeof(Locale->weekday); i++) {
16433e2b
SP
682 if (c == 'A') {
683 len = strlen(Locale->weekday[i]);
684 if (strncasecmp(buf,
685 Locale->weekday[i],
686 len) == 0)
687 break;
688 } else {
689 len = strlen(Locale->wday[i]);
690 if (strncasecmp(buf,
691 Locale->wday[i],
692 len) == 0)
693 break;
694 }
695 }
12bbe8b8 696 if (i == (int)asizeof(Locale->weekday))
16433e2b
SP
697 return 0;
698
699 tm->tm_wday = i;
700 buf += len;
701 break;
702
703 case 'U':
704 case 'W':
705 /*
706 * XXX This is bogus, as we can not assume any valid
707 * information present in the tm structure at this
708 * point to calculate a real value, so just check the
709 * range for now.
710 */
90d55c29 711 if (!isdigit((unsigned char)*buf))
16433e2b
SP
712 return 0;
713
714 len = 2;
715 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
716 i *= 10;
717 i += *buf - '0';
718 len--;
719 }
720 if (i > 53)
721 return 0;
722
723 if (*buf != 0 && isspace((unsigned char)*buf))
724 while (*ptr != 0 && !isspace((unsigned char)*ptr))
725 ptr++;
726 break;
727
728 case 'w':
729 if (!isdigit((unsigned char)*buf))
730 return 0;
731
732 i = *buf - '0';
733 if (i > 6)
734 return 0;
735
736 tm->tm_wday = 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 'd':
744 case 'e':
745 /*
746 * The %e specifier is explicitly documented as not
747 * being zero-padded but there is no harm in allowing
748 * such padding.
749 *
750 * XXX The %e specifier may gobble one too many
751 * digits if used incorrectly.
752 */
753 if (!isdigit((unsigned char)*buf))
754 return 0;
755
756 len = 2;
757 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
758 i *= 10;
759 i += *buf - '0';
760 len--;
761 }
762 if (i > 31)
763 return 0;
764
765 tm->tm_mday = i;
766
767 if (*buf != 0 && isspace((unsigned char)*buf))
768 while (*ptr != 0 && !isspace((unsigned char)*ptr))
769 ptr++;
770 break;
771
772 case 'B':
773 case 'b':
774 case 'h':
12bbe8b8 775 for (i = 0; i < (int)asizeof(Locale->month); i++) {
16433e2b
SP
776 if (Oalternative) {
777 if (c == 'B') {
778 len = strlen(Locale->alt_month[i]);
779 if (strncasecmp(buf,
780 Locale->alt_month[i],
781 len) == 0)
782 break;
783 }
784 } else {
785 if (c == 'B') {
786 len = strlen(Locale->month[i]);
787 if (strncasecmp(buf,
788 Locale->month[i],
789 len) == 0)
790 break;
791 } else {
792 len = strlen(Locale->mon[i]);
793 if (strncasecmp(buf,
794 Locale->mon[i],
795 len) == 0)
796 break;
797 }
798 }
799 }
12bbe8b8 800 if (i == (int)asizeof(Locale->month))
16433e2b
SP
801 return 0;
802
803 tm->tm_mon = i;
804 buf += len;
805 break;
806
807 case 'm':
808 if (!isdigit((unsigned char)*buf))
809 return 0;
810
811 len = 2;
812 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
813 i *= 10;
814 i += *buf - '0';
815 len--;
816 }
817 if (i < 1 || i > 12)
818 return 0;
819
820 tm->tm_mon = i - 1;
821
822 if (*buf != 0 && isspace((unsigned char)*buf))
823 while (*ptr != 0 && !isspace((unsigned char)*ptr))
824 ptr++;
825 break;
826
90d55c29
CBW
827 case 's':
828 {
829 char *cp;
830 int sverrno;
831 long n;
832 time_t t;
833 struct tm mytm;
834
835 sverrno = errno;
836 errno = 0;
837 n = strtol(buf, &cp, 10);
838 if (errno == ERANGE || (long)(t = n) != n) {
839 errno = sverrno;
840 return 0;
841 }
842 errno = sverrno;
843 buf = cp;
844 memset(&mytm, 0, sizeof(mytm));
845 my_init_tm(&mytm); /* XXX workaround - see my_init_tm() above */
846 mytm = *gmtime(&t);
847 tm->tm_sec = mytm.tm_sec;
848 tm->tm_min = mytm.tm_min;
849 tm->tm_hour = mytm.tm_hour;
850 tm->tm_mday = mytm.tm_mday;
851 tm->tm_mon = mytm.tm_mon;
852 tm->tm_year = mytm.tm_year;
853 tm->tm_wday = mytm.tm_wday;
854 tm->tm_yday = mytm.tm_yday;
855 tm->tm_isdst = mytm.tm_isdst;
856 }
857 break;
858
16433e2b
SP
859 case 'Y':
860 case 'y':
861 if (*buf == 0 || isspace((unsigned char)*buf))
862 break;
863
864 if (!isdigit((unsigned char)*buf))
865 return 0;
866
867 len = (c == 'Y') ? 4 : 2;
868 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
869 i *= 10;
870 i += *buf - '0';
871 len--;
872 }
873 if (c == 'Y')
874 i -= 1900;
875 if (c == 'y' && i < 69)
876 i += 100;
877 if (i < 0)
878 return 0;
879
880 tm->tm_year = i;
881
882 if (*buf != 0 && isspace((unsigned char)*buf))
883 while (*ptr != 0 && !isspace((unsigned char)*ptr))
884 ptr++;
885 break;
886
887 case 'Z':
888 {
889 const char *cp;
890 char *zonestr;
891
12bbe8b8 892 for (cp = buf; *cp && isupper((unsigned char)*cp); ++cp)
16433e2b
SP
893 {/*empty*/}
894 if (cp - buf) {
8a0cff69 895 zonestr = (char *)malloc(cp - buf + 1);
ded2eedc
CB
896 if (!zonestr) {
897 errno = ENOMEM;
898 return 0;
899 }
16433e2b
SP
900 strncpy(zonestr, buf, cp - buf);
901 zonestr[cp - buf] = '\0';
12016aad 902 my_tzset(aTHX);
16433e2b 903 if (0 == strcmp(zonestr, "GMT")) {
e9f284c9 904 *got_GMT = 1;
16433e2b 905 }
ded2eedc 906 free(zonestr);
e9f284c9 907 if (!*got_GMT) return 0;
16433e2b
SP
908 buf += cp - buf;
909 }
910 }
911 break;
90d55c29
CBW
912
913 case 'z':
914 {
915 int sign = 1;
916
917 if (*buf != '+') {
918 if (*buf == '-')
919 sign = -1;
920 else
921 return 0;
922 }
923
924 buf++;
925 i = 0;
926 for (len = 4; len > 0; len--) {
927 if (isdigit((int)*buf)) {
928 i *= 10;
929 i += *buf - '0';
930 buf++;
931 } else
932 return 0;
933 }
934
935 tm->tm_hour -= sign * (i / 100);
936 tm->tm_min -= sign * (i % 100);
e9f284c9 937 *got_GMT = 1;
90d55c29
CBW
938 }
939 break;
16433e2b
SP
940 }
941 }
942 return (char *)buf;
943}
944
d93e3b8c
CBW
945/* Saves alot of machine code.
946 Takes a (auto) SP, which may or may not have been PUSHed before, puts
947 tm struct members on Perl stack, then returns new, advanced, SP to caller.
948 Assign the return of push_common_tm to your SP, so you can continue to PUSH
949 or do a PUTBACK and return eventually.
950 !!!! push_common_tm does not touch PL_stack_sp !!!!
951 !!!! do not use PUTBACK then SPAGAIN semantics around push_common_tm !!!!
952 !!!! You must mortalize whatever push_common_tm put on stack yourself to
953 avoid leaking !!!!
954*/
955SV **
956push_common_tm(pTHX_ SV ** SP, struct tm *mytm)
957{
958 PUSHs(newSViv(mytm->tm_sec));
959 PUSHs(newSViv(mytm->tm_min));
960 PUSHs(newSViv(mytm->tm_hour));
961 PUSHs(newSViv(mytm->tm_mday));
962 PUSHs(newSViv(mytm->tm_mon));
963 PUSHs(newSViv(mytm->tm_year));
964 PUSHs(newSViv(mytm->tm_wday));
965 PUSHs(newSViv(mytm->tm_yday));
12bbe8b8 966 PUSHs(newSViv(mytm->tm_isdst));
d93e3b8c
CBW
967 return SP;
968}
969
970/* specialized common end of 2 XSUBs
971 SV ** SP -- pass your (auto) SP, which has not been PUSHed before, but was
972 reset to 0 (PPCODE only or SP -= items or XSprePUSH)
973 tm *mytm -- a tm *, will be proprocessed with my_mini_mktime
974 return -- none, after calling return_11part_tm, you must call "return;"
975 no exceptions
976*/
977void
978return_11part_tm(pTHX_ SV ** SP, struct tm *mytm)
979{
980 my_mini_mktime(mytm);
981
982 /* 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); */
d93e3b8c
CBW
983 EXTEND(SP, 11);
984 SP = push_common_tm(aTHX_ SP, mytm);
d93e3b8c
CBW
985 /* epoch */
986 PUSHs(newSViv(0));
987 /* islocal */
988 PUSHs(newSViv(0));
989 PUTBACK;
990 {
991 SV ** endsp = SP; /* the SV * under SP needs to be mortaled */
992 SP -= (11 - 1); /* subtract 0 based count of SVs to mortal */
993/* mortal target of SP, then increment before function call
994 so SP is already calculated before next comparison to not stall CPU */
995 do {
996 sv_2mortal(*SP++);
997 } while(SP <= endsp);
998 }
999 return;
1000}
1001
16433e2b
SP
1002MODULE = Time::Piece PACKAGE = Time::Piece
1003
1004PROTOTYPES: ENABLE
1005
9331e88f 1006void
12bbe8b8
CBW
1007_strftime(fmt, epoch, islocal = 1)
1008 char * fmt
1009 time_t epoch
1010 int islocal
16433e2b
SP
1011 CODE:
1012 {
1013 char tmpbuf[128];
1014 struct tm mytm;
1015 int len;
12bbe8b8
CBW
1016
1017 if(islocal == 1)
1018 mytm = *localtime(&epoch);
1019 else
1020 mytm = *gmtime(&epoch);
1021
16433e2b
SP
1022 len = strftime(tmpbuf, sizeof tmpbuf, fmt, &mytm);
1023 /*
12bbe8b8 1024 ** The following is needed to handle to the situation where
16433e2b
SP
1025 ** tmpbuf overflows. Basically we want to allocate a buffer
1026 ** and try repeatedly. The reason why it is so complicated
1027 ** is that getting a return value of 0 from strftime can indicate
1028 ** one of the following:
1029 ** 1. buffer overflowed,
1030 ** 2. illegal conversion specifier, or
1031 ** 3. the format string specifies nothing to be returned(not
1032 ** an error). This could be because format is an empty string
1033 ** or it specifies %p that yields an empty string in some locale.
1034 ** If there is a better way to make it portable, go ahead by
1035 ** all means.
1036 */
1037 if ((len > 0 && len < sizeof(tmpbuf)) || (len == 0 && *fmt == '\0'))
1038 ST(0) = sv_2mortal(newSVpv(tmpbuf, len));
1039 else {
1040 /* Possibly buf overflowed - try again with a bigger buf */
1041 int fmtlen = strlen(fmt);
1042 int bufsize = fmtlen + sizeof(tmpbuf);
1043 char* buf;
1044 int buflen;
1045
1046 New(0, buf, bufsize, char);
1047 while (buf) {
1048 buflen = strftime(buf, bufsize, fmt, &mytm);
1049 if (buflen > 0 && buflen < bufsize)
1050 break;
1051 /* heuristic to prevent out-of-memory errors */
1052 if (bufsize > 100*fmtlen) {
1053 Safefree(buf);
1054 buf = NULL;
1055 break;
1056 }
1057 bufsize *= 2;
1058 Renew(buf, bufsize, char);
1059 }
1060 if (buf) {
1061 ST(0) = sv_2mortal(newSVpv(buf, buflen));
1062 Safefree(buf);
1063 }
1064 else
1065 ST(0) = sv_2mortal(newSVpv(tmpbuf, len));
1066 }
1067 }
1068
1069void
1070_tzset()
1071 PPCODE:
d93e3b8c 1072 PUTBACK; /* makes rest of this function tailcall friendly */
12016aad 1073 my_tzset(aTHX);
d93e3b8c 1074 return; /* skip XSUBPP's PUTBACK */
16433e2b
SP
1075
1076void
1077_strptime ( string, format )
1078 char * string
1079 char * format
1080 PREINIT:
16433e2b
SP
1081 struct tm mytm;
1082 time_t t;
1083 char * remainder;
933a2256 1084 int got_GMT;
16433e2b
SP
1085 PPCODE:
1086 t = 0;
1087 mytm = *gmtime(&t);
12bbe8b8 1088 mytm.tm_isdst = -1; /* -1 means we don't know */
933a2256
CBW
1089 got_GMT = 0;
1090
1091 remainder = (char *)_strptime(aTHX_ string, format, &mytm, &got_GMT);
16433e2b 1092 if (remainder == NULL) {
90d55c29 1093 croak("Error parsing time");
16433e2b 1094 }
16433e2b
SP
1095 if (*remainder != '\0') {
1096 warn("garbage at end of string in strptime: %s", remainder);
1097 }
16433e2b 1098
d93e3b8c
CBW
1099 return_11part_tm(aTHX_ SP, &mytm);
1100 return;
3df1a9e2
GA
1101
1102void
1103_mini_mktime(int sec, int min, int hour, int mday, int mon, int year)
1104 PREINIT:
1105 struct tm mytm;
1106 time_t t;
1107 PPCODE:
1108 t = 0;
1109 mytm = *gmtime(&t);
1110
1111 mytm.tm_sec = sec;
1112 mytm.tm_min = min;
1113 mytm.tm_hour = hour;
1114 mytm.tm_mday = mday;
1115 mytm.tm_mon = mon;
1116 mytm.tm_year = year;
3df1a9e2 1117
d93e3b8c
CBW
1118 return_11part_tm(aTHX_ SP, &mytm);
1119 return;
90d55c29
CBW
1120
1121void
1122_crt_localtime(time_t sec)
d93e3b8c
CBW
1123 ALIAS:
1124 _crt_gmtime = 1
90d55c29
CBW
1125 PREINIT:
1126 struct tm mytm;
1127 PPCODE:
d93e3b8c
CBW
1128 if(ix) mytm = *gmtime(&sec);
1129 else mytm = *localtime(&sec);
90d55c29 1130 /* Need to get: $s,$n,$h,$d,$m,$y */
12bbe8b8 1131
90d55c29 1132 EXTEND(SP, 9);
d93e3b8c
CBW
1133 SP = push_common_tm(aTHX_ SP, &mytm);
1134 PUSHs(newSViv(mytm.tm_isdst));
1135 PUTBACK;
1136 {
1137 SV ** endsp = SP; /* the SV * under SP needs to be mortaled */
1138 SP -= (9 - 1); /* subtract 0 based count of SVs to mortal */
1139/* mortal target of SP, then increment before function call
1140 so SP is already calculated before next comparison to not stall CPU */
1141 do {
1142 sv_2mortal(*SP++);
1143 } while(SP <= endsp);
1144 }
1145 return;