This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Update Socket to CPAN version 2.019
[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
70#endif
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
124e6c84
RGS
315/* strptime copied from freebsd with the following copyright: */
316/*
317 * Copyright (c) 1994 Powerdog Industries. All rights reserved.
318 *
319 * Redistribution and use in source and binary forms, with or without
320 * modification, are permitted provided that the following conditions
321 * are met:
322 * 1. Redistributions of source code must retain the above copyright
323 * notice, this list of conditions and the following disclaimer.
324 * 2. Redistributions in binary form must reproduce the above copyright
325 * notice, this list of conditions and the following disclaimer
326 * in the documentation and/or other materials provided with the
327 * distribution.
328 * 3. All advertising materials mentioning features or use of this
329 * software must display the following acknowledgement:
330 * This product includes software developed by Powerdog Industries.
331 * 4. The name of Powerdog Industries may not be used to endorse or
332 * promote products derived from this software without specific prior
333 * written permission.
334 *
335 * THIS SOFTWARE IS PROVIDED BY POWERDOG INDUSTRIES ``AS IS'' AND ANY
336 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
337 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
338 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE POWERDOG INDUSTRIES BE
339 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
340 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
341 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
342 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
343 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
344 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
345 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
346 */
347
348#ifndef lint
349#ifndef NOID
350static char copyright[] =
351"@(#) Copyright (c) 1994 Powerdog Industries. All rights reserved.";
352static char sccsid[] = "@(#)strptime.c 0.1 (Powerdog) 94/03/27";
353#endif /* !defined NOID */
354#endif /* not lint */
be8a15fc 355
16433e2b
SP
356#include <time.h>
357#include <ctype.h>
358#include <string.h>
e9f284c9
MB
359static char * _strptime(pTHX_ const char *, const char *, struct tm *,
360 int *got_GMT);
16433e2b
SP
361
362#define asizeof(a) (sizeof (a) / sizeof ((a)[0]))
363
364struct lc_time_T {
365 const char * mon[12];
366 const char * month[12];
367 const char * wday[7];
368 const char * weekday[7];
369 const char * X_fmt;
370 const char * x_fmt;
371 const char * c_fmt;
372 const char * am;
373 const char * pm;
374 const char * date_fmt;
375 const char * alt_month[12];
376 const char * Ef_fmt;
377 const char * EF_fmt;
378};
379
380struct lc_time_T _time_localebuf;
381int _time_using_locale;
382
383const struct lc_time_T _C_time_locale = {
384 {
385 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
386 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
387 }, {
388 "January", "February", "March", "April", "May", "June",
389 "July", "August", "September", "October", "November", "December"
390 }, {
391 "Sun", "Mon", "Tue", "Wed",
392 "Thu", "Fri", "Sat"
393 }, {
394 "Sunday", "Monday", "Tuesday", "Wednesday",
395 "Thursday", "Friday", "Saturday"
396 },
397
398 /* X_fmt */
399 "%H:%M:%S",
400
401 /*
402 ** x_fmt
403 ** Since the C language standard calls for
404 ** "date, using locale's date format," anything goes.
405 ** Using just numbers (as here) makes Quakers happier;
406 ** it's also compatible with SVR4.
407 */
408 "%m/%d/%y",
409
410 /*
411 ** c_fmt (ctime-compatible)
412 ** Not used, just compatibility placeholder.
413 */
414 NULL,
415
416 /* am */
417 "AM",
418
419 /* pm */
420 "PM",
421
422 /* date_fmt */
423 "%a %Ef %X %Z %Y",
424
425 {
426 "January", "February", "March", "April", "May", "June",
427 "July", "August", "September", "October", "November", "December"
428 },
429
430 /* Ef_fmt
431 ** To determine short months / day order
432 */
433 "%b %e",
434
435 /* EF_fmt
436 ** To determine long months / day order
437 */
438 "%B %e"
439};
440
441#define Locale (&_C_time_locale)
442
443static char *
e9f284c9 444_strptime(pTHX_ const char *buf, const char *fmt, struct tm *tm, int *got_GMT)
16433e2b
SP
445{
446 char c;
447 const char *ptr;
448 int i,
449 len;
450 int Ealternative, Oalternative;
451
90d55c29
CBW
452 /* There seems to be a slightly improved version at
453 * http://www.opensource.apple.com/source/Libc/Libc-583/stdtime/strptime-fbsd.c
454 * which we may end up borrowing more from
455 */
16433e2b
SP
456 ptr = fmt;
457 while (*ptr != 0) {
458 if (*buf == 0)
459 break;
460
461 c = *ptr++;
90d55c29 462
16433e2b
SP
463 if (c != '%') {
464 if (isspace((unsigned char)c))
465 while (*buf != 0 && isspace((unsigned char)*buf))
466 buf++;
467 else if (c != *buf++)
468 return 0;
469 continue;
470 }
471
472 Ealternative = 0;
473 Oalternative = 0;
474label:
475 c = *ptr++;
476 switch (c) {
477 case 0:
478 case '%':
479 if (*buf++ != '%')
480 return 0;
481 break;
482
483 case '+':
e9f284c9 484 buf = _strptime(aTHX_ buf, Locale->date_fmt, tm, got_GMT);
16433e2b
SP
485 if (buf == 0)
486 return 0;
487 break;
488
489 case 'C':
490 if (!isdigit((unsigned char)*buf))
491 return 0;
492
493 /* XXX This will break for 3-digit centuries. */
494 len = 2;
495 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
496 i *= 10;
497 i += *buf - '0';
498 len--;
499 }
500 if (i < 19)
501 return 0;
502
503 tm->tm_year = i * 100 - 1900;
504 break;
505
506 case 'c':
507 /* NOTE: c_fmt is intentionally ignored */
e9f284c9 508 buf = _strptime(aTHX_ buf, "%a %Ef %T %Y", tm, got_GMT);
16433e2b
SP
509 if (buf == 0)
510 return 0;
511 break;
512
513 case 'D':
e9f284c9 514 buf = _strptime(aTHX_ buf, "%m/%d/%y", tm, got_GMT);
16433e2b
SP
515 if (buf == 0)
516 return 0;
517 break;
518
519 case 'E':
520 if (Ealternative || Oalternative)
521 break;
522 Ealternative++;
523 goto label;
524
525 case 'O':
526 if (Ealternative || Oalternative)
527 break;
528 Oalternative++;
529 goto label;
530
531 case 'F':
532 case 'f':
533 if (!Ealternative)
534 break;
e9f284c9 535 buf = _strptime(aTHX_ buf, (c == 'f') ? Locale->Ef_fmt : Locale->EF_fmt, tm, got_GMT);
16433e2b
SP
536 if (buf == 0)
537 return 0;
538 break;
539
540 case 'R':
e9f284c9 541 buf = _strptime(aTHX_ buf, "%H:%M", tm, got_GMT);
16433e2b
SP
542 if (buf == 0)
543 return 0;
544 break;
545
546 case 'r':
e9f284c9 547 buf = _strptime(aTHX_ buf, "%I:%M:%S %p", tm, got_GMT);
16433e2b
SP
548 if (buf == 0)
549 return 0;
550 break;
551
90d55c29
CBW
552 case 'n': /* whitespace */
553 case 't':
554 if (!isspace((unsigned char)*buf))
555 return 0;
556 while (isspace((unsigned char)*buf))
557 buf++;
558 break;
559
16433e2b 560 case 'T':
e9f284c9 561 buf = _strptime(aTHX_ buf, "%H:%M:%S", tm, got_GMT);
16433e2b
SP
562 if (buf == 0)
563 return 0;
564 break;
565
566 case 'X':
e9f284c9 567 buf = _strptime(aTHX_ buf, Locale->X_fmt, tm, got_GMT);
16433e2b
SP
568 if (buf == 0)
569 return 0;
570 break;
571
572 case 'x':
e9f284c9 573 buf = _strptime(aTHX_ buf, Locale->x_fmt, tm, got_GMT);
16433e2b
SP
574 if (buf == 0)
575 return 0;
576 break;
577
578 case 'j':
579 if (!isdigit((unsigned char)*buf))
580 return 0;
581
582 len = 3;
583 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
584 i *= 10;
585 i += *buf - '0';
586 len--;
587 }
588 if (i < 1 || i > 366)
589 return 0;
590
591 tm->tm_yday = i - 1;
9bc7f50b 592 tm->tm_mday = 0;
16433e2b
SP
593 break;
594
595 case 'M':
596 case 'S':
597 if (*buf == 0 || isspace((unsigned char)*buf))
598 break;
599
600 if (!isdigit((unsigned char)*buf))
601 return 0;
602
603 len = 2;
604 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
605 i *= 10;
606 i += *buf - '0';
607 len--;
608 }
609
610 if (c == 'M') {
611 if (i > 59)
612 return 0;
613 tm->tm_min = i;
614 } else {
615 if (i > 60)
616 return 0;
617 tm->tm_sec = i;
618 }
619
620 if (*buf != 0 && isspace((unsigned char)*buf))
621 while (*ptr != 0 && !isspace((unsigned char)*ptr))
622 ptr++;
623 break;
624
625 case 'H':
626 case 'I':
627 case 'k':
628 case 'l':
629 /*
630 * Of these, %l is the only specifier explicitly
631 * documented as not being zero-padded. However,
632 * there is no harm in allowing zero-padding.
633 *
634 * XXX The %l specifier may gobble one too many
635 * digits if used incorrectly.
636 */
90d55c29 637 if (!isdigit((unsigned char)*buf))
16433e2b
SP
638 return 0;
639
640 len = 2;
641 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
642 i *= 10;
643 i += *buf - '0';
644 len--;
645 }
646 if (c == 'H' || c == 'k') {
647 if (i > 23)
648 return 0;
649 } else if (i > 12)
650 return 0;
651
652 tm->tm_hour = 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 'p':
660 /*
661 * XXX This is bogus if parsed before hour-related
662 * specifiers.
663 */
90d55c29 664 len = strlen(Locale->am);
16433e2b
SP
665 if (strncasecmp(buf, Locale->am, len) == 0) {
666 if (tm->tm_hour > 12)
667 return 0;
668 if (tm->tm_hour == 12)
669 tm->tm_hour = 0;
670 buf += len;
671 break;
672 }
673
674 len = strlen(Locale->pm);
675 if (strncasecmp(buf, Locale->pm, len) == 0) {
676 if (tm->tm_hour > 12)
677 return 0;
678 if (tm->tm_hour != 12)
679 tm->tm_hour += 12;
680 buf += len;
681 break;
682 }
683
684 return 0;
685
686 case 'A':
687 case 'a':
688 for (i = 0; i < asizeof(Locale->weekday); i++) {
689 if (c == 'A') {
690 len = strlen(Locale->weekday[i]);
691 if (strncasecmp(buf,
692 Locale->weekday[i],
693 len) == 0)
694 break;
695 } else {
696 len = strlen(Locale->wday[i]);
697 if (strncasecmp(buf,
698 Locale->wday[i],
699 len) == 0)
700 break;
701 }
702 }
703 if (i == asizeof(Locale->weekday))
704 return 0;
705
706 tm->tm_wday = i;
707 buf += len;
708 break;
709
710 case 'U':
711 case 'W':
712 /*
713 * XXX This is bogus, as we can not assume any valid
714 * information present in the tm structure at this
715 * point to calculate a real value, so just check the
716 * range for now.
717 */
90d55c29 718 if (!isdigit((unsigned char)*buf))
16433e2b
SP
719 return 0;
720
721 len = 2;
722 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
723 i *= 10;
724 i += *buf - '0';
725 len--;
726 }
727 if (i > 53)
728 return 0;
729
730 if (*buf != 0 && isspace((unsigned char)*buf))
731 while (*ptr != 0 && !isspace((unsigned char)*ptr))
732 ptr++;
733 break;
734
735 case 'w':
736 if (!isdigit((unsigned char)*buf))
737 return 0;
738
739 i = *buf - '0';
740 if (i > 6)
741 return 0;
742
743 tm->tm_wday = i;
744
745 if (*buf != 0 && isspace((unsigned char)*buf))
746 while (*ptr != 0 && !isspace((unsigned char)*ptr))
747 ptr++;
748 break;
749
750 case 'd':
751 case 'e':
752 /*
753 * The %e specifier is explicitly documented as not
754 * being zero-padded but there is no harm in allowing
755 * such padding.
756 *
757 * XXX The %e specifier may gobble one too many
758 * digits if used incorrectly.
759 */
760 if (!isdigit((unsigned char)*buf))
761 return 0;
762
763 len = 2;
764 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
765 i *= 10;
766 i += *buf - '0';
767 len--;
768 }
769 if (i > 31)
770 return 0;
771
772 tm->tm_mday = i;
773
774 if (*buf != 0 && isspace((unsigned char)*buf))
775 while (*ptr != 0 && !isspace((unsigned char)*ptr))
776 ptr++;
777 break;
778
779 case 'B':
780 case 'b':
781 case 'h':
782 for (i = 0; i < asizeof(Locale->month); i++) {
783 if (Oalternative) {
784 if (c == 'B') {
785 len = strlen(Locale->alt_month[i]);
786 if (strncasecmp(buf,
787 Locale->alt_month[i],
788 len) == 0)
789 break;
790 }
791 } else {
792 if (c == 'B') {
793 len = strlen(Locale->month[i]);
794 if (strncasecmp(buf,
795 Locale->month[i],
796 len) == 0)
797 break;
798 } else {
799 len = strlen(Locale->mon[i]);
800 if (strncasecmp(buf,
801 Locale->mon[i],
802 len) == 0)
803 break;
804 }
805 }
806 }
807 if (i == asizeof(Locale->month))
808 return 0;
809
810 tm->tm_mon = i;
811 buf += len;
812 break;
813
814 case 'm':
815 if (!isdigit((unsigned char)*buf))
816 return 0;
817
818 len = 2;
819 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
820 i *= 10;
821 i += *buf - '0';
822 len--;
823 }
824 if (i < 1 || i > 12)
825 return 0;
826
827 tm->tm_mon = i - 1;
828
829 if (*buf != 0 && isspace((unsigned char)*buf))
830 while (*ptr != 0 && !isspace((unsigned char)*ptr))
831 ptr++;
832 break;
833
90d55c29
CBW
834 case 's':
835 {
836 char *cp;
837 int sverrno;
838 long n;
839 time_t t;
840 struct tm mytm;
841
842 sverrno = errno;
843 errno = 0;
844 n = strtol(buf, &cp, 10);
845 if (errno == ERANGE || (long)(t = n) != n) {
846 errno = sverrno;
847 return 0;
848 }
849 errno = sverrno;
850 buf = cp;
851 memset(&mytm, 0, sizeof(mytm));
852 my_init_tm(&mytm); /* XXX workaround - see my_init_tm() above */
853 mytm = *gmtime(&t);
854 tm->tm_sec = mytm.tm_sec;
855 tm->tm_min = mytm.tm_min;
856 tm->tm_hour = mytm.tm_hour;
857 tm->tm_mday = mytm.tm_mday;
858 tm->tm_mon = mytm.tm_mon;
859 tm->tm_year = mytm.tm_year;
860 tm->tm_wday = mytm.tm_wday;
861 tm->tm_yday = mytm.tm_yday;
862 tm->tm_isdst = mytm.tm_isdst;
863 }
864 break;
865
16433e2b
SP
866 case 'Y':
867 case 'y':
868 if (*buf == 0 || isspace((unsigned char)*buf))
869 break;
870
871 if (!isdigit((unsigned char)*buf))
872 return 0;
873
874 len = (c == 'Y') ? 4 : 2;
875 for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
876 i *= 10;
877 i += *buf - '0';
878 len--;
879 }
880 if (c == 'Y')
881 i -= 1900;
882 if (c == 'y' && i < 69)
883 i += 100;
884 if (i < 0)
885 return 0;
886
887 tm->tm_year = i;
888
889 if (*buf != 0 && isspace((unsigned char)*buf))
890 while (*ptr != 0 && !isspace((unsigned char)*ptr))
891 ptr++;
892 break;
893
894 case 'Z':
895 {
896 const char *cp;
897 char *zonestr;
898
899 for (cp = buf; *cp && isupper((unsigned char)*cp); ++cp)
900 {/*empty*/}
901 if (cp - buf) {
8a0cff69 902 zonestr = (char *)malloc(cp - buf + 1);
ded2eedc
CB
903 if (!zonestr) {
904 errno = ENOMEM;
905 return 0;
906 }
16433e2b
SP
907 strncpy(zonestr, buf, cp - buf);
908 zonestr[cp - buf] = '\0';
12016aad 909 my_tzset(aTHX);
16433e2b 910 if (0 == strcmp(zonestr, "GMT")) {
e9f284c9 911 *got_GMT = 1;
16433e2b 912 }
ded2eedc 913 free(zonestr);
e9f284c9 914 if (!*got_GMT) return 0;
16433e2b
SP
915 buf += cp - buf;
916 }
917 }
918 break;
90d55c29
CBW
919
920 case 'z':
921 {
922 int sign = 1;
923
924 if (*buf != '+') {
925 if (*buf == '-')
926 sign = -1;
927 else
928 return 0;
929 }
930
931 buf++;
932 i = 0;
933 for (len = 4; len > 0; len--) {
934 if (isdigit((int)*buf)) {
935 i *= 10;
936 i += *buf - '0';
937 buf++;
938 } else
939 return 0;
940 }
941
942 tm->tm_hour -= sign * (i / 100);
943 tm->tm_min -= sign * (i % 100);
e9f284c9 944 *got_GMT = 1;
90d55c29
CBW
945 }
946 break;
16433e2b
SP
947 }
948 }
949 return (char *)buf;
950}
951
d93e3b8c
CBW
952/* Saves alot of machine code.
953 Takes a (auto) SP, which may or may not have been PUSHed before, puts
954 tm struct members on Perl stack, then returns new, advanced, SP to caller.
955 Assign the return of push_common_tm to your SP, so you can continue to PUSH
956 or do a PUTBACK and return eventually.
957 !!!! push_common_tm does not touch PL_stack_sp !!!!
958 !!!! do not use PUTBACK then SPAGAIN semantics around push_common_tm !!!!
959 !!!! You must mortalize whatever push_common_tm put on stack yourself to
960 avoid leaking !!!!
961*/
962SV **
963push_common_tm(pTHX_ SV ** SP, struct tm *mytm)
964{
965 PUSHs(newSViv(mytm->tm_sec));
966 PUSHs(newSViv(mytm->tm_min));
967 PUSHs(newSViv(mytm->tm_hour));
968 PUSHs(newSViv(mytm->tm_mday));
969 PUSHs(newSViv(mytm->tm_mon));
970 PUSHs(newSViv(mytm->tm_year));
971 PUSHs(newSViv(mytm->tm_wday));
972 PUSHs(newSViv(mytm->tm_yday));
973 return SP;
974}
975
976/* specialized common end of 2 XSUBs
977 SV ** SP -- pass your (auto) SP, which has not been PUSHed before, but was
978 reset to 0 (PPCODE only or SP -= items or XSprePUSH)
979 tm *mytm -- a tm *, will be proprocessed with my_mini_mktime
980 return -- none, after calling return_11part_tm, you must call "return;"
981 no exceptions
982*/
983void
984return_11part_tm(pTHX_ SV ** SP, struct tm *mytm)
985{
986 my_mini_mktime(mytm);
987
988 /* 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); */
989
990 EXTEND(SP, 11);
991 SP = push_common_tm(aTHX_ SP, mytm);
992 /* isdst */
993 PUSHs(newSViv(0));
994 /* epoch */
995 PUSHs(newSViv(0));
996 /* islocal */
997 PUSHs(newSViv(0));
998 PUTBACK;
999 {
1000 SV ** endsp = SP; /* the SV * under SP needs to be mortaled */
1001 SP -= (11 - 1); /* subtract 0 based count of SVs to mortal */
1002/* mortal target of SP, then increment before function call
1003 so SP is already calculated before next comparison to not stall CPU */
1004 do {
1005 sv_2mortal(*SP++);
1006 } while(SP <= endsp);
1007 }
1008 return;
1009}
1010
16433e2b
SP
1011MODULE = Time::Piece PACKAGE = Time::Piece
1012
1013PROTOTYPES: ENABLE
1014
9331e88f 1015void
16433e2b
SP
1016_strftime(fmt, sec, min, hour, mday, mon, year, wday = -1, yday = -1, isdst = -1)
1017 char * fmt
1018 int sec
1019 int min
1020 int hour
1021 int mday
1022 int mon
1023 int year
1024 int wday
1025 int yday
1026 int isdst
1027 CODE:
1028 {
1029 char tmpbuf[128];
1030 struct tm mytm;
1031 int len;
1032 memset(&mytm, 0, sizeof(mytm));
1033 my_init_tm(&mytm); /* XXX workaround - see my_init_tm() above */
1034 mytm.tm_sec = sec;
1035 mytm.tm_min = min;
1036 mytm.tm_hour = hour;
1037 mytm.tm_mday = mday;
1038 mytm.tm_mon = mon;
1039 mytm.tm_year = year;
1040 mytm.tm_wday = wday;
1041 mytm.tm_yday = yday;
1042 mytm.tm_isdst = isdst;
1043 my_mini_mktime(&mytm);
1044 len = strftime(tmpbuf, sizeof tmpbuf, fmt, &mytm);
1045 /*
1046 ** The following is needed to handle to the situation where
1047 ** tmpbuf overflows. Basically we want to allocate a buffer
1048 ** and try repeatedly. The reason why it is so complicated
1049 ** is that getting a return value of 0 from strftime can indicate
1050 ** one of the following:
1051 ** 1. buffer overflowed,
1052 ** 2. illegal conversion specifier, or
1053 ** 3. the format string specifies nothing to be returned(not
1054 ** an error). This could be because format is an empty string
1055 ** or it specifies %p that yields an empty string in some locale.
1056 ** If there is a better way to make it portable, go ahead by
1057 ** all means.
1058 */
1059 if ((len > 0 && len < sizeof(tmpbuf)) || (len == 0 && *fmt == '\0'))
1060 ST(0) = sv_2mortal(newSVpv(tmpbuf, len));
1061 else {
1062 /* Possibly buf overflowed - try again with a bigger buf */
1063 int fmtlen = strlen(fmt);
1064 int bufsize = fmtlen + sizeof(tmpbuf);
1065 char* buf;
1066 int buflen;
1067
1068 New(0, buf, bufsize, char);
1069 while (buf) {
1070 buflen = strftime(buf, bufsize, fmt, &mytm);
1071 if (buflen > 0 && buflen < bufsize)
1072 break;
1073 /* heuristic to prevent out-of-memory errors */
1074 if (bufsize > 100*fmtlen) {
1075 Safefree(buf);
1076 buf = NULL;
1077 break;
1078 }
1079 bufsize *= 2;
1080 Renew(buf, bufsize, char);
1081 }
1082 if (buf) {
1083 ST(0) = sv_2mortal(newSVpv(buf, buflen));
1084 Safefree(buf);
1085 }
1086 else
1087 ST(0) = sv_2mortal(newSVpv(tmpbuf, len));
1088 }
1089 }
1090
1091void
1092_tzset()
1093 PPCODE:
d93e3b8c 1094 PUTBACK; /* makes rest of this function tailcall friendly */
12016aad 1095 my_tzset(aTHX);
d93e3b8c 1096 return; /* skip XSUBPP's PUTBACK */
16433e2b
SP
1097
1098void
1099_strptime ( string, format )
1100 char * string
1101 char * format
1102 PREINIT:
16433e2b
SP
1103 struct tm mytm;
1104 time_t t;
1105 char * remainder;
933a2256 1106 int got_GMT;
16433e2b
SP
1107 PPCODE:
1108 t = 0;
1109 mytm = *gmtime(&t);
933a2256
CBW
1110 got_GMT = 0;
1111
1112 remainder = (char *)_strptime(aTHX_ string, format, &mytm, &got_GMT);
16433e2b 1113 if (remainder == NULL) {
90d55c29 1114 croak("Error parsing time");
16433e2b 1115 }
16433e2b
SP
1116 if (*remainder != '\0') {
1117 warn("garbage at end of string in strptime: %s", remainder);
1118 }
16433e2b 1119
d93e3b8c
CBW
1120 return_11part_tm(aTHX_ SP, &mytm);
1121 return;
3df1a9e2
GA
1122
1123void
1124_mini_mktime(int sec, int min, int hour, int mday, int mon, int year)
1125 PREINIT:
1126 struct tm mytm;
1127 time_t t;
1128 PPCODE:
1129 t = 0;
1130 mytm = *gmtime(&t);
1131
1132 mytm.tm_sec = sec;
1133 mytm.tm_min = min;
1134 mytm.tm_hour = hour;
1135 mytm.tm_mday = mday;
1136 mytm.tm_mon = mon;
1137 mytm.tm_year = year;
3df1a9e2 1138
d93e3b8c
CBW
1139 return_11part_tm(aTHX_ SP, &mytm);
1140 return;
90d55c29
CBW
1141
1142void
1143_crt_localtime(time_t sec)
d93e3b8c
CBW
1144 ALIAS:
1145 _crt_gmtime = 1
90d55c29
CBW
1146 PREINIT:
1147 struct tm mytm;
1148 PPCODE:
d93e3b8c
CBW
1149 if(ix) mytm = *gmtime(&sec);
1150 else mytm = *localtime(&sec);
90d55c29
CBW
1151 /* Need to get: $s,$n,$h,$d,$m,$y */
1152
1153 EXTEND(SP, 9);
d93e3b8c
CBW
1154 SP = push_common_tm(aTHX_ SP, &mytm);
1155 PUSHs(newSViv(mytm.tm_isdst));
1156 PUTBACK;
1157 {
1158 SV ** endsp = SP; /* the SV * under SP needs to be mortaled */
1159 SP -= (9 - 1); /* subtract 0 based count of SVs to mortal */
1160/* mortal target of SP, then increment before function call
1161 so SP is already calculated before next comparison to not stall CPU */
1162 do {
1163 sv_2mortal(*SP++);
1164 } while(SP <= endsp);
1165 }
1166 return;