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.
138 #include <sys/time.h>
143 #include <langinfo.h>
147 #include <bits/uClibc_uintmaxtostr.h>
148 #include <bits/uClibc_mutex.h>
151 #ifdef __UCLIBC_HAS_WCHAR__
154 #ifdef __UCLIBC_HAS_XLOCALE__
158 libc_hidden_proto(asctime)
159 libc_hidden_proto(asctime_r)
160 libc_hidden_proto(ctime)
161 libc_hidden_proto(localtime)
162 libc_hidden_proto(localtime_r)
164 /* Experimentally off - libc_hidden_proto(memset) */
165 /* Experimentally off - libc_hidden_proto(memcpy) */
166 /* Experimentally off - libc_hidden_proto(strcmp) */
167 /* Experimentally off - libc_hidden_proto(strcpy) */
168 /* Experimentally off - libc_hidden_proto(strlen) */
169 /* Experimentally off - libc_hidden_proto(strncpy) */
170 /* libc_hidden_proto(sprintf) */
171 libc_hidden_proto(open)
172 libc_hidden_proto(read)
173 libc_hidden_proto(close)
174 libc_hidden_proto(getenv)
175 libc_hidden_proto(tzset)
176 libc_hidden_proto(gettimeofday)
177 /* Experimentally off - libc_hidden_proto(strncasecmp) */
178 libc_hidden_proto(strtol)
179 libc_hidden_proto(strtoul)
180 libc_hidden_proto(nl_langinfo)
182 #ifdef __UCLIBC_HAS_XLOCALE__
183 /* Experimentally off - libc_hidden_proto(strncasecmp_l) */
184 libc_hidden_proto(strtol_l)
185 libc_hidden_proto(strtoul_l)
186 libc_hidden_proto(nl_langinfo_l)
187 libc_hidden_proto(__ctype_b_loc)
188 #elif defined __UCLIBC_HAS_CTYPE_TABLES__
189 libc_hidden_proto(__ctype_b)
193 #define __isleap(y) ( !((y) % 4) && ( ((y) % 100) || !((y) % 400) ) )
197 #define TZNAME_MAX _POSIX_TZNAME_MAX
200 #if defined (L_tzset) || defined (L_localtime_r) || defined(L_strftime) || \
201 defined(L__time_mktime) || defined(L__time_mktime_tzi) || \
202 ((defined(L_strftime) || defined(L_strftime_l)) && \
203 defined(__UCLIBC_HAS_XLOCALE__))
205 void _time_tzset(int use_old_rules) attribute_hidden;
207 #ifndef L__time_mktime
209 /* Jan 1, 2007 Z - tm = 0,0,0,1,0,107,1,0,0 */
211 static const time_t new_rule_starts = 1167609600;
216 /**********************************************************************/
217 /* The era code is currently unfinished. */
218 /* #define ENABLE_ERA_CODE */
220 #define TZ_BUFLEN (2*TZNAME_MAX + 56)
222 #ifdef __UCLIBC_HAS_TZ_FILE__
224 #include <sys/stat.h>
226 /* ":<tzname>+hh:mm:ss<tzname>+hh:mm:ss,Mmm.w.d/hh:mm:ss,Mmm.w.d/hh:mm:ss" + nul */
227 /* 1 + 2*(1+TZNAME_MAX+1 + 9 + 7 + 9) + 1 = 2*TZNAME_MAX + 56 */
229 #else /* __UCLIBC_HAS_TZ_FILE__ */
231 /* Probably no longer needed. */
232 #undef __UCLIBC_HAS_TZ_FILE_READ_MANY__
234 #endif /* __UCLIBC_HAS_TZ_FILE__ */
236 /**********************************************************************/
238 extern struct tm __time_tm attribute_hidden;
243 short day; /* for J or normal */
246 short rule_type; /* J, M, \0 */
247 char tzname[TZNAME_MAX+1];
250 __UCLIBC_MUTEX_EXTERN(_time_tzlock);
252 extern rule_struct _time_tzinfo[2] attribute_hidden;
254 extern struct tm *_time_t2tm(const time_t *__restrict timer,
255 int offset, struct tm *__restrict result) attribute_hidden;
257 extern time_t _time_mktime(struct tm *timeptr, int store_on_success) attribute_hidden;
259 extern struct tm *__time_localtime_tzi(const time_t *__restrict timer,
260 struct tm *__restrict result,
261 rule_struct *tzi) attribute_hidden;
263 extern time_t _time_mktime_tzi(struct tm *timeptr, int store_on_success,
264 rule_struct *tzi) attribute_hidden;
266 /**********************************************************************/
269 static char __time_str[26];
271 char *asctime(const struct tm *ptm)
273 return asctime_r(ptm, __time_str);
275 libc_hidden_def(asctime)
278 /**********************************************************************/
281 /* Strictly speaking, this implementation isn't correct. ANSI/ISO specifies
282 * that the implementation of asctime() be equivalent to
284 * char *asctime(const struct tm *timeptr)
286 * static char wday_name[7][3] = {
287 * "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
289 * static char mon_name[12][3] = {
290 * "Jan", "Feb", "Mar", "Apr", "May", "Jun",
291 * "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
293 * static char result[26];
295 * sprintf(result, "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n",
296 * wday_name[timeptr->tm_wday],
297 * mon_name[timeptr->tm_mon],
298 * timeptr->tm_mday, timeptr->tm_hour,
299 * timeptr->tm_min, timeptr->tm_sec,
300 * 1900 + timeptr->tm_year);
304 * but the above is either inherently unsafe, or carries with it the implicit
305 * assumption that all fields of timeptr fall within their usual ranges, and
306 * that the tm_year value falls in the range [-2899,8099] to avoid overflowing
309 * If we take the implicit assumption as given, then the implementation below
310 * is still incorrect for tm_year values < -900, as there will be either
311 * 0-padding and/or a missing negative sign for the year conversion . But given
312 * the ususal use of asctime(), I think it isn't unreasonable to restrict correct
313 * operation to the domain of years between 1000 and 9999.
316 /* This is generally a good thing, but if you're _sure_ any data passed will be
317 * in range, you can #undef this. */
318 #define SAFE_ASCTIME_R 1
320 static const unsigned char at_data[] = {
321 'S', 'u', 'n', 'M', 'o', 'n', 'T', 'u', 'e', 'W', 'e', 'd',
322 'T', 'h', 'u', 'F', 'r', 'i', 'S', 'a', 't',
324 'J', 'a', 'n', 'F', 'e', 'b', 'M', 'a', 'r', 'A', 'p', 'r',
325 'M', 'a', 'y', 'J', 'u', 'n', 'J', 'u', 'l', 'A', 'u', 'g',
326 'S', 'e', 'p', 'O', 'c', 't', 'N', 'o', 'v', 'D', 'e', 'c',
328 #ifdef SAFE_ASCTIME_R
333 offsetof(struct tm, tm_mday),
335 offsetof(struct tm, tm_hour),
337 offsetof(struct tm, tm_min),
339 offsetof(struct tm, tm_sec),
340 ' ', '?', '?', '?', '?', '\n', 0
343 char *asctime_r(register const struct tm *__restrict ptm,
344 register char *__restrict buffer)
351 #ifdef SAFE_ASCTIME_R
352 memcpy(buffer, at_data + 3*(7 + 12), sizeof(at_data) - 3*(7 + 12));
354 if (((unsigned int)(ptm->tm_wday)) <= 6) {
355 memcpy(buffer, at_data + 3 * ptm->tm_wday, 3);
358 if (((unsigned int)(ptm->tm_mon)) <= 11) {
359 memcpy(buffer + 4, at_data + 3*7 + 3 * ptm->tm_mon, 3);
362 assert(((unsigned int)(ptm->tm_wday)) <= 6);
363 assert(((unsigned int)(ptm->tm_mon)) <= 11);
365 memcpy(buffer, at_data + 3*(7 + 12) - 3, sizeof(at_data) + 3 - 3*(7 + 12));
367 memcpy(buffer, at_data + 3 * ptm->tm_wday, 3);
368 memcpy(buffer + 4, at_data + 3*7 + 3 * ptm->tm_mon, 3);
371 #ifdef SAFE_ASCTIME_R
373 tmp = ptm->tm_year + 1900;
374 if (((unsigned int) tmp) < 10000) {
377 *buffer = '0' + (tmp % 10);
379 } while (*--buffer == '?');
381 /* Not sure if we should even bother ...
383 __set_errno(EOVERFLOW);
387 #else /* SAFE_ASCTIME_R */
389 tmp = ptm->tm_year + 1900;
390 assert( ((unsigned int) tmp) < 10000 );
391 /* Not sure if we should even bother ...
392 if ( ((unsigned int) tmp) >= 10000 ) {
393 __set_errno(EOVERFLOW);
398 *buffer = '0' + (tmp % 10);
400 } while (*--buffer == '?');
401 #endif /* SAFE_ASCTIME_R */
405 tmp = *((int *)(((const char *) ptm) + (int) *buffer));
406 #ifdef SAFE_ASCTIME_R
407 if (((unsigned int) tmp) >= 100) { /* Just check 2 digit non-neg. */
408 buffer[-1] = *buffer = '?';
410 #else /* SAFE_ASCTIME_R */
411 assert(((unsigned int) tmp) < 100); /* Just check 2 digit non-neg. */
412 #endif /* SAFE_ASCTIME_R */
414 *buffer = '0' + (tmp % 10);
416 buffer[-1] = '0' + (tmp/10);
418 buffer[-1] += (tmp/10);
421 } while ((buffer -= 2)[-2] == '0');
423 if (*++buffer == '0') { /* Space-pad day of month. */
429 libc_hidden_def(asctime_r)
432 /**********************************************************************/
435 #include <sys/times.h>
437 libc_hidden_proto(times)
440 #if CLOCKS_PER_SEC != 1000000L
441 #error unexpected value for CLOCKS_PER_SEC!
445 #ifdef __UCLIBC_CLK_TCK_CONST
446 # if __UCLIBC_CLK_TCK_CONST > CLOCKS_PER_SEC
447 # error __UCLIBC_CLK_TCK_CONST > CLOCKS_PER_SEC!
448 # elif __UCLIBC_CLK_TCK_CONST < 1
449 # error __UCLIBC_CLK_TCK_CONST < 1!
455 * On XSI-conformant systems, CLOCKS_PER_SEC is defined to be one million.
457 * The value returned by clock() may wrap around on some implementations.
458 * For example, on a machine with 32-bit values for clock_t, it wraps
459 * after 2147 seconds.
461 * This implies that we should bitwise and with LONG_MAX.
471 t = ((unsigned long) xtms.tms_utime) + xtms.tms_stime;
473 #ifndef __UCLIBC_CLK_TCK_CONST
475 # error __UCLIBC_CLK_TCK_CONST not defined!
477 #elif ((CLOCKS_PER_SEC % __UCLIBC_CLK_TCK_CONST) == 0)
479 /* CLOCKS_PER_SEC == k * __UCLIBC_CLK_TCK_CONST for some integer k >= 1. */
480 return ((t * (CLOCKS_PER_SEC/__UCLIBC_CLK_TCK_CONST)) & LONG_MAX);
484 /* Unlike the previous case, the scaling factor is not an integer.
485 * So when tms_utime, tms_stime, or their sum wraps, some of the
486 * "visible" bits in the return value are affected. Nothing we
487 * can really do about this though other than handle tms_utime and
488 * tms_stime seperately and then sum. But since that doesn't really
489 * buy us much, we don't bother. */
491 return ((((t / __UCLIBC_CLK_TCK_CONST) * CLOCKS_PER_SEC)
492 + ((((t % __UCLIBC_CLK_TCK_CONST) * CLOCKS_PER_SEC)
493 / __UCLIBC_CLK_TCK_CONST))
500 /**********************************************************************/
503 char *ctime(const time_t *t)
505 /* ANSI/ISO/SUSv3 say that ctime is equivalent to the following. */
506 return asctime(localtime(t));
508 libc_hidden_def(ctime)
510 /**********************************************************************/
513 char *ctime_r(const time_t *t, char *buf)
517 return asctime_r(localtime_r(t, &xtm), buf);
521 /**********************************************************************/
527 #error difftime implementation assumptions violated for you arch!
530 double difftime(time_t time1, time_t time0)
532 #if (LONG_MAX >> DBL_MANT_DIG) == 0
534 /* time_t fits in the mantissa of a double. */
535 return ((double) time1) - time0;
537 #elif ((LONG_MAX >> DBL_MANT_DIG) >> DBL_MANT_DIG) == 0
539 /* time_t can overflow the mantissa of a double. */
542 d = ((time_t) 1) << DBL_MANT_DIG;
548 /* Since FLT_RADIX==2 and d is a power of 2, the only possible
549 * rounding error in the expression below would occur from the
551 return (((double) t1) - t0) * d + (((double) time1) - time0);
554 #error difftime needs special implementation on your arch.
559 /**********************************************************************/
562 struct tm *gmtime(const time_t *timer)
564 register struct tm *ptm = &__time_tm;
566 _time_t2tm(timer, 0, ptm); /* Can return NULL... */
572 /**********************************************************************/
575 struct tm *gmtime_r(const time_t *__restrict timer,
576 struct tm *__restrict result)
578 return _time_t2tm(timer, 0, result);
582 /**********************************************************************/
585 struct tm *localtime(const time_t *timer)
587 register struct tm *ptm = &__time_tm;
589 /* In this implementation, tzset() is called by localtime_r(). */
591 localtime_r(timer, ptm); /* Can return NULL... */
595 libc_hidden_def(localtime)
598 /**********************************************************************/
601 struct tm *localtime_r(register const time_t *__restrict timer,
602 register struct tm *__restrict result)
604 __UCLIBC_MUTEX_LOCK(_time_tzlock);
606 _time_tzset(*timer < new_rule_starts);
608 __time_localtime_tzi(timer, result, _time_tzinfo);
610 __UCLIBC_MUTEX_UNLOCK(_time_tzlock);
614 libc_hidden_def(localtime_r)
617 /**********************************************************************/
618 #ifdef L__time_localtime_tzi
620 #ifdef __UCLIBC_HAS_TM_EXTENSIONS__
622 /* Experimentally off - libc_hidden_proto(strnlen) */
624 struct ll_tzname_item;
626 typedef struct ll_tzname_item {
627 struct ll_tzname_item *next;
628 char tzname[TZNAME_MAX+1];
631 static ll_tzname_item_t ll_tzname[] = {
632 { ll_tzname + 1, "UTC" }, /* Always 1st. */
633 { NULL, "???" } /* Always 2nd. (invalid or out-of-memory) */
636 static const char *lookup_tzname(const char *key)
640 for (p=ll_tzname ; p ; p=p->next) {
641 if (!strcmp(p->tzname, key)) {
646 /* Hmm... a new name. */
647 if (strnlen(key, TZNAME_MAX+1) < TZNAME_MAX+1) { /* Verify legal length */
648 if ((p = malloc(sizeof(ll_tzname_item_t))) != NULL) {
649 /* Insert as 3rd item in the list. */
650 p->next = ll_tzname[1].next;
651 ll_tzname[1].next = p;
652 strcpy(p->tzname, key);
657 /* Either invalid or couldn't alloc. */
658 return ll_tzname[1].tzname;
661 #endif /* __UCLIBC_HAS_TM_EXTENSIONS__ */
663 static const unsigned char day_cor[] = { /* non-leap */
664 31, 31, 34, 34, 35, 35, 36, 36, 36, 37, 37, 38, 38
665 /* 0, 0, 3, 3, 4, 4, 5, 5, 5, 6, 6, 7, 7 */
666 /* 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 */
669 /* Note: timezone locking is done by localtime_r. */
671 static int tm_isdst(register const struct tm *__restrict ptm,
672 register rule_struct *r)
675 int i, isdst, isleap, day, day0, monlen, mday;
676 int oday = oday; /* ok to be uninitialized, shutting up compiler warning */
679 if (r[1].tzname[0] != 0) {
680 /* First, get the current seconds offset from the start of the year.
681 * Fields of ptm are assumed to be in their normal ranges. */
684 + 60 * (long)(ptm->tm_hour
685 + 24 * ptm->tm_yday));
686 /* Do some prep work. */
687 i = (ptm->tm_year % 400) + 1900; /* Make sure we don't overflow. */
688 isleap = __isleap(i);
691 + i /* Normal years increment 1 wday. */
697 day = r->day; /* Common for 'J' and # case. */
698 if (r->rule_type == 'J') {
699 if (!isleap || (day < (31+29))) {
702 } else if (r->rule_type == 'M') {
703 /* Find 0-based day number for 1st of the month. */
704 day = 31*r->month - day_cor[r->month -1];
705 if (isleap && (day >= 59)) {
708 monlen = 31 + day_cor[r->month -1] - day_cor[r->month];
709 if (isleap && (r->month > 1)) {
712 /* Wweekday (0 is Sunday) of 1st of the month
713 * is (day0 + day) % 7. */
714 if ((mday = r->day - ((day0 + day) % 7)) >= 0) {
715 mday -= 7; /* Back up into prev month since r->week>0. */
717 if ((mday += 7 * r->week) >= monlen) {
720 /* So, 0-based day number is... */
725 /* Adjust sec since dst->std change time is in dst. */
726 sec += (r[-1].gmt_offset - r->gmt_offset);
728 ++isdst; /* Year starts in dst. */
733 /* Now convert day to seconds and add offset and compare. */
734 if (sec >= (day * 86400L) + r->dst_offset) {
744 struct tm attribute_hidden *__time_localtime_tzi(register const time_t *__restrict timer,
745 register struct tm *__restrict result,
755 offset = 604800L - tzi[dst].gmt_offset;
756 if (*timer > (LONG_MAX - 604800L)) {
760 *x = *timer + offset;
762 _time_t2tm(x, days, result);
763 result->tm_isdst = dst;
764 #ifdef __UCLIBC_HAS_TM_EXTENSIONS__
766 result->tm_gmtoff = - tzi[dst].gmt_offset;
767 result->tm_zone = lookup_tzname(tzi[dst].tzname);
769 result->__tm_gmtoff = - tzi[dst].gmt_offset;
770 result->__tm_zone = lookup_tzname(tzi[dst].tzname);
772 #endif /* __UCLIBC_HAS_TM_EXTENSIONS__ */
774 && ((result->tm_isdst = tm_isdst(result, tzi)) != 0));
780 /**********************************************************************/
783 time_t mktime(struct tm *timeptr)
785 return _time_mktime(timeptr, 1);
788 /* Another name for `mktime'. */
789 /* time_t timelocal(struct tm *tp) */
790 strong_alias(mktime,timelocal)
793 /**********************************************************************/
795 /* Like `mktime' but timeptr represents Universal Time, not local time. */
797 time_t timegm(struct tm *timeptr)
799 rule_struct gmt_tzinfo[2];
801 memset(gmt_tzinfo, 0, sizeof(gmt_tzinfo));
802 strcpy(gmt_tzinfo[0].tzname, "GMT"); /* Match glibc behavior here. */
804 return _time_mktime_tzi(timeptr, 1, gmt_tzinfo);
808 /**********************************************************************/
809 #if defined(L_strftime) || defined(L_strftime_l)
811 #if defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE)
813 libc_hidden_proto(strftime)
815 libc_hidden_proto(strftime_l)
817 size_t strftime(char *__restrict s, size_t maxsize,
818 const char *__restrict format,
819 const struct tm *__restrict timeptr)
821 return strftime_l(s, maxsize, format, timeptr, __UCLIBC_CURLOCALE);
823 libc_hidden_def(strftime)
825 #else /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */
827 #define NO_E_MOD 0x80
828 #define NO_O_MOD 0x40
830 #define ILLEGAL_SPEC 0x3f
832 #define INT_SPEC 0x00 /* must be 0x00!! */
833 #define STRING_SPEC 0x10 /* must be 0x10!! */
834 #define CALC_SPEC 0x20
835 #define STACKED_SPEC 0x30
837 #define MASK_SPEC 0x30
841 * No alternate digit (%O?) handling. Always uses 0-9.
842 * Alternate locale format (%E?) handling is broken for nontrivial ERAs.
843 * glibc's %P is currently faked by %p. This means it doesn't do lower case.
844 * glibc's %k, %l, and %s are handled.
845 * glibc apparently allows (and ignores) extraneous 'E' and 'O' modifiers,
846 * while they are flagged as illegal conversions here.
849 /* Warning: Assumes ASCII values! (as do lots of other things in the lib...) */
850 static const unsigned char spec[] = {
851 /* A */ 0x03 | STRING_SPEC | NO_E_MOD | NO_O_MOD,
852 /* B */ 0x04 | STRING_SPEC | NO_E_MOD | NO_O_MOD,
853 /* C */ 0x0a | INT_SPEC | NO_O_MOD,
854 /* D */ 0x02 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
855 /* E */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
856 /* F */ 0x03 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
857 /* G */ 0x03 | CALC_SPEC | NO_E_MOD | NO_O_MOD,
858 /* H */ 0x0b | INT_SPEC | NO_E_MOD,
859 /* I */ 0x0c | INT_SPEC | NO_E_MOD,
860 /* J */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
861 /* K */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
862 /* L */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
863 /* M */ 0x0d | INT_SPEC | NO_E_MOD,
864 /* N */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
865 /* O */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
866 /* P */ 0x05 | STRING_SPEC | NO_E_MOD | NO_O_MOD, /* glibc ; use %p */
867 /* Q */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
868 /* R */ 0x04 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
869 /* S */ 0x0e | INT_SPEC | NO_E_MOD,
870 /* T */ 0x05 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
871 /* U */ 0x04 | CALC_SPEC | NO_E_MOD,
872 /* V */ 0x05 | CALC_SPEC | NO_E_MOD,
873 /* W */ 0x06 | CALC_SPEC | NO_E_MOD,
874 /* X */ 0x0a | STACKED_SPEC | NO_O_MOD,
875 /* Y */ 0x0f | INT_SPEC | NO_O_MOD,
876 /* Z */ 0x01 | CALC_SPEC | NO_E_MOD | NO_O_MOD,
883 /* a */ 0x00 | STRING_SPEC | NO_E_MOD | NO_O_MOD,
884 /* b */ 0x01 | STRING_SPEC | NO_E_MOD | NO_O_MOD,
885 /* c */ 0x08 | STACKED_SPEC | NO_O_MOD,
886 /* d */ 0x00 | INT_SPEC | NO_E_MOD,
887 /* e */ 0x01 | INT_SPEC | NO_E_MOD,
888 /* f */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
889 /* g */ 0x02 | CALC_SPEC | NO_E_MOD | NO_O_MOD,
890 /* h */ 0x01 | STRING_SPEC | NO_E_MOD | NO_O_MOD, /* same as b */
891 /* i */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
892 /* j */ 0x08 | INT_SPEC | NO_E_MOD | NO_O_MOD,
893 /* k */ 0x03 | INT_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
894 /* l */ 0x04 | INT_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
895 /* m */ 0x05 | INT_SPEC | NO_E_MOD,
896 /* n */ 0x00 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
897 /* o */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
898 /* p */ 0x02 | STRING_SPEC | NO_E_MOD | NO_O_MOD,
899 /* q */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
900 /* r */ 0x0b | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
901 /* s */ 0x07 | CALC_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
902 /* t */ 0x01 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
903 /* u */ 0x07 | INT_SPEC | NO_E_MOD,
904 /* v */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
905 /* w */ 0x02 | INT_SPEC | NO_E_MOD,
906 /* x */ 0x09 | STACKED_SPEC | NO_O_MOD,
907 /* y */ 0x09 | INT_SPEC,
908 /* z */ 0x00 | CALC_SPEC | NO_E_MOD | NO_O_MOD,
911 /* WARNING!!! These are dependent on the layout of struct tm!!! */
912 #define FIELD_MAX (26+6+26)
913 60 /* 61? */, 59, 23, 31, 11, 0 /* 9999 */, 6, 0 /* 365 */,
915 #define TP_OFFSETS (FIELD_MAX+8)
922 0, /* CURRENTLY UNUSED */
923 /* NOTE: u,j,y order must be preserved as 6,7,5 seq is used in the code! */
924 #define CALC_OFFSETS (TP_OFFSETS + 7)
941 #define TP_CODES (TP_OFFSETS + 16 + 6)
948 0, /* CURRENTLY UNUSED */
951 2 | 128 | 32 | 16 , /* y */
952 2 | 128 | 64 | 32 | 16 , /* C */
954 2 | 32 | 16 | 0, /* I */
965 #define STRINGS_NL_ITEM_START (TP_CODES + 16 + 6)
966 _NL_ITEM_INDEX(ABDAY_1), /* a */
967 _NL_ITEM_INDEX(ABMON_1), /* b, h */
968 _NL_ITEM_INDEX(AM_STR), /* p */
969 _NL_ITEM_INDEX(DAY_1), /* A */
970 _NL_ITEM_INDEX(MON_1), /* B */
971 _NL_ITEM_INDEX(AM_STR), /* P -- wrong! need lower case */
973 #define STACKED_STRINGS_START (STRINGS_NL_ITEM_START+6)
974 6, 7, 8, 16, 24, 29, /* 6 - offsets from offset-count to strings */
977 '%', 'm', '/', '%', 'd', '/', '%', 'y', 0, /* 9 - %D */
978 '%', 'Y', '-', '%', 'm', '-', '%', 'd', 0, /* 9 - %F (glibc extension) */
979 '%', 'H', ':', '%', 'M', 0, /* 6 - %R*/
980 '%', 'H', ':', '%', 'M', ':', '%', 'S', 0, /* 9 - %T */
982 #define STACKED_STRINGS_NL_ITEM_START (STACKED_STRINGS_START + 43)
983 _NL_ITEM_INDEX(D_T_FMT), /* c */
984 _NL_ITEM_INDEX(D_FMT), /* x */
985 _NL_ITEM_INDEX(T_FMT), /* X */
986 _NL_ITEM_INDEX(T_FMT_AMPM), /* r */
987 #ifdef ENABLE_ERA_CODE
988 _NL_ITEM_INDEX(ERA_D_T_FMT), /* Ec */
989 _NL_ITEM_INDEX(ERA_D_FMT), /* Ex */
990 _NL_ITEM_INDEX(ERA_T_FMT), /* EX */
994 static int load_field(int k, const struct tm *__restrict timeptr)
999 r = ((int *) timeptr)[k];
1001 r_max = spec[FIELD_MAX + k];
1005 } else if (k == 5) {
1010 if ((((unsigned int) r) > r_max) || ((k == 3) && !r)) {
1019 #ifdef __UCLIBC_MJN3_ONLY__
1020 #warning TODO: Check multibyte format string validity.
1023 libc_hidden_proto(__XL_NPP(strftime))
1024 size_t __XL_NPP(strftime)(char *__restrict s, size_t maxsize,
1025 const char *__restrict format,
1026 const struct tm *__restrict timeptr __LOCALE_PARAM )
1029 register const char *p;
1030 register const char *o;
1031 #ifndef __UCLIBC_HAS_TM_EXTENSIONS__
1032 const rule_struct *rsp;
1034 const char *stack[MAX_PUSH];
1037 int field_val, i, j, lvl;
1038 int x[3]; /* wday, yday, year */
1040 char buf[__UIM_BUFLEN_LONG];
1044 /* We'll, let's get this out of the way. */
1045 _time_tzset(_time_mktime((struct tm *) timeptr, 0) < new_rule_starts);
1057 *s = 0; /* nul-terminate */
1058 return maxsize - count;
1065 if ((*(o = p) == '%') && (*++p != '%')) {
1068 if ((*p == 'O') || (*p == 'E')) { /* modifier */
1069 mod |= ((*p == 'O') ? NO_O_MOD : NO_E_MOD);
1073 if ((((unsigned char)(((*p) | 0x20) - 'a')) >= 26)
1074 || (((code = spec[(int)(*p - 'A')]) & mod) >= ILLEGAL_SPEC)
1082 code &= ILLEGAL_SPEC; /* modifiers are preserved in mod var. */
1084 if ((code & MASK_SPEC) == STACKED_SPEC) {
1085 if (lvl == MAX_PUSH) {
1086 goto OUTPUT; /* Stack full so treat as illegal spec. */
1089 if ((code &= 0xf) < 8) {
1090 p = ((const char *) spec) + STACKED_STRINGS_START + code;
1091 p += *((unsigned char *)p);
1094 p = ((const char *) spec) + STACKED_STRINGS_NL_ITEM_START
1096 #ifdef ENABLE_ERA_CODE
1097 if ((mod & NO_E_MOD) /* Actually, this means E modifier present. */
1098 && (*(o = __XL_NPP(nl_langinfo)(_NL_ITEM(LC_TIME,
1099 (int)(((unsigned char *)p)[4]))
1107 p = __XL_NPP(nl_langinfo)(_NL_ITEM(LC_TIME,
1108 (int)(*((unsigned char *)p)))
1114 o = ((const char *) spec) + 26; /* set to "????" */
1115 if ((code & MASK_SPEC) == CALC_SPEC) {
1120 /* Use a cast to silence the warning since *timeptr won't
1122 if ((t = _time_mktime((struct tm *) timeptr, 0))
1128 #ifdef TIME_T_IS_UNSIGNED
1129 o = _uintmaxtostr(buf + sizeof(buf) - 1,
1133 o = _uintmaxtostr(buf + sizeof(buf) - 1,
1135 -10, __UIM_DECIMAL);
1137 o_count = sizeof(buf);
1139 } else if (((*p) | 0x20) == 'z') { /* 'z' or 'Z' */
1141 if (timeptr->tm_isdst < 0) {
1142 /* SUSv3 specifies this behavior for 'z', but we'll also
1143 * treat it as "no timezone info" for 'Z' too. */
1148 #ifdef __UCLIBC_HAS_TM_EXTENSIONS__
1151 # define RSP_TZNAME timeptr->tm_zone
1152 # define RSP_GMT_OFFSET (-timeptr->tm_gmtoff)
1154 # define RSP_TZNAME timeptr->__tm_zone
1155 # define RSP_GMT_OFFSET (-timeptr->__tm_gmtoff)
1160 #define RSP_TZNAME rsp->tzname
1161 #define RSP_GMT_OFFSET rsp->gmt_offset
1163 __UCLIBC_MUTEX_LOCK(_time_tzlock);
1166 if (timeptr->tm_isdst > 0) {
1173 #ifdef __UCLIBC_HAS_TM_EXTENSIONS__
1174 /* Sigh... blasted glibc extensions. Of course we can't
1175 * count on the pointer being valid. Best we can do is
1176 * handle NULL, which looks to be all that glibc does.
1177 * At least that catches the memset() with 0 case.
1178 * NOTE: We handle this case differently than glibc!
1179 * It uses system timezone name (based on tm_isdst) in this
1180 * case... although it always seems to use the embedded
1181 * tm_gmtoff value. What we'll do instead is treat the
1182 * timezone name as unknown/invalid and return "???". */
1189 if (!o) { /* PARANOIA */
1190 o = spec+30; /* empty string */
1194 #ifdef __UCLIBC_HAS_TM_EXTENSIONS__
1199 if ((tzo = -RSP_GMT_OFFSET) < 0) {
1207 field_val = ((i / 60) * 100) + (i % 60);
1209 i = 16 + 6; /* 0-fill, width = 4 */
1211 #ifdef __UCLIBC_HAS_TM_EXTENSIONS__
1213 __UCLIBC_MUTEX_UNLOCK(_time_tzlock);
1219 /* TODO: don't need year for U, W */
1220 for (i=0 ; i < 3 ; i++) {
1221 if ((x[i] = load_field(spec[CALC_OFFSETS+i],timeptr)) < 0) {
1226 i = 16 + 2; /* 0-fill, width = 2 */
1228 if ((*p == 'U') || (*p == 'W')) {
1229 field_val = ((x[1] - x[0]) + 7);
1234 if ((*p == 'W') && !x[0]) {
1237 } else { /* ((*p == 'g') || (*p == 'G') || (*p == 'V')) */
1239 isofm = (((x[1] - x[0]) + 11) % 7) - 3; /* [-3,3] */
1241 if (x[1] < isofm) { /* belongs to previous year */
1243 x[1] += 365 + __isleap(x[2]);
1247 field_val = ((x[1] - isofm) / 7) + 1; /* week # */
1248 days = 365 + __isleap(x[2]);
1249 isofm = ((isofm + 7*53 + 3 - days)) %7 + days - 3; /* next year */
1250 if (x[1] >= isofm) { /* next year */
1256 if (*p != 'V') { /* need year */
1257 field_val = x[2]; /* TODO: what if x[2] now 10000 ?? */
1261 i = 16 + 6; /* 0-fill, width = 4 */
1267 i = TP_OFFSETS + (code & 0x1f);
1268 if ((field_val = load_field(spec[i],timeptr)) < 0) {
1272 i = spec[i+(TP_CODES - TP_OFFSETS)];
1274 j = (i & 128) ? 100: 12;
1280 if (((i&128) + field_val) == 0) { /* mod 12? == 0 */
1281 field_val = j; /* set to 12 */
1284 field_val += (i & 1);
1285 if ((i & 8) && !field_val) {
1290 if ((code & MASK_SPEC) == STRING_SPEC) {
1292 field_val += spec[STRINGS_NL_ITEM_START + (code & 0xf)];
1293 o = __XL_NPP(nl_langinfo)(_NL_ITEM(LC_TIME, field_val) __LOCALE_ARG );
1295 o_count = ((i >> 1) & 3) + 1;
1298 *(char *)(--o) = '0' + (field_val % 10);
1302 *buf = ' ' + (i & 16);
1309 while (o_count && count && *o) {
1316 libc_hidden_def(__XL_NPP(strftime))
1318 #endif /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */
1321 /**********************************************************************/
1322 #if defined(L_strptime) || defined(L_strptime_l)
1324 #define ISDIGIT(C) __isdigit_char((C))
1326 #ifdef __UCLIBC_DO_XLOCALE
1327 #define ISSPACE(C) isspace_l((C), locale_arg)
1329 #define ISSPACE(C) isspace((C))
1332 #if defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE)
1334 libc_hidden_proto(strptime)
1336 libc_hidden_proto(strptime_l)
1338 char *strptime(const char *__restrict buf, const char *__restrict format,
1339 struct tm *__restrict tm)
1341 return strptime_l(buf, format, tm, __UCLIBC_CURLOCALE);
1343 libc_hidden_def(strptime)
1345 #else /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */
1348 * 1) %l and %k are space-padded, so "%l" by itself fails while " %l" succeeds.
1349 * Both work for glibc. So, should we always strip spaces?
1354 * There are several differences between this strptime and glibc's strptime.
1355 * 1) glibc strips leading space before numeric conversions.
1356 * 2) glibc will read fields without whitespace in between. SUSv3 states
1357 * that you must have whitespace between conversion operators. Besides,
1358 * how do you know how long a number should be if there are leading 0s?
1359 * 3) glibc attempts to compute some the struct tm fields based on the
1360 * data retrieved; tm_wday in particular. I don't as I consider it
1361 * another glibc attempt at mind-reading...
1364 #define NO_E_MOD 0x80
1365 #define NO_O_MOD 0x40
1367 #define ILLEGAL_SPEC 0x3f
1369 #define INT_SPEC 0x00 /* must be 0x00!! */
1370 #define STRING_SPEC 0x10 /* must be 0x10!! */
1371 #define CALC_SPEC 0x20
1372 #define STACKED_SPEC 0x30
1374 #define MASK_SPEC 0x30
1376 /* Warning: Assumes ASCII values! (as do lots of other things in the lib...) */
1377 static const unsigned char spec[] = {
1378 /* A */ 0x02 | STRING_SPEC | NO_E_MOD | NO_O_MOD,
1379 /* B */ 0x01 | STRING_SPEC | NO_E_MOD | NO_O_MOD,
1380 /* C */ 0x08 | INT_SPEC | NO_O_MOD,
1381 /* D */ 0x01 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
1382 /* E */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1383 /* F */ 0x02 | STACKED_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
1384 /* G */ 0x0f | INT_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
1385 /* H */ 0x06 | INT_SPEC | NO_E_MOD,
1386 /* I */ 0x07 | INT_SPEC | NO_E_MOD,
1387 /* J */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1388 /* K */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1389 /* L */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1390 /* M */ 0x04 | INT_SPEC | NO_E_MOD,
1391 /* N */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1392 /* O */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1393 /* P */ 0x00 | STRING_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
1394 /* Q */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1395 /* R */ 0x03 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
1396 /* S */ 0x05 | INT_SPEC | NO_E_MOD,
1397 /* T */ 0x04 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
1398 /* U */ 0x0c | INT_SPEC | NO_E_MOD,
1399 /* V */ 0x0d | INT_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
1400 /* W */ 0x0c | INT_SPEC | NO_E_MOD,
1401 /* X */ 0x0a | STACKED_SPEC | NO_O_MOD,
1402 /* Y */ 0x0a | INT_SPEC | NO_O_MOD,
1403 /* Z */ 0x02 | CALC_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
1405 /* WARNING! This assumes orderings:
1407 * ABDAY_1-ABDAY-7,DAY_1-DAY_7
1408 * ABMON_1-ABMON_12,MON_1-MON12
1409 * Also, there are exactly 6 bytes between 'Z' and 'a'.
1411 #define STRINGS_NL_ITEM_START (26)
1412 _NL_ITEM_INDEX(AM_STR), /* p (P) */
1413 _NL_ITEM_INDEX(ABMON_1), /* B, b */
1414 _NL_ITEM_INDEX(ABDAY_1), /* A, a */
1419 /* a */ 0x02 | STRING_SPEC | NO_E_MOD | NO_O_MOD,
1420 /* b */ 0x01 | STRING_SPEC | NO_E_MOD | NO_O_MOD,
1421 /* c */ 0x08 | STACKED_SPEC | NO_O_MOD,
1422 /* d */ 0x00 | INT_SPEC | NO_E_MOD,
1423 /* e */ 0x00 | INT_SPEC | NO_E_MOD,
1424 /* f */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1425 /* g */ 0x0e | INT_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
1426 /* h */ 0x01 | STRING_SPEC | NO_E_MOD | NO_O_MOD,
1427 /* i */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1428 /* j */ 0x01 | INT_SPEC | NO_E_MOD | NO_O_MOD,
1429 /* k */ 0x06 | INT_SPEC | NO_E_MOD, /* glibc */
1430 /* l */ 0x07 | INT_SPEC | NO_E_MOD, /* glibc */
1431 /* m */ 0x02 | INT_SPEC | NO_E_MOD,
1432 /* n */ 0x00 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
1433 /* o */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1434 /* p */ 0x00 | STRING_SPEC | NO_E_MOD | NO_O_MOD,
1435 /* q */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1436 /* r */ 0x0b | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
1437 /* s */ 0x00 | CALC_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
1438 /* t */ 0x00 | STACKED_SPEC | NO_E_MOD | NO_O_MOD,
1439 /* u */ 0x0b | INT_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
1440 /* v */ ILLEGAL_SPEC | NO_E_MOD | NO_O_MOD,
1441 /* w */ 0x03 | INT_SPEC | NO_E_MOD,
1442 /* x */ 0x09 | STACKED_SPEC | NO_O_MOD,
1443 /* y */ 0x09 | INT_SPEC,
1444 /* z */ 0x01 | CALC_SPEC | NO_E_MOD | NO_O_MOD, /* glibc */
1446 #define INT_FIELD_START (26+6+26)
1447 /* (field #) << 3 + lower bound (0|1) + correction 0:none, 2:-1, 4:-1900
1448 * followed by upper bound prior to correction with 1=>366 and 2=>9999. */
1449 /* d, e */ (3 << 3) + 1 + 0, 31,
1450 /* j */ (7 << 3) + 1 + 2, /* 366 */ 1,
1451 /* m */ (4 << 3) + 1 + 2, 12,
1452 /* w */ (6 << 3) + 0 + 0, 6,
1453 /* M */ (1 << 3) + 0 + 0, 59,
1454 /* S */ 0 + 0 + 0, 60,
1455 /* H (k) */ (2 << 3) + 0 + 0, 23,
1456 /* I (l) */ (9 << 3) + 1 + 0, 12, /* goes with 8 -- am/pm */
1457 /* C */ (10<< 3) + 0 + 0, 99,
1458 /* y */ (11<< 3) + 0 + 0, 99,
1459 /* Y */ (5 << 3) + 0 + 4, /* 9999 */ 2,
1460 /* u */ (6 << 3) + 1 + 0, 7,
1461 /* The following are processed and range-checked, but ignored otherwise. */
1462 /* U, W */ (12<< 3) + 0 + 0, 53,
1463 /* V */ (12<< 3) + 1 + 0, 53,
1464 /* g */ (12<< 3) + 0 + 0, 99,
1465 /* G */ (12<< 3) + 0 /*+ 4*/, /* 9999 */ 2, /* Note: -1 or 10000? */
1467 #define STACKED_STRINGS_START (INT_FIELD_START+32)
1468 5, 6, 14, 22, 27, /* 5 - offsets from offset-count to strings */
1469 ' ', 0, /* 2 - %n or %t */
1470 '%', 'm', '/', '%', 'd', '/', '%', 'y', 0, /* 9 - %D */
1471 '%', 'Y', '-', '%', 'm', '-', '%', 'd', 0, /* 9 - %F (glibc extension) */
1472 '%', 'H', ':', '%', 'M', 0, /* 6 - %R*/
1473 '%', 'H', ':', '%', 'M', ':', '%', 'S', 0, /* 9 - %T */
1475 #define STACKED_STRINGS_NL_ITEM_START (STACKED_STRINGS_START + 40)
1476 _NL_ITEM_INDEX(D_T_FMT), /* c */
1477 _NL_ITEM_INDEX(D_FMT), /* x */
1478 _NL_ITEM_INDEX(T_FMT), /* X */
1479 _NL_ITEM_INDEX(T_FMT_AMPM), /* r */
1480 #ifdef ENABLE_ERA_CODE
1481 _NL_ITEM_INDEX(ERA_D_T_FMT), /* Ec */
1482 _NL_ITEM_INDEX(ERA_D_FMT), /* Ex */
1483 _NL_ITEM_INDEX(ERA_T_FMT), /* EX */
1489 libc_hidden_proto(__XL_NPP(strptime))
1490 char *__XL_NPP(strptime)(const char *__restrict buf, const char *__restrict format,
1491 struct tm *__restrict tm __LOCALE_PARAM)
1493 register const char *p;
1495 const char *stack[MAX_PUSH];
1503 fields[i] = INT_MIN;
1511 if (lvl == 0) { /* Done. */
1512 if (fields[6] == 7) { /* Cleanup for %u here since just once. */
1513 fields[6] = 0; /* Don't use mod in case unset. */
1517 do { /* Store the values into tm. */
1518 if (fields[i] != INT_MIN) {
1519 ((int *) tm)[i] = fields[i];
1523 return (char *) buf; /* Success. */
1529 if ((*p == '%') && (*++p != '%')) {
1531 if ((*p == 'O') || (*p == 'E')) { /* Modifier? */
1532 mod |= ((*p == 'O') ? NO_O_MOD : NO_E_MOD);
1537 || (((unsigned char)(((*p) | 0x20) - 'a')) >= 26)
1538 || (((code = spec[(int)(*p - 'A')]) & mod) >= ILLEGAL_SPEC)
1540 return NULL; /* Illegal spec. */
1543 if ((code & MASK_SPEC) == STACKED_SPEC) {
1544 if (lvl == MAX_PUSH) {
1545 return NULL; /* Stack full so treat as illegal spec. */
1548 if ((code &= 0xf) < 8) {
1549 p = ((const char *) spec) + STACKED_STRINGS_START + code;
1550 p += *((unsigned char *)p);
1554 p = ((const char *) spec) + STACKED_STRINGS_NL_ITEM_START
1556 #ifdef ENABLE_ERA_CODE
1557 if ((mod & NO_E_MOD) /* Actually, this means E modifier present. */
1558 && (*(o = __XL_NPP(nl_langinfo)(_NL_ITEM(LC_TIME,
1559 (int)(((unsigned char *)p)[4]))
1567 p = __XL_NPP(nl_langinfo)(_NL_ITEM(LC_TIME,
1568 (int)(*((unsigned char *)p)))
1575 if ((code & MASK_SPEC) == STRING_SPEC) {
1577 j = spec[STRINGS_NL_ITEM_START + 3 + code];
1578 i = _NL_ITEM(LC_TIME, spec[STRINGS_NL_ITEM_START + code]);
1579 /* Go backwards to check full names before abreviations. */
1582 o = __XL_NPP(nl_langinfo)(i+j __LOCALE_ARG);
1583 if (!__XL_NPP(strncasecmp)(buf,o,strlen(o) __LOCALE_ARG) && *o) {
1584 do { /* Found a match. */
1587 if (!code) { /* am/pm */
1589 if (fields[9] >= 0) { /* We have a previous %I or %l. */
1590 fields[2] = fields[9] + fields[8];
1592 } else { /* day (4) or month (6) */
1593 fields[2 + (code << 1)]
1594 = j % (spec[STRINGS_NL_ITEM_START + 3 + code] >> 1);
1599 return NULL; /* Failed to match. */
1602 if ((code & MASK_SPEC) == CALC_SPEC) {
1603 if ((code &= 0xf) < 1) { /* s or z*/
1609 if (!ISSPACE(*buf)) { /* Signal an error if whitespace. */
1610 #ifdef TIME_T_IS_UNSIGNED
1611 t = __XL_NPP(strtoul)(buf, &o, 10 __LOCALE_ARG);
1613 t = __XL_NPP(strtol)(buf, &o, 10 __LOCALE_ARG);
1616 if ((o == buf) || errno) { /* Not a number or overflow. */
1619 __set_errno(i); /* Restore errno. */
1622 if (!code) { /* s */
1623 localtime_r(&t, tm); /* TODO: check for failure? */
1625 do { /* Now copy values from tm to fields. */
1626 fields[i] = ((int *) tm)[i];
1630 /* TODO: glibc treats %Z as a nop. For now, do the same. */
1634 assert((code & MASK_SPEC) == INT_SPEC);
1636 register const unsigned char *x;
1638 x = spec + INT_FIELD_START + (code << 1);
1639 if ((j = x[1]) < 3) { /* upper bound (inclusive) */
1640 j = ((j==1) ? 366 : 9999);
1643 while (ISDIGIT(*buf)) {
1647 if ((i = 10*i + (*buf - '0')) > j) { /* Overflow. */
1652 if (i < (*x & 1)) { /* This catches no-digit case too. */
1662 if (*x == (9 << 3) + 1 + 0) { /* %I or %l */
1666 if (fields[8] >= 0) { /* We have a previous %p or %P. */
1667 fields[2] = i + fields[8];
1671 fields[(*x) >> 3] = i;
1673 if (((unsigned char)(*x - (10<< 3) + 0 + 0)) <= 8) { /* %C or %y */
1674 if ((j = fields[10]) < 0) { /* No %C, so i must be %y data. */
1675 if (i <= 68) { /* Map [0-68] to 2000+i */
1678 } else { /* Have %C data, but what about %y? */
1679 if ((i = fields[11]) < 0) { /* No %y data. */
1680 i = 0; /* Treat %y val as 0 following glibc's example. */
1688 } else if (ISSPACE(*p)) {
1690 while (ISSPACE(*buf)) {
1694 } else if (*buf++ == *p++) {
1699 libc_hidden_def(__XL_NPP(strptime))
1701 #endif /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */
1704 /**********************************************************************/
1708 #error The uClibc version of time is in sysdeps/linux/common.
1711 time_t time(register time_t *tloc)
1714 register struct timeval *p = &tv;
1716 gettimeofday(p, NULL); /* This should never fail... */
1726 /**********************************************************************/
1729 static const char vals[] = {
1730 'T', 'Z', 0, /* 3 */
1731 'U', 'T', 'C', 0, /* 4 */
1732 25, 60, 60, 1, /* 4 */
1735 6, 0, 0, /* Note: overloaded for non-M non-J case... */
1737 ',', 'M', '4', '.', '1', '.', '0',
1738 ',', 'M', '1', '0', '.', '5', '.', '0', 0,
1739 ',', 'M', '3', '.', '2', '.', '0',
1740 ',', 'M', '1', '1', '.', '1', '.', '0', 0
1744 #define UTC (vals + 3)
1745 #define RANGE (vals + 7)
1746 #define RULE (vals + 11 - 1)
1747 #define DEFAULT_RULES (vals + 22)
1748 #define DEFAULT_2007_RULES (vals + 38)
1750 /* Initialize to UTC. */
1753 char *tzname[2] = { (char *) UTC, (char *) (UTC-1) };
1755 __UCLIBC_MUTEX_INIT(_time_tzlock, PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP);
1757 rule_struct _time_tzinfo[2];
1759 static const char *getoffset(register const char *e, long *pn)
1761 register const char *s = RANGE-1;
1769 if (__isdigit_char(*e)) {
1772 if (__isdigit_char(*e)) {
1773 f = 10 * f + (*e++ - '0');
1775 if (((unsigned int)f) >= *s) {
1790 static const char *getnumber(register const char *e, int *pn)
1793 /* bcc can optimize the counter if it thinks it is a pointer... */
1794 register const char *n = (const char *) 3;
1798 while (n && __isdigit_char(*e)) {
1799 f = 10 * f + (*e++ - '0');
1804 return (n == (const char *) 3) ? NULL : e;
1810 while (n && __isdigit_char(*e)) {
1811 f = 10 * f + (*e++ - '0');
1816 return (n == 3) ? NULL : e;
1817 #endif /* __BCC__ */
1821 #ifdef __UCLIBC_MJN3_ONLY__
1822 #warning CONSIDER: Should we preserve errno from open/read/close errors re TZ file?
1825 #ifdef __UCLIBC_HAS_TZ_FILE__
1827 #ifndef __UCLIBC_HAS_TZ_FILE_READ_MANY__
1828 static smallint TZ_file_read; /* Let BSS initialization set this to 0. */
1829 #endif /* __UCLIBC_HAS_TZ_FILE_READ_MANY__ */
1831 static char *read_TZ_file(char *buf)
1838 if ((fd = open(__UCLIBC_TZ_FILE_PATH__, O_RDONLY)) >= 0) {
1842 if ((r = read(fd, p, todo)) < 0) {
1852 if ((p > buf) && (p[-1] == '\n')) { /* Must end with newline. */
1855 #ifndef __UCLIBC_HAS_TZ_FILE_READ_MANY__
1857 #endif /* __UCLIBC_HAS_TZ_FILE_READ_MANY__ */
1867 #endif /* __UCLIBC_HAS_TZ_FILE__ */
1869 #ifndef __UCLIBC_HAS_CTYPE_TABLES__
1870 libc_hidden_proto(isascii)
1875 _time_tzset((time(NULL)) < new_rule_starts);
1878 void _time_tzset(int use_old_rules)
1880 register const char *e;
1884 rule_struct new_rules[2];
1887 #ifdef __UCLIBC_HAS_TZ_FILE__
1888 char buf[TZ_BUFLEN];
1889 #endif /* __UCLIBC_HAS_TZ_FILE__ */
1890 #ifdef __UCLIBC_HAS_TZ_CACHING__
1891 static char oldval[TZ_BUFLEN]; /* BSS-zero'd. */
1892 #endif /* __UCLIBC_HAS_TZ_CACHING__ */
1894 __UCLIBC_MUTEX_LOCK(_time_tzlock);
1896 e = getenv(TZ); /* TZ env var always takes precedence. */
1898 #if defined(__UCLIBC_HAS_TZ_FILE__) && !defined(__UCLIBC_HAS_TZ_FILE_READ_MANY__)
1899 /* Put this inside the lock to prevent the possiblity of two different
1900 * timezones being used in a threaded app. */
1903 TZ_file_read = 0; /* Reset if the TZ env var is set. */
1904 } else if (TZ_file_read) {
1907 #endif /* defined(__UCLIBC_HAS_TZ_FILE__) && !defined(__UCLIBC_HAS_TZ_FILE_READ_MANY__) */
1909 /* Warning!!! Since uClibc doesn't do lib locking, the following is
1910 * potentially unsafe in a multi-threaded program since it is remotely
1911 * possible that another thread could call setenv() for TZ and overwrite
1912 * the string being parsed. So, don't do that... */
1914 if ((!e /* TZ env var not set... */
1915 #ifdef __UCLIBC_HAS_TZ_FILE__
1916 && !(e = read_TZ_file(buf)) /* and no file or invalid file */
1917 #endif /* __UCLIBC_HAS_TZ_FILE__ */
1918 ) || !*e) { /* or set to empty string. */
1919 ILLEGAL: /* TODO: Clean up the following... */
1920 #ifdef __UCLIBC_HAS_TZ_CACHING__
1921 *oldval = 0; /* Set oldval to an empty string. */
1922 #endif /* __UCLIBC_HAS_TZ_CACHING__ */
1923 memset(_time_tzinfo, 0, 2*sizeof(rule_struct));
1924 strcpy(_time_tzinfo[0].tzname, UTC);
1928 if (*e == ':') { /* Ignore leading ':'. */
1932 #ifdef __UCLIBC_HAS_TZ_CACHING__
1933 if (strcmp(e, oldval) == 0) { /* Same string as last time... */
1934 goto FAST_DONE; /* So nothing to do. */
1936 /* Make a copy of the TZ env string. It won't be nul-terminated if
1937 * it is too long, but it that case it will be illegal and will be reset
1938 * to the empty string anyway. */
1939 strncpy(oldval, e, TZ_BUFLEN);
1940 #endif /* __UCLIBC_HAS_TZ_CACHING__ */
1943 new_rules[1].tzname[0] = 0;
1945 /* Get std or dst name. */
1952 s = new_rules[count].tzname;
1955 && isascii(*e) /* SUSv3 requires char in portable char set. */
1957 || (c && (isalnum(*e) || (*e == '+') || (*e == '-'))))
1960 if (++n > TZNAME_MAX) {
1966 if ((n < 3) /* Check for minimum length. */
1967 || (c && (*e++ != c)) /* Match any quoting '<'. */
1974 if ((*e != '-') && (*e != '+')) {
1975 if (count && !__isdigit_char(*e)) {
1976 off -= 3600; /* Default to 1 hour ahead of std. */
1983 if (!(e = getoffset(e, &off))) {
1988 off = -off; /* Save off in case needed for dst default. */
1991 new_rules[count].gmt_offset = off;
1994 new_rules[1].gmt_offset = off; /* Shouldn't be needed... */
1999 } else { /* OK, we have dst, so get some rules. */
2001 if (!*e) { /* No rules so default to US rules. */
2002 e = use_old_rules ? DEFAULT_RULES : DEFAULT_2007_RULES;
2004 if (e == DEFAULT_RULES)
2005 printf("tzset: Using old rules.\n");
2006 else if (e == DEFAULT_2007_RULES)
2007 printf("tzset: Using new rules\n");
2009 printf("tzset: Using undefined rules\n");
2010 #endif /* DEBUG_TZSET */
2020 if ((c = *e++) == 'M') {
2022 } else if (c == 'J') {
2030 *(p = &new_rules[count].rule_type) = c;
2037 if (!(e = getnumber(e, &f))
2038 || (((unsigned int)(f - s[1])) > n)
2039 || (*s && (*e++ != *s))
2044 } while ((n = *(s += 2)) > 0);
2046 off = 2 * 60 * 60; /* Default to 2:00:00 */
2049 if (!(e = getoffset(e, &off))) {
2053 new_rules[count].dst_offset = off;
2054 } while (++count < 2);
2061 memcpy(_time_tzinfo, new_rules, sizeof(new_rules));
2063 tzname[0] = _time_tzinfo[0].tzname;
2064 tzname[1] = _time_tzinfo[1].tzname;
2065 daylight = !!_time_tzinfo[1].tzname[0];
2066 timezone = _time_tzinfo[0].gmt_offset;
2068 #if defined(__UCLIBC_HAS_TZ_FILE__) || defined(__UCLIBC_HAS_TZ_CACHING__)
2071 __UCLIBC_MUTEX_UNLOCK(_time_tzlock);
2073 libc_hidden_def(tzset)
2075 /**********************************************************************/
2076 /* #ifdef L_utime */
2078 /* utime is a syscall in both linux and elks. */
2079 /* int utime(const char *path, const struct utimbuf *times) */
2082 /**********************************************************************/
2084 /**********************************************************************/
2088 #error The uClibc version of utimes is in sysdeps/linux/common.
2092 #include <sys/time.h>
2094 int utimes(const char *filename, register const struct timeval *tvp)
2096 register struct utimbuf *p = NULL;
2101 p->actime = tvp[0].tv_sec;
2102 p->modtime = tvp[1].tv_sec;
2104 return utime(filename, p);
2108 /**********************************************************************/
2111 static const uint16_t _vals[] = {
2112 60, 60, 24, 7 /* special */, 36524, 1461, 365, 0
2115 static const unsigned char days[] = {
2116 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, /* non-leap */
2120 #ifdef __UCLIBC_HAS_TM_EXTENSIONS__
2121 static const char utc_string[] = "UTC";
2125 * If time_t is 32 bits, then no overflow is possible.
2126 * It time_t is > 32 bits, this needs to be adjusted to deal with overflow.
2129 /* Note: offset is the correction in _days_ to *timer! */
2131 struct tm attribute_hidden *_time_t2tm(const time_t *__restrict timer,
2132 int offset, struct tm *__restrict result)
2136 int wday = wday; /* ok to be uninitialized, shutting up warning */
2139 register const uint16_t *vp;
2145 if ((v = *vp) == 7) {
2146 /* Overflow checking, assuming time_t is long int... */
2147 #if (LONG_MAX > INT_MAX) && (LONG_MAX > 2147483647L)
2148 #if (INT_MAX == 2147483647L) && (LONG_MAX == 9223372036854775807L)
2149 /* Valid range for t is [-784223472856L, 784223421720L].
2150 * Outside of this range, the tm_year field will overflow. */
2151 if (((unsigned long)(t + offset- -784223472856L))
2152 > (784223421720L - -784223472856L)
2157 #error overflow conditions unknown
2161 /* We have days since the epoch, so caluclate the weekday. */
2162 #if defined(__BCC__) && TIME_T_IS_UNSIGNED
2163 wday = (t + 4) % (*vp); /* t is unsigned */
2165 wday = ((int)((t % (*vp)) + 11)) % ((int)(*vp)); /* help bcc */
2167 /* Set divisor to days in 400 years. Be kind to bcc... */
2168 v = ((time_t)(vp[1])) << 2;
2170 /* Change to days since 1/1/1601 so that for 32 bit time_t
2171 * values, we'll have t >= 0. This should be changed for
2172 * archs with larger time_t types.
2173 * Also, correct for offset since a multiple of 7. */
2175 /* TODO: Does this still work on archs with time_t > 32 bits? */
2176 t += (135140L - 366) + offset; /* 146097 - (365*30 + 7) -366 */
2178 #if defined(__BCC__) && TIME_T_IS_UNSIGNED
2179 t -= ((t1 = t / v) * v);
2181 if ((t -= ((t1 = t / v) * v)) < 0) {
2187 if ((*vp == 7) && (t == v-1)) {
2188 --t; /* Correct for 400th year leap case */
2189 ++p[4]; /* Stash the extra day... */
2192 #if defined(__BCC__) && 0
2216 *p += ((int) t); /* result[7] .. tm_yday */
2218 p -= 2; /* at result[5] */
2220 #if (LONG_MAX > INT_MAX) && (LONG_MAX > 2147483647L)
2221 /* Protect against overflow. TODO: Unecessary if int arith wraps? */
2222 *p = ((((p[-2]<<2) + p[-1])*25 + p[0])<< 2) + (p[1] - 299); /* tm_year */
2224 *p = ((((p[-2]<<2) + p[-1])*25 + p[0])<< 2) + p[1] - 299; /* tm_year */
2227 p[1] = wday; /* result[6] .. tm_wday */
2230 register const unsigned char *d = days;
2233 if (__isleap(wday)) {
2237 wday = p[2] + 1; /* result[7] .. tm_yday */
2238 *--p = 0; /* at result[4] .. tm_mon */
2242 d -= 11; /* Backup to non-leap Feb. */
2245 ++*p; /* Increment tm_mon. */
2247 p[-1] = wday; /* result[3] .. tm_mday */
2249 /* TODO -- should this be 0? */
2250 p[4] = 0; /* result[8] .. tm_isdst */
2251 #ifdef __UCLIBC_HAS_TM_EXTENSIONS__
2253 result->tm_gmtoff = 0;
2254 result->tm_zone = utc_string;
2256 result->__tm_gmtoff = 0;
2257 result->__tm_zone = utc_string;
2259 #endif /* __UCLIBC_HAS_TM_EXTENSIONS__ */
2265 /**********************************************************************/
2268 struct tm __time_tm; /* Global shared by gmtime() and localtime(). */
2271 /**********************************************************************/
2272 #ifdef L__time_mktime
2274 time_t attribute_hidden _time_mktime(struct tm *timeptr, int store_on_success)
2278 __UCLIBC_MUTEX_LOCK(_time_tzlock);
2282 t = _time_mktime_tzi(timeptr, store_on_success, _time_tzinfo);
2284 __UCLIBC_MUTEX_UNLOCK(_time_tzlock);
2290 /**********************************************************************/
2291 #ifdef L__time_mktime_tzi
2293 static const unsigned char __vals[] = {
2294 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, /* non-leap */
2298 time_t attribute_hidden _time_mktime_tzi(struct tm *timeptr, int store_on_success,
2308 /* 0:sec 1:min 2:hour 3:mday 4:mon 5:year 6:wday 7:yday 8:isdst */
2309 register int *p = (int *) &x;
2310 register const unsigned char *s;
2313 memcpy(p, timeptr, sizeof(struct tm));
2315 if (!tzi[1].tzname[0]) { /* No dst in this timezone, */
2316 p[8] = 0; /* so set tm_isdst to 0. */
2320 if (p[8]) { /* Either dst or unknown? */
2321 default_dst = 1; /* Assume advancing (even if unknown). */
2322 p[8] = ((p[8] > 0) ? 1 : -1); /* Normalize so abs() <= 1. */
2326 p[5] = (p[5] - ((p[6] = p[5]/d) * d)) + (p[7] = p[4]/12);
2327 if ((p[4] -= 12 * p[7]) < 0) {
2333 d = (p[5] += 1900); /* Correct year. Now between 1900 and 2300. */
2343 s -= 11; /* Backup to non-leap Feb. */
2349 _time_tzset (x.tm_year < 2007); /* tm_year was expanded above */
2353 days = -719163L + ((long)d)*365 + ((d/4) - (d/100) + (d/400) + p[3] + p[7]);
2354 secs = p[0] + 60*( p[1] + 60*((long)(p[2])) )
2355 + tzi[default_dst].gmt_offset;
2361 if ( ((unsigned long)(days + secs/86400L)) > 49710L) {
2365 secs += (days * 86400L);
2368 d = -719163L + d*365 + (d/4) - (d/100) + (d/400);
2370 + tzi[default_dst].gmt_offset
2373 + 24*(((146073L * ((long long)(p[6])) + d)
2377 if (((unsigned long long)(secs - LONG_MIN))
2378 > (((unsigned long long)LONG_MAX) - LONG_MIN)
2385 d = ((struct tm *)p)->tm_isdst;
2388 __time_localtime_tzi(&t, (struct tm *)p, tzi);
2390 if (t == ((time_t)(-1))) { /* Remember, time_t can be unsigned. */
2394 if ((d < 0) && (((struct tm *)p)->tm_isdst != default_dst)) {
2396 secs -= (days * 86400L);
2398 secs += (tzi[1-default_dst].gmt_offset
2399 - tzi[default_dst].gmt_offset);
2404 if (store_on_success) {
2405 memcpy(timeptr, p, sizeof(struct tm));
2414 /**********************************************************************/
2415 #if defined(L_wcsftime) || defined(L_wcsftime_l)
2417 #if defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE)
2419 libc_hidden_proto(wcsftime)
2421 libc_hidden_proto(wcsftime_l)
2423 size_t wcsftime(wchar_t *__restrict s, size_t maxsize,
2424 const wchar_t *__restrict format,
2425 const struct tm *__restrict timeptr)
2427 return wcsftime_l(s, maxsize, format, timeptr, __UCLIBC_CURLOCALE);
2429 libc_hidden_def(wcsftime)
2431 #else /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */
2433 libc_hidden_proto(__XL_NPP(wcsftime))
2434 size_t __XL_NPP(wcsftime)(wchar_t *__restrict s, size_t maxsize,
2435 const wchar_t *__restrict format,
2436 const struct tm *__restrict timeptr __LOCALE_PARAM )
2438 #warning wcsftime always fails
2439 return 0; /* always fail */
2441 libc_hidden_def(__XL_NPP(wcsftime))
2443 #endif /* defined(__UCLIBC_HAS_XLOCALE__) && !defined(__UCLIBC_DO_XLOCALE) */
2446 /**********************************************************************/
2448 /* Return the number of days in YEAR. */
2450 int dysize(int year)
2452 return __isleap(year) ? 366 : 365;
2456 /**********************************************************************/