1 /* Copyright (C) 2002-2004 Manuel Novoa III <mjn3@codepoet.org>
3 * GNU Library General Public License (LGPL) version 2 or later.
5 * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details.
8 /* June 15, 2002 Initial Notes:
10 * Note: It is assumed throught that time_t is either long or unsigned long.
11 * Similarly, clock_t is assumed to be long int.
13 * Warning: Assumptions are made about the layout of struct tm! It is
14 * assumed that the initial fields of struct tm are (in order):
15 * tm_sec, tm_min, tm_hour, tm_mday, tm_mon, tm_year, tm_wday, tm_yday
17 * Reached the inital goal of supporting the ANSI/ISO C99 time functions
18 * as well as SUSv3's strptime. All timezone info is obtained from the
21 * Differences from glibc worth noting:
23 * Leap seconds are not considered here.
25 * glibc stores additional timezone info the struct tm, whereas we don't.
27 * Alternate digits and era handling are not currently implemented.
28 * The modifiers are accepted, and tested for validity with the following
29 * specifier, but are ignored otherwise.
31 * strftime does not implement glibc extension modifiers or widths for
32 * conversion specifiers. However it does implement the glibc
33 * extension specifiers %l, %k, and %s. It also recognizes %P, but
34 * treats it as a synonym for %p; i.e. doesn't convert to lower case.
36 * strptime implements the glibc extension specifiers. However, it follows
37 * SUSv3 in requiring at least one non-alphanumeric char between
38 * conversion specifiers. Also, strptime only sets struct tm fields
39 * for which format specifiers appear and does not try to infer other
40 * fields (such as wday) as glibc's version does.
42 * TODO - Since glibc's %l and %k can space-pad their output in strftime,
43 * it might be reasonable to eat whitespace first for those specifiers.
44 * This could be done by pushing " %I" and " %H" respectively so that
45 * leading whitespace is consumed. This is really only an issue if %l
46 * or %k occurs at the start of the format string.
48 * TODO - Implement getdate? tzfile? struct tm extensions?
50 * TODO - Rework _time_mktime to remove the dependency on long long.
55 * Fixed allowed char check for std and dst TZ fields.
57 * Added several options concerned with timezone support. The names will
58 * probably change once Erik gets the new config system in place.
60 * Defining __TIME_TZ_FILE causes tzset() to attempt to read the TZ value
61 * from the file /etc/TZ if the TZ env variable isn't set. The file contents
62 * must be the intended value of TZ, followed by a newline. No other chars,
63 * spacing, etc is allowed. As an example, an easy way for me to init
64 * /etc/TZ appropriately would be: echo CST6CDT > /etc/TZ
66 * Defining __TIME_TZ_FILE_ONCE will cause all further accesses of /etc/TZ
67 * to be skipped once a legal value has been read.
69 * Defining __TIME_TZ_OPT_SPEED will cause a tzset() to keep a copy of the
70 * last TZ setting string and do a "fast out" if the current string is the
73 * Nov 21, 2002 Fix an error return case in _time_mktime.
75 * Nov 26, 2002 Fix bug in setting daylight and timezone when no (valid) TZ.
76 * Bug reported by Arne Bernin <arne@alamut.de> in regards to freeswan.
78 * July 27, 2003 Adjust the struct tm extension field support.
79 * Change __tm_zone back to a ptr and add the __tm_tzname[] buffer for
80 * __tm_zone to point to. This gets around complaints from g++.
81 * Who knows... it might even fix the PPC timezone init problem.
83 * July 29, 2003 Fix a bug in mktime behavior when tm_isdst was -1.
84 * Bug reported by "Sid Wade" <sid@vivato.net> in regards to busybox.
86 * NOTE: uClibc mktime behavior is different than glibc's when
87 * the struct tm has tm_isdst == -1 and also had fields outside of
90 * Apparently, glibc examines (at least) tm_sec and guesses the app's
91 * intention of assuming increasing or decreasing time when entering an
92 * ambiguous time period at the dst<->st boundaries.
94 * The uClibc behavior is to always normalize the struct tm and then
95 * try to determing the dst setting.
97 * As long as tm_isdst != -1 or the time specifiec by struct tm is
98 * unambiguous (not falling in the dst<->st transition region) both
99 * uClibc and glibc should produce the same result for mktime.
101 * Oct 31, 2003 Kill the seperate __tm_zone and __tm_tzname[] and which
102 * doesn't work if you want the memcpy the struct. Sigh... I didn't
103 * think about that. So now, when the extensions are enabled, we
104 * malloc space when necessary and keep the timezone names in a linked
107 * Fix a dst-related bug which resulted in use of uninitialized data.
109 * Nov 15, 2003 I forgot to update the thread locking in the last dst fix.
111 * Dec 14, 2003 Fix some dst issues in _time_mktime().
112 * Normalize the tm_isdst value to -1, 0, or 1.
113 * If no dst for this timezone, then reset tm_isdst to 0.
116 * Change clock() to allow wrapping.
117 * Add timegm() function.
118 * Make lookup_tzname() static (as it should have been).
119 * Have strftime() get timezone information from the passed struct
120 * for the %z and %Z conversions when using struct tm extensions.
123 * Fix 2 bugs in strftime related to glibc struct tm extensions.
124 * 1) Need to negate tm_gmtoff field value when used. (bug 336).
125 * 2) Deal with NULL ptr case for tm_zone field, which was causing
126 * segfaults in both the NIST/PCTS tests and the Python 2.4.1
128 * NOTE: We set uninitialized timezone names to "???", and this
129 * differs (intentionally) from glibc's behavior.
137 #include <sys/time.h>
142 #include <langinfo.h>
146 #include <bits/uClibc_uintmaxtostr.h>
147 #include <bits/uClibc_mutex.h>
149 #if defined __UCLIBC_HAS_WCHAR__ && (defined L_wcsftime || defined L_wcsftime_l)
151 # define CHAR_T wchar_t
152 # define UCHAR_T unsigned int
154 # define strftime wcsftime
156 # if defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE)
157 # define strftime_l wcsftime_l
161 # define strftime_l wcsftime_l
162 # define L_strftime_l
166 # define UCHAR_T unsigned char
170 #define __isleap(y) ( !((y) % 4) && ( ((y) % 100) || !((y) % 400) ) )
174 #define TZNAME_MAX _POSIX_TZNAME_MAX
177 #if defined (L_tzset) || defined (L_localtime_r) || defined(L_strftime) || \
178 defined(L__time_mktime) || defined(L__time_mktime_tzi) || \
179 ((defined(L_strftime) || defined(L_strftime_l)) && \
180 defined(__UCLIBC_HAS_XLOCALE__))
182 void _time_tzset(int use_old_rules) attribute_hidden;
184 #ifndef L__time_mktime
186 /* Jan 1, 2007 Z - tm = 0,0,0,1,0,107,1,0,0 */
188 static const time_t new_rule_starts = 1167609600;
193 /**********************************************************************/
194 /* The era code is currently unfinished. */
195 /* #define ENABLE_ERA_CODE */
197 #define TZ_BUFLEN (2*TZNAME_MAX + 56)
199 #ifdef __UCLIBC_HAS_TZ_FILE__
201 #include <sys/stat.h>
203 /* ":<tzname>+hh:mm:ss<tzname>+hh:mm:ss,Mmm.w.d/hh:mm:ss,Mmm.w.d/hh:mm:ss" + nul */
204 /* 1 + 2*(1+TZNAME_MAX+1 + 9 + 7 + 9) + 1 = 2*TZNAME_MAX + 56 */
206 #else /* __UCLIBC_HAS_TZ_FILE__ */
208 /* Probably no longer needed. */
209 #undef __UCLIBC_HAS_TZ_FILE_READ_MANY__
211 #endif /* __UCLIBC_HAS_TZ_FILE__ */
213 /**********************************************************************/
215 extern struct tm __time_tm attribute_hidden;
220 short day; /* for J or normal */
223 short rule_type; /* J, M, \0 */
224 char tzname[TZNAME_MAX+1];
227 __UCLIBC_MUTEX_EXTERN(_time_tzlock) attribute_hidden;
229 extern rule_struct _time_tzinfo[2] attribute_hidden;
231 extern struct tm *_time_t2tm(const time_t *__restrict timer,
232 int offset, struct tm *__restrict result) attribute_hidden;
234 extern time_t _time_mktime(struct tm *timeptr, int store_on_success) attribute_hidden;
236 extern struct tm *__time_localtime_tzi(const time_t *__restrict timer,
237 struct tm *__restrict result,
238 rule_struct *tzi) attribute_hidden;
240 extern time_t _time_mktime_tzi(struct tm *timeptr, int store_on_success,
241 rule_struct *tzi) attribute_hidden;
243 /**********************************************************************/
246 static char __time_str[26];
248 char *asctime(const struct tm *ptm)
250 return asctime_r(ptm, __time_str);
252 libc_hidden_def(asctime)
255 /**********************************************************************/
258 /* Strictly speaking, this implementation isn't correct. ANSI/ISO specifies
259 * that the implementation of asctime() be equivalent to
261 * char *asctime(const struct tm *timeptr)
263 * static char wday_name[7][3] = {
264 * "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
266 * static char mon_name[12][3] = {
267 * "Jan", "Feb", "Mar", "Apr", "May", "Jun",
268 * "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
270 * static char result[26];
272 * sprintf(result, "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n",
273 * wday_name[timeptr->tm_wday],
274 * mon_name[timeptr->tm_mon],
275 * timeptr->tm_mday, timeptr->tm_hour,
276 * timeptr->tm_min, timeptr->tm_sec,
277 * 1900 + timeptr->tm_year);
281 * but the above is either inherently unsafe, or carries with it the implicit
282 * assumption that all fields of timeptr fall within their usual ranges, and
283 * that the tm_year value falls in the range [-2899,8099] to avoid overflowing
286 * If we take the implicit assumption as given, then the implementation below
287 * is still incorrect for tm_year values < -900, as there will be either
288 * 0-padding and/or a missing negative sign for the year conversion . But given
289 * the usual use of asctime(), I think it isn't unreasonable to restrict correct
290 * operation to the domain of years between 1000 and 9999.
293 /* This is generally a good thing, but if you're _sure_ any data passed will be
294 * in range, you can #undef this. */
295 #define SAFE_ASCTIME_R 1
297 static const unsigned char at_data[] = {
298 'S', 'u', 'n', 'M', 'o', 'n', 'T', 'u', 'e', 'W', 'e', 'd',
299 'T', 'h', 'u', 'F', 'r', 'i', 'S', 'a', 't',
301 'J', 'a', 'n', 'F', 'e', 'b', 'M', 'a', 'r', 'A', 'p', 'r',
302 'M', 'a', 'y', 'J', 'u', 'n', 'J', 'u', 'l', 'A', 'u', 'g',
303 'S', 'e', 'p', 'O', 'c', 't', 'N', 'o', 'v', 'D', 'e', 'c',
305 #ifdef SAFE_ASCTIME_R
310 offsetof(struct tm, tm_mday),
312 offsetof(struct tm, tm_hour),
314 offsetof(struct tm, tm_min),
316 offsetof(struct tm, tm_sec),
317 ' ', '?', '?', '?', '?', '\n', 0
320 char *asctime_r(register const struct tm *__restrict ptm,
321 register char *__restrict buffer)
328 #ifdef SAFE_ASCTIME_R
329 memcpy(buffer, at_data + 3*(7 + 12), sizeof(at_data) - 3*(7 + 12));
331 if (((unsigned int)(ptm->tm_wday)) <= 6) {
332 memcpy(buffer, at_data + 3 * ptm->tm_wday, 3);
335 if (((unsigned int)(ptm->tm_mon)) <= 11) {
336 memcpy(buffer + 4, at_data + 3*7 + 3 * ptm->tm_mon, 3);
339 assert(((unsigned int)(ptm->tm_wday)) <= 6);
340 assert(((unsigned int)(ptm->tm_mon)) <= 11);
342 memcpy(buffer, at_data + 3*(7 + 12) - 3, sizeof(at_data) + 3 - 3*(7 + 12));
344 memcpy(buffer, at_data + 3 * ptm->tm_wday, 3);
345 memcpy(buffer + 4, at_data + 3*7 + 3 * ptm->tm_mon, 3);
348 #ifdef SAFE_ASCTIME_R
350 tmp = ptm->tm_year + 1900;
351 if (((unsigned int) tmp) < 10000) {
354 *buffer = '0' + (tmp % 10);
356 } while (*--buffer == '?');
358 /* Not sure if we should even bother ...
360 __set_errno(EOVERFLOW);
364 #else /* SAFE_ASCTIME_R */
366 tmp = ptm->tm_year + 1900;
367 assert( ((unsigned int) tmp) < 10000 );
368 /* Not sure if we should even bother ...
369 if ( ((unsigned int) tmp) >= 10000 ) {
370 __set_errno(EOVERFLOW);
375 *buffer = '0' + (tmp % 10);
377 } while (*--buffer == '?');
378 #endif /* SAFE_ASCTIME_R */
382 tmp = *((int *)(((const char *) ptm) + (int) *buffer));
383 #ifdef SAFE_ASCTIME_R
384 if (((unsigned int) tmp) >= 100) { /* Just check 2 digit non-neg. */
385 buffer[-1] = *buffer = '?';
388 assert(((unsigned int) tmp) < 100); /* Just check 2 digit non-neg. */
391 *buffer = '0' + (tmp % 10);
393 buffer[-1] = '0' + (tmp/10);
395 buffer[-1] += (tmp/10);
398 } while ((buffer -= 2)[-2] == '0');
400 if (*++buffer == '0') { /* Space-pad day of month. */
406 libc_hidden_def(asctime_r)
409 /**********************************************************************/
412 #include <sys/times.h>
415 #if CLOCKS_PER_SEC != 1000000L
416 #error unexpected value for CLOCKS_PER_SEC!
420 #ifdef __UCLIBC_CLK_TCK_CONST
421 # if __UCLIBC_CLK_TCK_CONST > CLOCKS_PER_SEC
422 # error __UCLIBC_CLK_TCK_CONST > CLOCKS_PER_SEC!
423 # elif __UCLIBC_CLK_TCK_CONST < 1
424 # error __UCLIBC_CLK_TCK_CONST < 1!
430 * On XSI-conformant systems, CLOCKS_PER_SEC is defined to be one million.
432 * The value returned by clock() may wrap around on some implementations.
433 * For example, on a machine with 32-bit values for clock_t, it wraps
434 * after 2147 seconds.
436 * This implies that we should bitwise and with LONG_MAX.
446 t = ((unsigned long) xtms.tms_utime) + xtms.tms_stime;
448 #ifndef __UCLIBC_CLK_TCK_CONST
450 # error __UCLIBC_CLK_TCK_CONST not defined!
452 #elif ((CLOCKS_PER_SEC % __UCLIBC_CLK_TCK_CONST) == 0)
454 /* CLOCKS_PER_SEC == k * __UCLIBC_CLK_TCK_CONST for some integer k >= 1. */
455 return ((t * (CLOCKS_PER_SEC/__UCLIBC_CLK_TCK_CONST)) & LONG_MAX);
459 /* Unlike the previous case, the scaling factor is not an integer.
460 * So when tms_utime, tms_stime, or their sum wraps, some of the
461 * "visible" bits in the return value are affected. Nothing we
462 * can really do about this though other than handle tms_utime and
463 * tms_stime seperately and then sum. But since that doesn't really
464 * buy us much, we don't bother. */
466 return ((((t / __UCLIBC_CLK_TCK_CONST) * CLOCKS_PER_SEC)
467 + ((((t % __UCLIBC_CLK_TCK_CONST) * CLOCKS_PER_SEC)
468 / __UCLIBC_CLK_TCK_CONST))
475 /**********************************************************************/
478 char *ctime(const time_t *t)
480 /* ANSI/ISO/SUSv3 say that ctime is equivalent to the following:
481 * return asctime(localtime(t));
482 * I don't think "equivalent" means "it uses the same internal buffer",
483 * it means "gives the same resultant string".
485 * I doubt anyone ever uses weird code like:
486 * struct tm *ptm = localtime(t1); ...; ctime(t2); use(ptm);
487 * which relies on the assumption that ctime's and localtime's
488 * internal static struct tm is the same.
490 * Using localtime_r instead of localtime avoids linking in
491 * localtime's static buffer:
494 memset(&xtm, 0, sizeof(xtm));
496 return asctime(localtime_r(t, &xtm));
498 libc_hidden_def(ctime)
500 /**********************************************************************/
503 char *ctime_r(const time_t *t, char *buf)
507 return asctime_r(localtime_r(t, &xtm), buf);
511 /**********************************************************************/
517 #error difftime implementation assumptions violated for you arch!
520 double difftime(time_t time1, time_t time0)
522 #if (LONG_MAX >> DBL_MANT_DIG) == 0
524 /* time_t fits in the mantissa of a double. */
525 return (double)time1 - (double)time0;
527 #elif ((LONG_MAX >> DBL_MANT_DIG) >> DBL_MANT_DIG) == 0
529 /* time_t can overflow the mantissa of a double. */
532 d = ((time_t) 1) << DBL_MANT_DIG;
538 /* Since FLT_RADIX==2 and d is a power of 2, the only possible
539 * rounding error in the expression below would occur from the
541 return (((double) t1) - t0) * d + (((double) time1) - time0);
544 #error difftime needs special implementation on your arch.
549 /**********************************************************************/
552 struct tm *gmtime(const time_t *timer)
554 register struct tm *ptm = &__time_tm;
556 _time_t2tm(timer, 0, ptm); /* Can return NULL... */
562 /**********************************************************************/
565 struct tm *gmtime_r(const time_t *__restrict timer,
566 struct tm *__restrict result)
568 return _time_t2tm(timer, 0, result);
572 /**********************************************************************/
575 struct tm *localtime(const time_t *timer)
577 register struct tm *ptm = &__time_tm;
579 /* In this implementation, tzset() is called by localtime_r(). */
581 localtime_r(timer, ptm); /* Can return NULL... */
585 libc_hidden_def(localtime)
588 /**********************************************************************/
591 struct tm *localtime_r(register const time_t *__restrict timer,
592 register struct tm *__restrict result)
594 __UCLIBC_MUTEX_LOCK(_time_tzlock);
596 _time_tzset(*timer < new_rule_starts);
598 __time_localtime_tzi(timer, result, _time_tzinfo);
600 __UCLIBC_MUTEX_UNLOCK(_time_tzlock);
604 libc_hidden_def(localtime_r)
607 /**********************************************************************/
608 #ifdef L__time_localtime_tzi
610 #ifdef __UCLIBC_HAS_TM_EXTENSIONS__
612 struct ll_tzname_item;
614 typedef struct ll_tzname_item {
615 struct ll_tzname_item *next;
619 /* Structures form a list "UTC" -> "???" -> "tzname1" -> "tzname2"... */
621 struct ll_tzname_item *next;
623 } ll_tzname_UNKNOWN = { NULL, "???" };
624 static const struct {
625 struct ll_tzname_item *next;
627 } ll_tzname_UTC = { (void*)&ll_tzname_UNKNOWN, "UTC" };
629 static const char *lookup_tzname(const char *key)
632 ll_tzname_item_t *p = (void*) &ll_tzname_UTC;
635 if (strcmp(p->tzname, key) == 0)
640 /* Hmm... a new name. */
641 len = strnlen(key, TZNAME_MAX+1);
642 if (len < TZNAME_MAX+1) { /* Verify legal length */
643 p = malloc(sizeof(ll_tzname_item_t) + len);
645 /* Insert as 3rd item in the list. */
646 p->next = ll_tzname_UNKNOWN.next;
647 ll_tzname_UNKNOWN.next = p;
648 return strcpy(p->tzname, key);
652 /* Either invalid or couldn't alloc. */
653 return ll_tzname_UNKNOWN.tzname;
656 #endif /* __UCLIBC_HAS_TM_EXTENSIONS__ */
658 static const unsigned char day_cor[] = { /* non-leap */
659 31, 31, 34, 34, 35, 35, 36, 36, 36, 37, 37, 38, 38
660 /* 0, 0, 3, 3, 4, 4, 5, 5, 5, 6, 6, 7, 7 */
661 /* 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 */
664 /* Note: timezone locking is done by localtime_r. */
666 static int tm_isdst(register const struct tm *__restrict ptm,
667 register rule_struct *r)
670 int i, isdst, isleap, day, day0, monlen, mday;
671 int oday = oday; /* ok to be uninitialized, shutting up compiler warning */
674 if (r[1].tzname[0] != 0) {
675 /* First, get the current seconds offset from the start of the year.
676 * Fields of ptm are assumed to be in their normal ranges. */
679 + 60 * (long)(ptm->tm_hour
680 + 24 * ptm->tm_yday));
681 /* Do some prep work. */
682 i = (ptm->tm_year % 400) + 1900; /* Make sure we don't overflow. */
683 isleap = __isleap(i);
686 + i /* Normal years increment 1 wday. */
692 day = r->day; /* Common for 'J' and # case. */
693 if (r->rule_type == 'J') {
694 if (!isleap || (day < (31+29))) {
697 } else if (r->rule_type == 'M') {
698 /* Find 0-based day number for 1st of the month. */
699 day = 31 * r->month - day_cor[r->month - 1];
700 if (isleap && (day >= 59)) {
703 monlen = 31 + day_cor[r->month - 1] - day_cor[r->month];
704 if (isleap && (r->month == 2)) {
707 /* Weekday (0 is Sunday) of 1st of the month
708 * is (day0 + day) % 7. */
709 mday = r->day - ((day0 + day) % 7);
711 mday -= 7; /* Back up into prev month since r->week > 0. */
714 if (mday >= monlen) {
717 /* So, 0-based day number is... */
722 /* Adjust sec since dst->std change time is in dst. */
723 sec += (r[-1].gmt_offset - r->gmt_offset);
725 ++isdst; /* Year starts in dst. */
730 /* Now convert day to seconds and add offset and compare. */
731 if (sec >= (day * 86400L) + r->dst_offset) {
741 struct tm attribute_hidden *__time_localtime_tzi(register const time_t *__restrict timer,
742 register struct tm *__restrict result,
752 offset = 604800L - tzi[dst].gmt_offset;
753 if (*timer > (LONG_MAX - 604800L)) {
757 *x = *timer + offset;
759 _time_t2tm(x, days, result);
760 result->tm_isdst = dst;
761 #ifdef __UCLIBC_HAS_TM_EXTENSIONS__
763 result->tm_gmtoff = - tzi[dst].gmt_offset;
764 result->tm_zone = lookup_tzname(tzi[dst].tzname);
766 result->__tm_gmtoff = - tzi[dst].gmt_offset;
767 result->__tm_zone = lookup_tzname(tzi[dst].tzname);
769 #endif /* __UCLIBC_HAS_TM_EXTENSIONS__ */
771 && ((result->tm_isdst = tm_isdst(result, tzi)) != 0));
777 /**********************************************************************/
780 time_t mktime(struct tm *timeptr)
782 return _time_mktime(timeptr, 1);
785 /* Another name for `mktime'. */
786 /* time_t timelocal(struct tm *tp) */
787 strong_alias(mktime,timelocal)
790 /**********************************************************************/
792 /* Like `mktime' but timeptr represents Universal Time, not local time. */
794 time_t timegm(struct tm *timeptr)
796 rule_struct gmt_tzinfo[2];
798 memset(gmt_tzinfo, 0, sizeof(gmt_tzinfo));
799 strcpy(gmt_tzinfo[0].tzname, "GMT"); /* Match glibc behavior here. */
801 return _time_mktime_tzi(timeptr, 1, gmt_tzinfo);
805 /**********************************************************************/
806 #if defined(L_strftime) || defined(L_strftime_l) \
807 || defined(L_wcsftime) || defined(L_wcsftime_l)
809 #if defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE)
811 size_t strftime(CHAR_T *__restrict s, size_t maxsize,
812 const CHAR_T *__restrict format,
813 const struct tm *__restrict timeptr)
815 return strftime_l(s, maxsize, format, timeptr, __UCLIBC_CURLOCALE);
818 #else /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */
820 #define NO_E_MOD 0x80
821 #define NO_O_MOD 0x40
823 #define ILLEGAL_SPEC 0x3f
825 #define INT_SPEC 0x00 /* must be 0x00!! */
826 #define STRING_SPEC 0x10 /* must be 0x10!! */
827 #define CALC_SPEC 0x20
828 #define STACKED_SPEC 0x30
830 #define MASK_SPEC 0x30
834 * No alternate digit (%O?) handling. Always uses 0-9.
835 * Alternate locale format (%E?) handling is broken for nontrivial ERAs.
836 * glibc's %P is currently faked by %p. This means it doesn't do lower case.
837 * glibc's %k, %l, and %s are handled.
838 * glibc apparently allows (and ignores) extraneous 'E' and 'O' modifiers,
839 * while they are flagged as illegal conversions here.
842 /* Warning: Assumes ASCII values! (as do lots of other things in the lib...) */
843 static const unsigned char spec[] = {
844 /* A */ 0x03 | STRING_SPEC | NO_E_MOD | NO_O_MOD,
845 /* B */ 0x04 | STRING_SPEC | NO_E_MOD | NO_O_MOD,
846 /* C */ 0x0a | INT_SPEC | NO_O_MOD,
847 /* D */ 0x02 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
848 /* E */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
849 /* F */ 0x03 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
850 /* G */ 0x03 | CALC_SPEC | NO_E_MOD | NO_O_MOD,
851 /* H */ 0x0b | INT_SPEC | NO_E_MOD,
852 /* I */ 0x0c | INT_SPEC | NO_E_MOD,
853 /* J */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
854 /* K */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
855 /* L */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
856 /* M */ 0x0d | INT_SPEC | NO_E_MOD,
857 /* N */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
858 /* O */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
859 /* P */ 0x05 | STRING_SPEC | NO_E_MOD | NO_O_MOD, /* glibc ; use %p */
860 /* Q */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
861 /* R */ 0x04 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
862 /* S */ 0x0e | INT_SPEC | NO_E_MOD,
863 /* T */ 0x05 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
864 /* U */ 0x04 | CALC_SPEC | NO_E_MOD,
865 /* V */ 0x05 | CALC_SPEC | NO_E_MOD,
866 /* W */ 0x06 | CALC_SPEC | NO_E_MOD,
867 /* X */ 0x0a | STACKED_SPEC | NO_O_MOD,
868 /* Y */ 0x0f | INT_SPEC | NO_O_MOD,
869 /* Z */ 0x01 | CALC_SPEC | NO_E_MOD | NO_O_MOD,
876 /* a */ 0x00 | STRING_SPEC | NO_E_MOD | NO_O_MOD,
877 /* b */ 0x01 | STRING_SPEC | NO_E_MOD | NO_O_MOD,
878 /* c */ 0x08 | STACKED_SPEC | NO_O_MOD,
879 /* d */ 0x00 | INT_SPEC | NO_E_MOD,
880 /* e */ 0x01 | INT_SPEC | NO_E_MOD,
881 /* f */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
882 /* g */ 0x02 | CALC_SPEC | NO_E_MOD | NO_O_MOD,
883 /* h */ 0x01 | STRING_SPEC | NO_E_MOD | NO_O_MOD, /* same as b */
884 /* i */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
885 /* j */ 0x08 | INT_SPEC | NO_E_MOD | NO_O_MOD,
886 /* k */ 0x03 | INT_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
887 /* l */ 0x04 | INT_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
888 /* m */ 0x05 | INT_SPEC | NO_E_MOD,
889 /* n */ 0x00 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
890 /* o */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
891 /* p */ 0x02 | STRING_SPEC | NO_E_MOD | NO_O_MOD,
892 /* q */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
893 /* r */ 0x0b | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
894 /* s */ 0x07 | CALC_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
895 /* t */ 0x01 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
896 /* u */ 0x07 | INT_SPEC | NO_E_MOD,
897 /* v */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
898 /* w */ 0x02 | INT_SPEC | NO_E_MOD,
899 /* x */ 0x09 | STACKED_SPEC | NO_O_MOD,
900 /* y */ 0x09 | INT_SPEC,
901 /* z */ 0x00 | CALC_SPEC | NO_E_MOD | NO_O_MOD,
904 /* WARNING!!! These are dependent on the layout of struct tm!!! */
905 #define FIELD_MAX (26+6+26)
906 60 /* 61? */, 59, 23, 31, 11, 0 /* 9999 */, 6, 0 /* 365 */,
908 #define TP_OFFSETS (FIELD_MAX+8)
915 0, /* CURRENTLY UNUSED */
916 /* NOTE: u,j,y order must be preserved as 6,7,5 seq is used in the code! */
917 #define CALC_OFFSETS (TP_OFFSETS + 7)
934 #define TP_CODES (TP_OFFSETS + 16 + 6)
941 0, /* CURRENTLY UNUSED */
944 2 | 128 | 32 | 16 , /* y */
945 2 | 128 | 64 | 32 | 16 , /* C */
947 2 | 32 | 16 | 0, /* I */
958 #define STRINGS_NL_ITEM_START (TP_CODES + 16 + 6)
959 _NL_ITEM_INDEX(ABDAY_1), /* a */
960 _NL_ITEM_INDEX(ABMON_1), /* b, h */
961 _NL_ITEM_INDEX(AM_STR), /* p */
962 _NL_ITEM_INDEX(DAY_1), /* A */
963 _NL_ITEM_INDEX(MON_1), /* B */
964 _NL_ITEM_INDEX(AM_STR), /* P -- wrong! need lower case */
966 #define STACKED_STRINGS_START (STRINGS_NL_ITEM_START+6)
967 6, 7, 8, 16, 24, 29, /* 6 - offsets from offset-count to strings */
970 '%', 'm', '/', '%', 'd', '/', '%', 'y', 0, /* 9 - %D */
971 '%', 'Y', '-', '%', 'm', '-', '%', 'd', 0, /* 9 - %F (glibc extension) */
972 '%', 'H', ':', '%', 'M', 0, /* 6 - %R*/
973 '%', 'H', ':', '%', 'M', ':', '%', 'S', 0, /* 9 - %T */
975 #define STACKED_STRINGS_NL_ITEM_START (STACKED_STRINGS_START + 43)
976 _NL_ITEM_INDEX(D_T_FMT), /* c */
977 _NL_ITEM_INDEX(D_FMT), /* x */
978 _NL_ITEM_INDEX(T_FMT), /* X */
979 _NL_ITEM_INDEX(T_FMT_AMPM), /* r */
980 #ifdef ENABLE_ERA_CODE
981 _NL_ITEM_INDEX(ERA_D_T_FMT), /* Ec */
982 _NL_ITEM_INDEX(ERA_D_FMT), /* Ex */
983 _NL_ITEM_INDEX(ERA_T_FMT), /* EX */
987 static int load_field(int k, const struct tm *__restrict timeptr)
992 r = ((int *) timeptr)[k];
994 r_max = spec[FIELD_MAX + k];
1003 if ((((unsigned int) r) > r_max) || ((k == 3) && !r)) {
1010 #if defined __UCLIBC_HAS_WCHAR__ && (defined L_wcsftime || defined L_wcsftime_l)
1011 static wchar_t* fmt_to_wc_1(const char *src)
1014 size_t src_len = strlen(src);
1015 wchar_t *dest = (wchar_t *)malloc((src_len + 1) * sizeof(wchar_t));
1019 if (mbsrtowcs(dest, &src, src_len + 1, &mbstate) == (size_t) -1) {
1025 # define fmt_to_wc(dest, src) \
1026 dest = alloc[++allocno] = fmt_to_wc_1(src)
1027 # define to_wc(dest, src) \
1028 dest = fmt_to_wc_1(src)
1030 # define fmt_to_wc(dest, src) (dest) = (src)
1031 # define to_wc(dest, src) (dest) = (src)
1036 #ifdef __UCLIBC_MJN3_ONLY__
1037 #warning TODO: Check multibyte format string validity.
1040 size_t __XL_NPP(strftime)(CHAR_T *__restrict s, size_t maxsize,
1041 const CHAR_T *__restrict format,
1042 const struct tm *__restrict timeptr __LOCALE_PARAM )
1045 register const CHAR_T *p;
1048 #ifndef __UCLIBC_HAS_TM_EXTENSIONS__
1049 const rule_struct *rsp;
1051 const CHAR_T *stack[MAX_PUSH];
1052 #if defined __UCLIBC_HAS_WCHAR__ && (defined L_wcsftime || defined L_wcsftime_l)
1053 const CHAR_T *alloc[MAX_PUSH];
1058 int field_val = 0, i = 0, j, lvl;
1059 int x[3]; /* wday, yday, year */
1061 char buf[__UIM_BUFLEN_LONG] = {0,};
1065 /* We'll, let's get this out of the way. */
1066 _time_tzset(_time_mktime((struct tm *) timeptr, 0) < new_rule_starts);
1078 *s = 0; /* nul-terminate */
1079 return maxsize - count;
1086 if ((*(o = (CHAR_T *)p) == '%') && (*++p != '%')) {
1089 if ((*p == 'O') || (*p == 'E')) { /* modifier */
1090 mod |= ((*p == 'O') ? NO_O_MOD : NO_E_MOD);
1094 if ((((unsigned char)(((*p) | 0x20) - 'a')) >= 26)
1095 || (((code = spec[(int)(*p - 'A')]) & mod) >= ILLEGAL_SPEC)
1103 code &= ILLEGAL_SPEC; /* modifiers are preserved in mod var. */
1105 if ((code & MASK_SPEC) == STACKED_SPEC) {
1106 if (lvl == MAX_PUSH) {
1107 goto OUTPUT; /* Stack full so treat as illegal spec. */
1110 if ((code &= 0xf) < 8) {
1111 ccp = (const char *)(spec + STACKED_STRINGS_START + code);
1116 ccp = (const char *)spec + STACKED_STRINGS_NL_ITEM_START + (code & 7);
1118 #ifdef ENABLE_ERA_CODE
1119 if ((mod & NO_E_MOD) /* Actually, this means E modifier present. */
1120 && (*(ccp = __XL_NPP(nl_langinfo)(_NL_ITEM(LC_TIME,
1121 (int)(((unsigned char *)p)[4]))
1129 ccp = __XL_NPP(nl_langinfo)(_NL_ITEM(LC_TIME,
1130 (int)(*((unsigned char *)p)))
1137 ccp = (const char *)(spec + 26); /* set to "????" */
1138 if ((code & MASK_SPEC) == CALC_SPEC) {
1143 /* Use a cast to silence the warning since *timeptr won't
1145 if ((t = _time_mktime((struct tm *) timeptr, 0))
1151 #ifdef TIME_T_IS_UNSIGNED
1152 ccp = _uintmaxtostr(buf + sizeof(buf) - 1,
1156 ccp = _uintmaxtostr(buf + sizeof(buf) - 1,
1158 -10, __UIM_DECIMAL);
1160 o_count = sizeof(buf);
1163 } else if (((*p) | 0x20) == 'z') { /* 'z' or 'Z' */
1165 if (timeptr->tm_isdst < 0) {
1166 /* SUSv3 specifies this behavior for 'z', but we'll also
1167 * treat it as "no timezone info" for 'Z' too. */
1172 #ifdef __UCLIBC_HAS_TM_EXTENSIONS__
1175 # define RSP_TZNAME timeptr->tm_zone
1176 # define RSP_GMT_OFFSET (-timeptr->tm_gmtoff)
1178 # define RSP_TZNAME timeptr->__tm_zone
1179 # define RSP_GMT_OFFSET (-timeptr->__tm_gmtoff)
1184 #define RSP_TZNAME rsp->tzname
1185 #define RSP_GMT_OFFSET rsp->gmt_offset
1187 __UCLIBC_MUTEX_LOCK(_time_tzlock);
1190 if (timeptr->tm_isdst > 0) {
1197 #ifdef __UCLIBC_HAS_TM_EXTENSIONS__
1198 /* Sigh... blasted glibc extensions. Of course we can't
1199 * count on the pointer being valid. Best we can do is
1200 * handle NULL, which looks to be all that glibc does.
1201 * At least that catches the memset() with 0 case.
1202 * NOTE: We handle this case differently than glibc!
1203 * It uses system timezone name (based on tm_isdst) in this
1204 * case... although it always seems to use the embedded
1205 * tm_gmtoff value. What we'll do instead is treat the
1206 * timezone name as unknown/invalid and return "???". */
1208 ccp = (const char *)(spec + 27); /* "???" */
1211 assert(ccp != NULL);
1213 if (!ccp) { /* PARANOIA */
1214 ccp = spec+30; /* empty string */
1219 #ifdef __UCLIBC_HAS_TM_EXTENSIONS__
1224 if ((tzo = -RSP_GMT_OFFSET) < 0) {
1232 field_val = ((i / 60) * 100) + (i % 60);
1234 i = 16 + 6; /* 0-fill, width = 4 */
1236 #ifndef __UCLIBC_HAS_TM_EXTENSIONS__
1237 __UCLIBC_MUTEX_UNLOCK(_time_tzlock);
1243 /* TODO: don't need year for U, W */
1244 for (i=0 ; i < 3 ; i++) {
1245 if ((x[i] = load_field(spec[CALC_OFFSETS+i],timeptr)) < 0) {
1250 i = 16 + 2; /* 0-fill, width = 2 */
1252 if ((*p == 'U') || (*p == 'W')) {
1253 field_val = ((x[1] - x[0]) + 7);
1258 if ((*p == 'W') && !x[0]) {
1261 } else { /* ((*p == 'g') || (*p == 'G') || (*p == 'V')) */
1263 isofm = (((x[1] - x[0]) + 11) % 7) - 3; /* [-3,3] */
1265 if (x[1] < isofm) { /* belongs to previous year */
1267 x[1] += 365 + __isleap(x[2]);
1271 field_val = ((x[1] - isofm) / 7) + 1; /* week # */
1272 days = 365 + __isleap(x[2]);
1273 isofm = ((isofm + 7*53 + 3 - days)) % 7 + days - 3; /* next year */
1274 if (x[1] >= isofm) { /* next year */
1280 if (*p != 'V') { /* need year */
1281 field_val = x[2]; /* TODO: what if x[2] now 10000 ?? */
1285 i = 16 + 6; /* 0-fill, width = 4 */
1291 i = TP_OFFSETS + (code & 0x1f);
1292 if ((field_val = load_field(spec[i], timeptr)) < 0) {
1296 i = spec[i+(TP_CODES - TP_OFFSETS)];
1298 j = (i & 128) ? 100: 12;
1304 if (((i & 128) + field_val) == 0) { /* mod 12? == 0 */
1305 field_val = j; /* set to 12 */
1308 field_val += (i & 1);
1309 if ((i & 8) && !field_val) {
1314 if ((code & MASK_SPEC) == STRING_SPEC) {
1316 field_val += spec[STRINGS_NL_ITEM_START + (code & 0xf)];
1317 ccp = __XL_NPP(nl_langinfo)(_NL_ITEM(LC_TIME, field_val) __LOCALE_ARG);
1320 o_count = ((i >> 1) & 3) + 1;
1321 ccp = buf + o_count;
1323 *(char *)(--ccp) = '0' + (field_val % 10);
1325 } while (ccp > buf);
1327 *buf = ' ' + (i & 16);
1335 while (o_count && count && *o) {
1340 #if defined __UCLIBC_HAS_WCHAR__ && (defined L_wcsftime || defined L_wcsftime_l)
1342 free((void *)alloc[allocno--]);
1346 # ifdef L_strftime_l
1347 libc_hidden_def(strftime_l)
1350 #endif /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */
1353 /**********************************************************************/
1354 #if defined(L_strptime) || defined(L_strptime_l)
1356 #define ISDIGIT(C) __isdigit_char((C))
1358 #ifdef __UCLIBC_DO_XLOCALE
1359 #define ISSPACE(C) isspace_l((C), locale_arg)
1361 #define ISSPACE(C) isspace((C))
1364 #if defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE)
1366 char *strptime(const char *__restrict buf, const char *__restrict format,
1367 struct tm *__restrict tm)
1369 return strptime_l(buf, format, tm, __UCLIBC_CURLOCALE);
1372 #else /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */
1375 * 1) %l and %k are space-padded, so "%l" by itself fails while " %l" succeeds.
1376 * Both work for glibc. So, should we always strip spaces?
1381 * There are several differences between this strptime and glibc's strptime.
1382 * 1) glibc strips leading space before numeric conversions.
1383 * 2) glibc will read fields without whitespace in between. SUSv3 states
1384 * that you must have whitespace between conversion operators. Besides,
1385 * how do you know how long a number should be if there are leading 0s?
1386 * 3) glibc attempts to compute some the struct tm fields based on the
1387 * data retrieved; tm_wday in particular. I don't as I consider it
1388 * another glibc attempt at mind-reading...
1391 #define NO_E_MOD 0x80
1392 #define NO_O_MOD 0x40
1394 #define ILLEGAL_SPEC 0x3f
1396 #define INT_SPEC 0x00 /* must be 0x00!! */
1397 #define STRING_SPEC 0x10 /* must be 0x10!! */
1398 #define CALC_SPEC 0x20
1399 #define STACKED_SPEC 0x30
1401 #define MASK_SPEC 0x30
1403 /* Warning: Assumes ASCII values! (as do lots of other things in the lib...) */
1404 static const unsigned char spec[] = {
1405 /* A */ 0x02 | STRING_SPEC | NO_E_MOD | NO_O_MOD,
1406 /* B */ 0x01 | STRING_SPEC | NO_E_MOD | NO_O_MOD,
1407 /* C */ 0x08 | INT_SPEC | NO_O_MOD,
1408 /* D */ 0x01 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
1409 /* E */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1410 /* F */ 0x02 | STACKED_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
1411 /* G */ 0x0f | INT_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
1412 /* H */ 0x06 | INT_SPEC | NO_E_MOD,
1413 /* I */ 0x07 | INT_SPEC | NO_E_MOD,
1414 /* J */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1415 /* K */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1416 /* L */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1417 /* M */ 0x04 | INT_SPEC | NO_E_MOD,
1418 /* N */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1419 /* O */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1420 /* P */ 0x00 | STRING_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
1421 /* Q */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1422 /* R */ 0x03 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
1423 /* S */ 0x05 | INT_SPEC | NO_E_MOD,
1424 /* T */ 0x04 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
1425 /* U */ 0x0c | INT_SPEC | NO_E_MOD,
1426 /* V */ 0x0d | INT_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
1427 /* W */ 0x0c | INT_SPEC | NO_E_MOD,
1428 /* X */ 0x0a | STACKED_SPEC | NO_O_MOD,
1429 /* Y */ 0x0a | INT_SPEC | NO_O_MOD,
1430 /* Z */ 0x02 | CALC_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
1432 /* WARNING! This assumes orderings:
1434 * ABDAY_1-ABDAY-7,DAY_1-DAY_7
1435 * ABMON_1-ABMON_12,MON_1-MON12
1436 * Also, there are exactly 6 bytes between 'Z' and 'a'.
1438 #define STRINGS_NL_ITEM_START (26)
1439 _NL_ITEM_INDEX(AM_STR), /* p (P) */
1440 _NL_ITEM_INDEX(ABMON_1), /* B, b */
1441 _NL_ITEM_INDEX(ABDAY_1), /* A, a */
1446 /* a */ 0x02 | STRING_SPEC | NO_E_MOD | NO_O_MOD,
1447 /* b */ 0x01 | STRING_SPEC | NO_E_MOD | NO_O_MOD,
1448 /* c */ 0x08 | STACKED_SPEC | NO_O_MOD,
1449 /* d */ 0x00 | INT_SPEC | NO_E_MOD,
1450 /* e */ 0x00 | INT_SPEC | NO_E_MOD,
1451 /* f */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1452 /* g */ 0x0e | INT_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
1453 /* h */ 0x01 | STRING_SPEC | NO_E_MOD | NO_O_MOD,
1454 /* i */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1455 /* j */ 0x01 | INT_SPEC | NO_E_MOD | NO_O_MOD,
1456 /* k */ 0x06 | INT_SPEC | NO_E_MOD, /* glibc */
1457 /* l */ 0x07 | INT_SPEC | NO_E_MOD, /* glibc */
1458 /* m */ 0x02 | INT_SPEC | NO_E_MOD,
1459 /* n */ 0x00 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
1460 /* o */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1461 /* p */ 0x00 | STRING_SPEC | NO_E_MOD | NO_O_MOD,
1462 /* q */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1463 /* r */ 0x0b | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
1464 /* s */ 0x00 | CALC_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
1465 /* t */ 0x00 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
1466 /* u */ 0x0b | INT_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
1467 /* v */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1468 /* w */ 0x03 | INT_SPEC | NO_E_MOD,
1469 /* x */ 0x09 | STACKED_SPEC | NO_O_MOD,
1470 /* y */ 0x09 | INT_SPEC,
1471 /* z */ 0x01 | CALC_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
1473 #define INT_FIELD_START (26+6+26)
1474 /* (field #) << 3 + lower bound (0|1) + correction 0:none, 2:-1, 4:-1900
1475 * followed by upper bound prior to correction with 1=>366 and 2=>9999. */
1476 /* d, e */ (3 << 3) + 1 + 0, 31,
1477 /* j */ (7 << 3) + 1 + 2, /* 366 */ 1,
1478 /* m */ (4 << 3) + 1 + 2, 12,
1479 /* w */ (6 << 3) + 0 + 0, 6,
1480 /* M */ (1 << 3) + 0 + 0, 59,
1481 /* S */ 0 + 0 + 0, 60,
1482 /* H (k) */ (2 << 3) + 0 + 0, 23,
1483 /* I (l) */ (9 << 3) + 1 + 0, 12, /* goes with 8 -- am/pm */
1484 /* C */ (10<< 3) + 0 + 0, 99,
1485 /* y */ (11<< 3) + 0 + 0, 99,
1486 /* Y */ (5 << 3) + 0 + 4, /* 9999 */ 2,
1487 /* u */ (6 << 3) + 1 + 0, 7,
1488 /* The following are processed and range-checked, but ignored otherwise. */
1489 /* U, W */ (12<< 3) + 0 + 0, 53,
1490 /* V */ (12<< 3) + 1 + 0, 53,
1491 /* g */ (12<< 3) + 0 + 0, 99,
1492 /* G */ (12<< 3) + 0 /*+ 4*/, /* 9999 */ 2, /* Note: -1 or 10000? */
1494 #define STACKED_STRINGS_START (INT_FIELD_START+32)
1495 5, 6, 14, 22, 27, /* 5 - offsets from offset-count to strings */
1496 ' ', 0, /* 2 - %n or %t */
1497 '%', 'm', '/', '%', 'd', '/', '%', 'y', 0, /* 9 - %D */
1498 '%', 'Y', '-', '%', 'm', '-', '%', 'd', 0, /* 9 - %F (glibc extension) */
1499 '%', 'H', ':', '%', 'M', 0, /* 6 - %R*/
1500 '%', 'H', ':', '%', 'M', ':', '%', 'S', 0, /* 9 - %T */
1502 #define STACKED_STRINGS_NL_ITEM_START (STACKED_STRINGS_START + 40)
1503 _NL_ITEM_INDEX(D_T_FMT), /* c */
1504 _NL_ITEM_INDEX(D_FMT), /* x */
1505 _NL_ITEM_INDEX(T_FMT), /* X */
1506 _NL_ITEM_INDEX(T_FMT_AMPM), /* r */
1507 #ifdef ENABLE_ERA_CODE
1508 _NL_ITEM_INDEX(ERA_D_T_FMT), /* Ec */
1509 _NL_ITEM_INDEX(ERA_D_FMT), /* Ex */
1510 _NL_ITEM_INDEX(ERA_T_FMT), /* EX */
1516 char *__XL_NPP(strptime)(const char *__restrict buf, const char *__restrict format,
1517 struct tm *__restrict tm __LOCALE_PARAM)
1519 register const char *p;
1521 const char *stack[MAX_PUSH];
1529 fields[i] = INT_MIN;
1537 if (lvl == 0) { /* Done. */
1538 if (fields[6] == 7) { /* Cleanup for %u here since just once. */
1539 fields[6] = 0; /* Don't use mod in case unset. */
1543 do { /* Store the values into tm. */
1544 if (fields[i] != INT_MIN) {
1545 ((int *) tm)[i] = fields[i];
1549 return (char *) buf; /* Success. */
1555 if ((*p == '%') && (*++p != '%')) {
1557 if ((*p == 'O') || (*p == 'E')) { /* Modifier? */
1558 mod |= ((*p == 'O') ? NO_O_MOD : NO_E_MOD);
1563 || (((unsigned char)(((*p) | 0x20) - 'a')) >= 26)
1564 || (((code = spec[(int)(*p - 'A')]) & mod) >= ILLEGAL_SPEC)
1566 return NULL; /* Illegal spec. */
1569 if ((code & MASK_SPEC) == STACKED_SPEC) {
1570 if (lvl == MAX_PUSH) {
1571 return NULL; /* Stack full so treat as illegal spec. */
1574 if ((code &= 0xf) < 8) {
1575 p = ((const char *) spec) + STACKED_STRINGS_START + code;
1576 p += *((unsigned char *)p);
1580 p = ((const char *) spec) + STACKED_STRINGS_NL_ITEM_START
1582 #ifdef ENABLE_ERA_CODE
1583 if ((mod & NO_E_MOD) /* Actually, this means E modifier present. */
1584 && (*(o = __XL_NPP(nl_langinfo)(_NL_ITEM(LC_TIME,
1585 (int)(((unsigned char *)p)[4]))
1593 p = __XL_NPP(nl_langinfo)(_NL_ITEM(LC_TIME,
1594 (int)(*((unsigned char *)p)))
1602 if ((code & MASK_SPEC) == STRING_SPEC) {
1604 j = spec[STRINGS_NL_ITEM_START + 3 + code];
1605 i = _NL_ITEM(LC_TIME, spec[STRINGS_NL_ITEM_START + code]);
1606 /* Go backwards to check full names before abreviations. */
1609 o = __XL_NPP(nl_langinfo)(i+j __LOCALE_ARG);
1610 if (!__XL_NPP(strncasecmp)(buf, o, strlen(o) __LOCALE_ARG) && *o) {
1611 do { /* Found a match. */
1614 if (!code) { /* am/pm */
1616 if (fields[9] >= 0) { /* We have a previous %I or %l. */
1617 fields[2] = fields[9] + fields[8];
1619 } else { /* day (4) or month (6) */
1620 fields[2 + (code << 1)]
1621 = j % (spec[STRINGS_NL_ITEM_START + 3 + code] >> 1);
1626 return NULL; /* Failed to match. */
1629 if ((code & MASK_SPEC) == CALC_SPEC) {
1630 if ((code &= 0xf) < 1) { /* s or z*/
1636 if (!ISSPACE(*buf)) { /* Signal an error if whitespace. */
1637 #ifdef TIME_T_IS_UNSIGNED
1638 t = __XL_NPP(strtoul)(buf, &o, 10 __LOCALE_ARG);
1640 t = __XL_NPP(strtol)(buf, &o, 10 __LOCALE_ARG);
1643 if ((o == buf) || errno) { /* Not a number or overflow. */
1646 __set_errno(i); /* Restore errno. */
1649 if (!code) { /* s */
1650 localtime_r(&t, tm); /* TODO: check for failure? */
1652 do { /* Now copy values from tm to fields. */
1653 fields[i] = ((int *) tm)[i];
1657 /* TODO: glibc treats %Z as a nop. For now, do the same. */
1661 assert((code & MASK_SPEC) == INT_SPEC);
1663 register const unsigned char *x;
1665 x = spec + INT_FIELD_START + (code << 1);
1666 if ((j = x[1]) < 3) { /* upper bound (inclusive) */
1667 j = ((j==1) ? 366 : 9999);
1670 while (ISDIGIT(*buf)) {
1674 if ((i = 10*i + (*buf - '0')) > j) { /* Overflow. */
1679 if (i < (*x & 1)) { /* This catches no-digit case too. */
1689 if (*x == (9 << 3) + 1 + 0) { /* %I or %l */
1693 if (fields[8] >= 0) { /* We have a previous %p or %P. */
1694 fields[2] = i + fields[8];
1698 fields[(*x) >> 3] = i;
1700 if (((unsigned char)(*x - (10 << 3) + 0 + 0)) <= 8) { /* %C or %y */
1701 if ((j = fields[10]) < 0) { /* No %C, so i must be %y data. */
1702 if (i <= 68) { /* Map [0-68] to 2000+i */
1705 } else { /* Have %C data, but what about %y? */
1706 if ((i = fields[11]) < 0) { /* No %y data. */
1707 i = 0; /* Treat %y val as 0 following glibc's example. */
1715 } else if (ISSPACE(*p)) {
1717 while (ISSPACE(*buf)) {
1721 } else if (*buf++ == *p++) {
1726 # ifdef L_strptime_l
1727 libc_hidden_def(strptime_l)
1730 #endif /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */
1733 /**********************************************************************/
1737 #error The uClibc version of time is in sysdeps/linux/common.
1740 time_t time(register time_t *tloc)
1743 register struct timeval *p = &tv;
1745 gettimeofday(p, NULL); /* This should never fail... */
1755 /**********************************************************************/
1758 static const char vals[] = {
1759 'T', 'Z', 0, /* 3 */
1760 'U', 'T', 'C', 0, /* 4 */
1761 25, 60, 60, 1, /* 4 */
1764 6, 0, 0, /* Note: overloaded for non-M non-J case... */
1766 ',', 'M', '4', '.', '1', '.', '0',
1767 ',', 'M', '1', '0', '.', '5', '.', '0', 0,
1768 ',', 'M', '3', '.', '2', '.', '0',
1769 ',', 'M', '1', '1', '.', '1', '.', '0', 0
1773 #define UTC (vals + 3)
1774 #define RANGE (vals + 7)
1775 #define RULE (vals + 11 - 1)
1776 #define DEFAULT_RULES (vals + 22)
1777 #define DEFAULT_2007_RULES (vals + 38)
1779 /* Initialize to UTC. */
1782 char *tzname[2] = { (char *) UTC, (char *) (UTC-1) };
1784 __UCLIBC_MUTEX_INIT(_time_tzlock, PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP);
1786 rule_struct _time_tzinfo[2];
1788 static const char *getoffset(register const char *e, long *pn)
1790 register const char *s = RANGE-1;
1798 if (__isdigit_char(*e)) {
1801 if (__isdigit_char(*e)) {
1802 f = 10 * f + (*e++ - '0');
1804 if (((unsigned int)f) >= *s) {
1819 static const char *getnumber(register const char *e, int *pn)
1822 /* bcc can optimize the counter if it thinks it is a pointer... */
1823 register const char *n = (const char *) 3;
1827 while (n && __isdigit_char(*e)) {
1828 f = 10 * f + (*e++ - '0');
1833 return (n == (const char *) 3) ? NULL : e;
1839 while (n && __isdigit_char(*e)) {
1840 f = 10 * f + (*e++ - '0');
1845 return (n == 3) ? NULL : e;
1846 #endif /* __BCC__ */
1850 #ifdef __UCLIBC_MJN3_ONLY__
1851 #warning CONSIDER: Should we preserve errno from open/read/close errors re TZ file?
1854 #ifdef __UCLIBC_HAS_TZ_FILE__
1856 #ifndef __UCLIBC_HAS_TZ_FILE_READ_MANY__
1857 static smallint TZ_file_read; /* Let BSS initialization set this to 0. */
1860 static char *read_TZ_file(char *buf)
1866 fd = open(__UCLIBC_TZ_FILE_PATH__, O_RDONLY);
1869 /* TZ are small *files*. On files, short reads
1870 * only occur on EOF (unlike, say, pipes).
1871 * The code below is pedanticallly more correct,
1872 * but this way we always read at least twice:
1873 * 1st read is short, 2nd one is zero bytes.
1875 size_t todo = TZ_BUFLEN;
1878 r = read(fd, p, todo);
1887 /* Shorter, and does one fewer read syscall */
1888 r = read(fd, buf, TZ_BUFLEN);
1893 if ((p > buf) && (p[-1] == '\n')) { /* Must end with newline */
1896 #ifndef __UCLIBC_HAS_TZ_FILE_READ_MANY__
1905 #ifdef __UCLIBC_FALLBACK_TO_ETC_LOCALTIME__
1907 fd = open("/etc/localtime", O_RDONLY);
1909 r = read(fd, buf, TZ_BUFLEN);
1911 || strncmp(buf, "TZif", 4) != 0
1912 || (unsigned char)buf[4] < 2
1913 || lseek(fd, -TZ_BUFLEN, SEEK_END) < 0
1917 /* tzfile.h from tzcode database says about TZif2+ files:
1919 ** If tzh_version is '2' or greater, the above is followed by a second instance
1920 ** of tzhead and a second instance of the data in which each coded transition
1921 ** time uses 8 rather than 4 chars,
1922 ** then a POSIX-TZ-environment-variable-style string for use in handling
1923 ** instants after the last transition time stored in the file
1924 ** (with nothing between the newlines if there is no POSIX representation for
1927 r = read(fd, buf, TZ_BUFLEN);
1928 if (r <= 0 || buf[--r] != '\n')
1932 if (buf[--r] == '\n') {
1934 #ifndef __UCLIBC_HAS_TZ_FILE_READ_MANY__
1939 } /* else ('\n' not found): p remains NULL */
1943 #endif /* __UCLIBC_FALLBACK_TO_ETC_LOCALTIME__ */
1947 #endif /* __UCLIBC_HAS_TZ_FILE__ */
1951 _time_tzset((time(NULL)) < new_rule_starts);
1954 void _time_tzset(int use_old_rules)
1956 register const char *e;
1960 rule_struct new_rules[2];
1963 #ifdef __UCLIBC_HAS_TZ_FILE__
1964 char buf[TZ_BUFLEN];
1966 #ifdef __UCLIBC_HAS_TZ_CACHING__
1967 static char oldval[TZ_BUFLEN]; /* BSS-zero'd. */
1970 /* Put this inside the lock to prevent the possibility of two different
1971 * timezones being used in a threaded app. */
1972 __UCLIBC_MUTEX_LOCK(_time_tzlock);
1974 e = getenv(TZ); /* TZ env var always takes precedence. */
1976 #if defined(__UCLIBC_HAS_TZ_FILE__) && !defined(__UCLIBC_HAS_TZ_FILE_READ_MANY__)
1978 /* Never use TZfile if TZ env var is set. */
1982 /* We already parsed TZfile before, skip everything. */
1987 /* Warning!!! Since uClibc doesn't do lib locking, the following is
1988 * potentially unsafe in a multi-threaded program since it is remotely
1989 * possible that another thread could call setenv() for TZ and overwrite
1990 * the string being parsed. So, don't do that... */
1992 #ifdef __UCLIBC_HAS_TZ_FILE__
1994 e = read_TZ_file(buf);
1996 if (!e /* TZ env var not set and no TZfile (or bad TZfile) */
1997 || !*e /* or set to empty string. */
2002 if (*e == ':') { /* Ignore leading ':'. */
2006 #ifdef __UCLIBC_HAS_TZ_CACHING__
2007 if (strcmp(e, oldval) == 0) {
2008 /* Same string as last time... nothing to do. */
2011 /* Make a copy of the TZ env string. It won't be nul-terminated if
2012 * it is too long, but it that case it will be illegal and will be reset
2013 * to the empty string anyway. */
2014 strncpy(oldval, e, TZ_BUFLEN);
2018 new_rules[1].tzname[0] = 0;
2020 /* Get std or dst name. */
2027 s = new_rules[count].tzname;
2030 && isascii(*e) /* SUSv3 requires char in portable char set. */
2032 || (c && (isalnum(*e) || (*e == '+') || (*e == '-')))
2036 if (++n > TZNAME_MAX) {
2042 if ((n < 3) /* Check for minimum length. */
2043 || (c && (*e++ != c)) /* Match any quoting '<'. */
2050 if ((*e != '-') && (*e != '+')) {
2051 if (count && !__isdigit_char(*e)) {
2052 off -= 3600; /* Default to 1 hour ahead of std. */
2059 e = getoffset(e, &off);
2065 off = -off; /* Save off in case needed for dst default. */
2068 new_rules[count].gmt_offset = off;
2071 new_rules[1].gmt_offset = off; /* Shouldn't be needed... */
2076 } else { /* OK, we have dst, so get some rules. */
2078 if (!*e) { /* No rules so default to US rules. */
2079 e = use_old_rules ? DEFAULT_RULES : DEFAULT_2007_RULES;
2081 if (e == DEFAULT_RULES)
2082 printf("tzset: Using old rules.\n");
2083 else if (e == DEFAULT_2007_RULES)
2084 printf("tzset: Using new rules\n");
2086 printf("tzset: Using undefined rules\n");
2100 } else if (c == 'J') {
2108 p = &new_rules[count].rule_type;
2116 e = getnumber(e, &f);
2118 || ((unsigned int)(f - s[1]) > n)
2119 || (*s && (*e++ != *s))
2128 off = 2 * 60 * 60; /* Default to 2:00:00 */
2131 e = getoffset(e, &off);
2136 new_rules[count].dst_offset = off;
2137 } while (++count < 2);
2141 #ifdef __UCLIBC_HAS_TZ_CACHING__
2142 oldval[0] = 0; /* oldval = "" */
2144 memset(_time_tzinfo, 0, sizeof(_time_tzinfo));
2145 strcpy(_time_tzinfo[0].tzname, UTC);
2150 memcpy(_time_tzinfo, new_rules, sizeof(new_rules));
2152 tzname[0] = _time_tzinfo[0].tzname;
2153 tzname[1] = _time_tzinfo[1].tzname;
2154 daylight = !!_time_tzinfo[1].tzname[0];
2155 timezone = _time_tzinfo[0].gmt_offset;
2157 #if (defined(__UCLIBC_HAS_TZ_FILE__) && !defined(__UCLIBC_HAS_TZ_FILE_READ_MANY__)) || \
2158 defined(__UCLIBC_HAS_TZ_CACHING__)
2161 __UCLIBC_MUTEX_UNLOCK(_time_tzlock);
2163 libc_hidden_def(tzset)
2165 /**********************************************************************/
2166 /* #ifdef L_utime */
2168 /* utime is a syscall in both linux and elks. */
2169 /* int utime(const char *path, const struct utimbuf *times) */
2172 /**********************************************************************/
2174 /**********************************************************************/
2178 #error The uClibc version of utimes is in sysdeps/linux/common.
2183 int utimes(const char *filename, register const struct timeval *tvp)
2185 register struct utimbuf *p = NULL;
2190 p->actime = tvp[0].tv_sec;
2191 p->modtime = tvp[1].tv_sec;
2193 return utime(filename, p);
2197 /**********************************************************************/
2200 static const uint16_t _vals[] = {
2201 60, 60, 24, 7 /* special */, 36524, 1461, 365, 0
2204 static const unsigned char days[] = {
2205 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, /* non-leap */
2209 #ifdef __UCLIBC_HAS_TM_EXTENSIONS__
2210 static const char utc_string[] = "UTC";
2214 * If time_t is 32 bits, then no overflow is possible.
2215 * It time_t is > 32 bits, this needs to be adjusted to deal with overflow.
2218 /* Note: offset is the correction in _days_ to *timer! */
2220 struct tm attribute_hidden *_time_t2tm(const time_t *__restrict timer,
2221 int offset, struct tm *__restrict result)
2225 int wday = wday; /* ok to be uninitialized, shutting up warning */
2228 register const uint16_t *vp;
2234 if ((v = *vp) == 7) {
2235 /* Overflow checking, assuming time_t is long int... */
2236 #if (LONG_MAX > INT_MAX) && (LONG_MAX > 2147483647L)
2237 #if (INT_MAX == 2147483647L) && (LONG_MAX == 9223372036854775807L)
2238 /* Valid range for t is [-784223472856L, 784223421720L].
2239 * Outside of this range, the tm_year field will overflow. */
2240 if (((unsigned long)(t + offset- -784223472856L))
2241 > (784223421720L - -784223472856L)
2246 #error overflow conditions unknown
2250 /* We have days since the epoch, so caluclate the weekday. */
2251 #if defined(__BCC__) && TIME_T_IS_UNSIGNED
2252 wday = (t + 4) % (*vp); /* t is unsigned */
2254 wday = ((int)((t % (*vp)) + 11)) % ((int)(*vp)); /* help bcc */
2256 /* Set divisor to days in 400 years. Be kind to bcc... */
2257 v = ((time_t)(vp[1])) << 2;
2259 /* Change to days since 1/1/1601 so that for 32 bit time_t
2260 * values, we'll have t >= 0. This should be changed for
2261 * archs with larger time_t types.
2262 * Also, correct for offset since a multiple of 7. */
2264 /* TODO: Does this still work on archs with time_t > 32 bits? */
2265 t += (135140L - 366) + offset; /* 146097 - (365*30 + 7) -366 */
2267 #if defined(__BCC__) && TIME_T_IS_UNSIGNED
2268 t -= ((t1 = t / v) * v);
2270 if ((t -= ((t1 = t / v) * v)) < 0) {
2276 if ((*vp == 7) && (t == v-1)) {
2277 --t; /* Correct for 400th year leap case */
2278 ++p[4]; /* Stash the extra day... */
2281 #if defined(__BCC__) && 0
2304 *p += ((int) t); /* result[7] .. tm_yday */
2306 p -= 2; /* at result[5] */
2308 #if (LONG_MAX > INT_MAX) && (LONG_MAX > 2147483647L)
2309 /* Protect against overflow. TODO: Unecessary if int arith wraps? */
2310 *p = ((((p[-2]<<2) + p[-1])*25 + p[0])<< 2) + (p[1] - 299); /* tm_year */
2312 *p = ((((p[-2]<<2) + p[-1])*25 + p[0])<< 2) + p[1] - 299; /* tm_year */
2315 p[1] = wday; /* result[6] .. tm_wday */
2318 register const unsigned char *d = days;
2321 if (__isleap(wday)) {
2325 wday = p[2] + 1; /* result[7] .. tm_yday */
2326 *--p = 0; /* at result[4] .. tm_mon */
2330 d -= 11; /* Backup to non-leap Feb. */
2333 ++*p; /* Increment tm_mon. */
2335 p[-1] = wday; /* result[3] .. tm_mday */
2337 /* TODO -- should this be 0? */
2338 p[4] = 0; /* result[8] .. tm_isdst */
2339 #ifdef __UCLIBC_HAS_TM_EXTENSIONS__
2341 result->tm_gmtoff = 0;
2342 result->tm_zone = utc_string;
2344 result->__tm_gmtoff = 0;
2345 result->__tm_zone = utc_string;
2347 #endif /* __UCLIBC_HAS_TM_EXTENSIONS__ */
2353 /**********************************************************************/
2356 struct tm __time_tm; /* Global shared by gmtime() and localtime(). */
2359 /**********************************************************************/
2360 #ifdef L__time_mktime
2362 time_t attribute_hidden _time_mktime(struct tm *timeptr, int store_on_success)
2366 __UCLIBC_MUTEX_LOCK(_time_tzlock);
2370 t = _time_mktime_tzi(timeptr, store_on_success, _time_tzinfo);
2372 __UCLIBC_MUTEX_UNLOCK(_time_tzlock);
2378 /**********************************************************************/
2379 #ifdef L__time_mktime_tzi
2381 static const unsigned char __vals[] = {
2382 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, /* non-leap */
2386 time_t attribute_hidden _time_mktime_tzi(struct tm *timeptr, int store_on_success,
2396 /* 0:sec 1:min 2:hour 3:mday 4:mon 5:year 6:wday 7:yday 8:isdst */
2397 register int *p = (int *) &x;
2398 register const unsigned char *s;
2401 memcpy(p, timeptr, sizeof(struct tm));
2403 if (!tzi[1].tzname[0]) { /* No dst in this timezone, */
2404 p[8] = 0; /* so set tm_isdst to 0. */
2408 if (p[8]) { /* Either dst or unknown? */
2409 default_dst = 1; /* Assume advancing (even if unknown). */
2410 p[8] = ((p[8] > 0) ? 1 : -1); /* Normalize so abs() <= 1. */
2414 p[5] = (p[5] - ((p[6] = p[5]/d) * d)) + (p[7] = p[4]/12);
2415 if ((p[4] -= 12 * p[7]) < 0) {
2421 d = (p[5] += 1900); /* Correct year. Now between 1900 and 2300. */
2431 s -= 11; /* Backup to non-leap Feb. */
2437 _time_tzset (x.tm_year < 2007); /* tm_year was expanded above */
2441 days = -719163L + ((long)d)*365 + ((d/4) - (d/100) + (d/400) + p[3] + p[7]);
2442 secs = p[0] + 60*( p[1] + 60*((long)(p[2])) )
2443 + tzi[default_dst].gmt_offset;
2449 if ( ((unsigned long)(days + secs/86400L)) > 49710L) {
2453 secs += (days * 86400L);
2456 d = -719163L + d*365 + (d/4) - (d/100) + (d/400);
2458 + tzi[default_dst].gmt_offset
2461 + 24*(((146073L * ((long long)(p[6])) + d)
2465 if (((unsigned long long)(secs - LONG_MIN))
2466 > (((unsigned long long)LONG_MAX) - LONG_MIN)
2473 d = ((struct tm *)p)->tm_isdst;
2476 __time_localtime_tzi(&t, (struct tm *)p, tzi);
2478 if (t == ((time_t)(-1))) { /* Remember, time_t can be unsigned. */
2482 if ((d < 0) && (((struct tm *)p)->tm_isdst != default_dst)) {
2484 secs -= (days * 86400L);
2486 secs += (tzi[1-default_dst].gmt_offset
2487 - tzi[default_dst].gmt_offset);
2492 if (store_on_success) {
2493 memcpy(timeptr, p, sizeof(struct tm));
2502 /**********************************************************************/
2503 #if (defined(L_wcsftime) || defined(L_wcsftime_l))
2505 /* Implemented via strftime / strftime_l wchar_t variants */
2508 /**********************************************************************/
2510 /* Return the number of days in YEAR. */
2512 int dysize(int year)
2514 return __isleap(year) ? 366 : 365;
2518 /**********************************************************************/