1 /* NOTE: This file defines both strftime() and wcsftime(). Take care when
2 * making changes. See also wcsftime.c, and note the (small) overlap in the
3 * manual description, taking care to edit both as needed. */
4 /*
5 * strftime.c
6 * Original Author: G. Haley
7 * Additions from: Eric Blake, Corinna Vinschen
8 * Changes to allow dual use as wcstime, also: Craig Howland
9 *
10 * Places characters into the array pointed to by s as controlled by the string
11 * pointed to by format. If the total number of resulting characters including
12 * the terminating null character is not more than maxsize, returns the number
13 * of characters placed into the array pointed to by s (not including the
14 * terminating null character); otherwise zero is returned and the contents of
15 * the array indeterminate.
16 */
17
18 /*
19 FUNCTION
20 <<strftime>>, <<strftime_l>>---convert date and time to a formatted string
21
22 INDEX
23 strftime
24
25 INDEX
26 strftime_l
27
28 SYNOPSIS
29 #include <time.h>
30 size_t strftime(char *restrict <[s]>, size_t <[maxsize]>,
31 const char *restrict <[format]>,
32 const struct tm *restrict <[timp]>);
33 size_t strftime_l(char *restrict <[s]>, size_t <[maxsize]>,
34 const char *restrict <[format]>,
35 const struct tm *restrict <[timp]>,
36 locale_t <[locale]>);
37
38 DESCRIPTION
39 <<strftime>> converts a <<struct tm>> representation of the time (at
40 <[timp]>) into a null-terminated string, starting at <[s]> and occupying
41 no more than <[maxsize]> characters.
42
43 <<strftime_l>> is like <<strftime>> but creates a string in a format
44 as expected in locale <[locale]>. If <[locale]> is LC_GLOBAL_LOCALE or
45 not a valid locale object, the behaviour is undefined.
46
47 You control the format of the output using the string at <[format]>.
48 <<*<[format]>>> can contain two kinds of specifications: text to be
49 copied literally into the formatted string, and time conversion
50 specifications. Time conversion specifications are two- and
51 three-character sequences beginning with `<<%>>' (use `<<%%>>' to
52 include a percent sign in the output). Each defined conversion
53 specification selects only the specified field(s) of calendar time
54 data from <<*<[timp]>>>, and converts it to a string in one of the
55 following ways:
56
57 o+
58 o %a
59 The abbreviated weekday name according to the current locale. [tm_wday]
60
61 o %A
62 The full weekday name according to the current locale.
63 In the default "C" locale, one of `<<Sunday>>', `<<Monday>>', `<<Tuesday>>',
64 `<<Wednesday>>', `<<Thursday>>', `<<Friday>>', `<<Saturday>>'. [tm_wday]
65
66 o %b
67 The abbreviated month name according to the current locale. [tm_mon]
68
69 o %B
70 The full month name according to the current locale.
71 In the default "C" locale, one of `<<January>>', `<<February>>',
72 `<<March>>', `<<April>>', `<<May>>', `<<June>>', `<<July>>',
73 `<<August>>', `<<September>>', `<<October>>', `<<November>>',
74 `<<December>>'. [tm_mon]
75
76 o %c
77 The preferred date and time representation for the current locale.
78 [tm_sec, tm_min, tm_hour, tm_mday, tm_mon, tm_year, tm_wday]
79
80 o %C
81 The century, that is, the year divided by 100 then truncated. For
82 4-digit years, the result is zero-padded and exactly two characters;
83 but for other years, there may a negative sign or more digits. In
84 this way, `<<%C%y>>' is equivalent to `<<%Y>>'. [tm_year]
85
86 o %d
87 The day of the month, formatted with two digits (from `<<01>>' to
88 `<<31>>'). [tm_mday]
89
90 o %D
91 A string representing the date, in the form `<<"%m/%d/%y">>'.
92 [tm_mday, tm_mon, tm_year]
93
94 o %e
95 The day of the month, formatted with leading space if single digit
96 (from `<<1>>' to `<<31>>'). [tm_mday]
97
98 o %E<<x>>
99 In some locales, the E modifier selects alternative representations of
100 certain modifiers <<x>>. In newlib, it is ignored, and treated as %<<x>>.
101
102 o %F
103 A string representing the ISO 8601:2000 date format, in the form
104 `<<"%Y-%m-%d">>'. [tm_mday, tm_mon, tm_year]
105
106 o %g
107 The last two digits of the week-based year, see specifier %G (from
108 `<<00>>' to `<<99>>'). [tm_year, tm_wday, tm_yday]
109
110 o %G
111 The week-based year. In the ISO 8601:2000 calendar, week 1 of the year
112 includes January 4th, and begin on Mondays. Therefore, if January 1st,
113 2nd, or 3rd falls on a Sunday, that day and earlier belong to the last
114 week of the previous year; and if December 29th, 30th, or 31st falls
115 on Monday, that day and later belong to week 1 of the next year. For
116 consistency with %Y, it always has at least four characters.
117 Example: "%G" for Saturday 2nd January 1999 gives "1998", and for
118 Tuesday 30th December 1997 gives "1998". [tm_year, tm_wday, tm_yday]
119
120 o %h
121 Synonym for "%b". [tm_mon]
122
123 o %H
124 The hour (on a 24-hour clock), formatted with two digits (from
125 `<<00>>' to `<<23>>'). [tm_hour]
126
127 o %I
128 The hour (on a 12-hour clock), formatted with two digits (from
129 `<<01>>' to `<<12>>'). [tm_hour]
130
131 o %j
132 The count of days in the year, formatted with three digits
133 (from `<<001>>' to `<<366>>'). [tm_yday]
134
135 o %k
136 The hour (on a 24-hour clock), formatted with leading space if single
137 digit (from `<<0>>' to `<<23>>'). Non-POSIX extension (c.p. %I). [tm_hour]
138
139 o %l
140 The hour (on a 12-hour clock), formatted with leading space if single
141 digit (from `<<1>>' to `<<12>>'). Non-POSIX extension (c.p. %H). [tm_hour]
142
143 o %m
144 The month number, formatted with two digits (from `<<01>>' to `<<12>>').
145 [tm_mon]
146
147 o %M
148 The minute, formatted with two digits (from `<<00>>' to `<<59>>'). [tm_min]
149
150 o %n
151 A newline character (`<<\n>>').
152
153 o %O<<x>>
154 In some locales, the O modifier selects alternative digit characters
155 for certain modifiers <<x>>. In newlib, it is ignored, and treated as %<<x>>.
156
157 o %p
158 Either `<<AM>>' or `<<PM>>' as appropriate, or the corresponding strings for
159 the current locale. [tm_hour]
160
161 o %P
162 Same as '<<%p>>', but in lowercase. This is a GNU extension. [tm_hour]
163
164 o %q
165 Quarter of the year (from `<<1>>' to `<<4>>'), with January starting
166 the first quarter. This is a GNU extension. [tm_mon]
167
168 o %r
169 Replaced by the time in a.m. and p.m. notation. In the "C" locale this
170 is equivalent to "%I:%M:%S %p". In locales which don't define a.m./p.m.
171 notations, the result is an empty string. [tm_sec, tm_min, tm_hour]
172
173 o %R
174 The 24-hour time, to the minute. Equivalent to "%H:%M". [tm_min, tm_hour]
175
176 o %s
177 The time elapsed, in seconds, since the start of the Unix epoch at
178 1970-01-01 00:00:00 UTC.
179
180 o %S
181 The second, formatted with two digits (from `<<00>>' to `<<60>>'). The
182 value 60 accounts for the occasional leap second. [tm_sec]
183
184 o %t
185 A tab character (`<<\t>>').
186
187 o %T
188 The 24-hour time, to the second. Equivalent to "%H:%M:%S". [tm_sec,
189 tm_min, tm_hour]
190
191 o %u
192 The weekday as a number, 1-based from Monday (from `<<1>>' to
193 `<<7>>'). [tm_wday]
194
195 o %U
196 The week number, where weeks start on Sunday, week 1 contains the first
197 Sunday in a year, and earlier days are in week 0. Formatted with two
198 digits (from `<<00>>' to `<<53>>'). See also <<%W>>. [tm_wday, tm_yday]
199
200 o %V
201 The week number, where weeks start on Monday, week 1 contains January 4th,
202 and earlier days are in the previous year. Formatted with two digits
203 (from `<<01>>' to `<<53>>'). See also <<%G>>. [tm_year, tm_wday, tm_yday]
204
205 o %v
206 A string representing the BSD/OSX/Ruby VMS/Oracle date format, in the form
207 "%e-%b-%Y". Non-POSIX extension. [tm_mday, tm_mon, tm_year]
208
209 o %w
210 The weekday as a number, 0-based from Sunday (from `<<0>>' to `<<6>>').
211 [tm_wday]
212
213 o %W
214 The week number, where weeks start on Monday, week 1 contains the first
215 Monday in a year, and earlier days are in week 0. Formatted with two
216 digits (from `<<00>>' to `<<53>>'). [tm_wday, tm_yday]
217
218 o %x
219 Replaced by the preferred date representation in the current locale.
220 In the "C" locale this is equivalent to "%m/%d/%y".
221 [tm_mon, tm_mday, tm_year]
222
223 o %X
224 Replaced by the preferred time representation in the current locale.
225 In the "C" locale this is equivalent to "%H:%M:%S". [tm_sec, tm_min, tm_hour]
226
227 o %y
228 The last two digits of the year (from `<<00>>' to `<<99>>'). [tm_year]
229 (Implementation interpretation: always positive, even for negative years.)
230
231 o %Y
232 The full year, equivalent to <<%C%y>>. It will always have at least four
233 characters, but may have more. The year is accurate even when tm_year
234 added to the offset of 1900 overflows an int. [tm_year]
235
236 o %z
237 The offset from UTC. The format consists of a sign (negative is west of
238 Greewich), two characters for hour, then two characters for minutes
239 (-hhmm or +hhmm). If tm_isdst is negative, the offset is unknown and no
240 output is generated; if it is zero, the offset is the standard offset for
241 the current time zone; and if it is positive, the offset is the daylight
242 savings offset for the current timezone. The offset is determined from
243 the TZ environment variable, as if by calling tzset(). [tm_isdst]
244
245 o %Z
246 The current time zone abbreviation. If tm_isdst is negative, no output
247 is generated. Otherwise, the time zone abbreviation is based on the TZ
248 environment variable, as if by calling tzset(). [tm_isdst]
249
250 o %%
251 A single character, `<<%>>'.
252 o-
253
254 RETURNS
255 When the formatted time takes up no more than <[maxsize]> characters,
256 the result is the length of the formatted string. Otherwise, if the
257 formatting operation was abandoned due to lack of room, the result is
258 <<0>>, and the string starting at <[s]> corresponds to just those
259 parts of <<*<[format]>>> that could be completely filled in within the
260 <[maxsize]> limit.
261
262 PORTABILITY
263 ANSI C requires <<strftime>>, but does not specify the contents of
264 <<*<[s]>>> when the formatted string would require more than
265 <[maxsize]> characters. Unrecognized specifiers and fields of
266 <<timp>> that are out of range cause undefined results. Since some
267 formats expand to 0 bytes, it is wise to set <<*<[s]>>> to a nonzero
268 value beforehand to distinguish between failure and an empty string.
269 This implementation does not support <<s>> being NULL, nor overlapping
270 <<s>> and <<format>>.
271
272 <<strftime_l>> is POSIX-1.2008.
273
274 <<strftime>> and <<strftime_l>> require no supporting OS subroutines.
275
276 BUGS
277 (NOT Cygwin:) <<strftime>> ignores the LC_TIME category of the current
278 locale, hard-coding the "C" locale settings.
279 */
280
281 #define _GNU_SOURCE
282 #include <stddef.h>
283 #include <stdio.h>
284 #include <time.h>
285 #include <string.h>
286 #include <stdlib.h>
287 #include <limits.h>
288 #include <ctype.h>
289 #include <wctype.h>
290 #include <wchar.h>
291 #include "local.h"
292 #include "../locale/setlocale.h"
293
294 /* Defines to make the file dual use for either strftime() or wcsftime().
295 * To get wcsftime, define MAKE_WCSFTIME.
296 * To get strftime, do not define MAKE_WCSFTIME.
297 * Names are kept friendly to strftime() usage. The biggest ugliness is the
298 * use of the CQ() macro to make either regular character constants and
299 * string literals or wide-character constants and wide-character-string
300 * literals, as appropriate. */
301 #if !defined(MAKE_WCSFTIME)
302 # define CHAR char /* string type basis */
303 # define CQ(a) a /* character constant qualifier */
304 # define t_snprintf snprintf /* char equivalent function name */
305 # define t_strncmp strncmp /* char equivalent function name */
306 # define SFLG /* %s flag (null for normal char) */
307 # define _ctloc(x) (ctloclen = strlen (ctloc = _CurrentTimeLocale->x))
308 # define TOLOWER(c) tolower((int)(unsigned char)(c))
309 # define STRTOUL(c,p,b) strtoul((c),(p),(b))
310 # define STRCPY(a,b) strcpy((a),(b))
311 # define STRCHR(a,b) strchr((a),(b))
312 # define STRLEN(a) strlen(a)
313 # else
314 # define strftime wcsftime /* Alternate function name */
315 # define strftime_l wcsftime_l /* Alternate function name */
316 # define CHAR wchar_t /* string type basis */
317 # define CQ(a) L##a /* character constant qualifier */
318 # define t_snprintf swprintf /* wide-char equivalent function name */
319 # define t_strncmp wcsncmp /* wide-char equivalent function name */
320 # define TOLOWER(c) towlower((wint_t)(c))
321 # define STRTOUL(c,p,b) wcstoul((c),(p),(b))
322 # define STRCPY(a,b) wcscpy((a),(b))
323 # define STRCHR(a,b) wcschr((a),(b))
324 # define STRLEN(a) wcslen(a)
325 # define SFLG "l" /* %s flag (l for wide char) */
326 # ifdef __HAVE_LOCALE_INFO_EXTENDED__
327 # define _ctloc(x) (ctloclen = wcslen (ctloc = _CurrentTimeLocale->w##x))
328 # else
329 # define CTLOCBUFLEN 256 /* Arbitrary big buffer size */
330 static const wchar_t *
__ctloc(wchar_t * buf,const char * elem,size_t * len_ret)331 __ctloc (wchar_t *buf, const char *elem, size_t *len_ret)
332 {
333 buf[CTLOCBUFLEN - 1] = L'\0';
334 *len_ret = mbstowcs (buf, elem, CTLOCBUFLEN - 1);
335 if (*len_ret == (size_t) -1 )
336 *len_ret = 0;
337 return buf;
338 }
339 # define _ctloc(x) (ctloc = __ctloc (ctlocbuf, _CurrentTimeLocale->x, \
340 &ctloclen))
341 # endif
342 #endif /* MAKE_WCSFTIME */
343
344 #define CHECK_LENGTH() if (len < 0 || (count += len) >= maxsize) \
345 return 0
346
347 /* Enforce the coding assumptions that YEAR_BASE is positive. (%C, %Y, etc.) */
348 #if YEAR_BASE < 0
349 # error "YEAR_BASE < 0"
350 #endif
351
352 /* Using the tm_year, tm_wday, and tm_yday components of TIM_P, return
353 -1, 0, or 1 as the adjustment to add to the year for the ISO week
354 numbering used in "%g%G%V", avoiding overflow. */
355 static int
iso_year_adjust(const struct tm * tim_p)356 iso_year_adjust (const struct tm *tim_p)
357 {
358 /* Account for fact that tm_year==0 is year 1900. */
359 int leap = isleap (tim_p->tm_year + (YEAR_BASE
360 - (tim_p->tm_year < 0 ? 0 : 2000)));
361
362 /* Pack the yday, wday, and leap year into a single int since there are so
363 many disparate cases. */
364 #define PACK(yd, wd, lp) (((yd) << 4) + (wd << 1) + (lp))
365 switch (PACK (tim_p->tm_yday, tim_p->tm_wday, leap))
366 {
367 case PACK (0, 5, 0): /* Jan 1 is Fri, not leap. */
368 case PACK (0, 6, 0): /* Jan 1 is Sat, not leap. */
369 case PACK (0, 0, 0): /* Jan 1 is Sun, not leap. */
370 case PACK (0, 5, 1): /* Jan 1 is Fri, leap year. */
371 case PACK (0, 6, 1): /* Jan 1 is Sat, leap year. */
372 case PACK (0, 0, 1): /* Jan 1 is Sun, leap year. */
373 case PACK (1, 6, 0): /* Jan 2 is Sat, not leap. */
374 case PACK (1, 0, 0): /* Jan 2 is Sun, not leap. */
375 case PACK (1, 6, 1): /* Jan 2 is Sat, leap year. */
376 case PACK (1, 0, 1): /* Jan 2 is Sun, leap year. */
377 case PACK (2, 0, 0): /* Jan 3 is Sun, not leap. */
378 case PACK (2, 0, 1): /* Jan 3 is Sun, leap year. */
379 return -1; /* Belongs to last week of previous year. */
380 case PACK (362, 1, 0): /* Dec 29 is Mon, not leap. */
381 case PACK (363, 1, 1): /* Dec 29 is Mon, leap year. */
382 case PACK (363, 1, 0): /* Dec 30 is Mon, not leap. */
383 case PACK (363, 2, 0): /* Dec 30 is Tue, not leap. */
384 case PACK (364, 1, 1): /* Dec 30 is Mon, leap year. */
385 case PACK (364, 2, 1): /* Dec 30 is Tue, leap year. */
386 case PACK (364, 1, 0): /* Dec 31 is Mon, not leap. */
387 case PACK (364, 2, 0): /* Dec 31 is Tue, not leap. */
388 case PACK (364, 3, 0): /* Dec 31 is Wed, not leap. */
389 case PACK (365, 1, 1): /* Dec 31 is Mon, leap year. */
390 case PACK (365, 2, 1): /* Dec 31 is Tue, leap year. */
391 case PACK (365, 3, 1): /* Dec 31 is Wed, leap year. */
392 return 1; /* Belongs to first week of next year. */
393 }
394 return 0; /* Belongs to specified year. */
395 #undef PACK
396 }
397
398 #ifdef _WANT_C99_TIME_FORMATS
399 typedef struct {
400 int year;
401 CHAR *era_C;
402 CHAR *era_Y;
403 } era_info_t;
404
405 static era_info_t *
406 #if defined (MAKE_WCSFTIME) && defined (__HAVE_LOCALE_INFO_EXTENDED__)
get_era_info(const struct tm * tim_p,const wchar_t * era)407 get_era_info (const struct tm *tim_p, const wchar_t *era)
408 #else
409 get_era_info (const struct tm *tim_p, const char *era)
410 #endif
411 {
412 #if defined (MAKE_WCSFTIME) && defined (__HAVE_LOCALE_INFO_EXTENDED__)
413 wchar_t *c;
414 const wchar_t *dir;
415 # define ERA_STRCHR(a,b) wcschr((a),(b))
416 # define ERA_STRNCPY(a,b,c) wcsncpy((a),(b),(c))
417 # define ERA_STRTOL(a,b,c) wcstol((a),(b),(c))
418 #else
419 char *c;
420 const char *dir;
421 # define ERA_STRCHR(a,b) strchr((a),(b))
422 # define ERA_STRNCPY(a,b,c) strncpy((a),(b),(c))
423 # define ERA_STRTOL(a,b,c) strtol((a),(b),(c))
424 #endif
425 long offset;
426 struct tm stm, etm;
427 era_info_t *ei;
428
429 ei = (era_info_t *) calloc (1, sizeof (era_info_t));
430 if (!ei)
431 return NULL;
432
433 stm.tm_isdst = etm.tm_isdst = 0;
434 while (era)
435 {
436 dir = era;
437 era += 2;
438 offset = ERA_STRTOL (era, &c, 10);
439 era = c + 1;
440 stm.tm_year = ERA_STRTOL (era, &c, 10) - YEAR_BASE;
441 /* Adjust offset for negative gregorian dates. */
442 if (stm.tm_year <= -YEAR_BASE)
443 ++stm.tm_year;
444 stm.tm_mon = ERA_STRTOL (c + 1, &c, 10) - 1;
445 stm.tm_mday = ERA_STRTOL (c + 1, &c, 10);
446 stm.tm_hour = stm.tm_min = stm.tm_sec = 0;
447 era = c + 1;
448 if (era[0] == '-' && era[1] == '*')
449 {
450 etm = stm;
451 stm.tm_year = INT_MIN;
452 stm.tm_mon = stm.tm_mday = stm.tm_hour = stm.tm_min = stm.tm_sec = 0;
453 era += 3;
454 }
455 else if (era[0] == '+' && era[1] == '*')
456 {
457 etm.tm_year = INT_MAX;
458 etm.tm_mon = 11;
459 etm.tm_mday = 31;
460 etm.tm_hour = 23;
461 etm.tm_min = etm.tm_sec = 59;
462 era += 3;
463 }
464 else
465 {
466 etm.tm_year = ERA_STRTOL (era, &c, 10) - YEAR_BASE;
467 /* Adjust offset for negative gregorian dates. */
468 if (etm.tm_year <= -YEAR_BASE)
469 ++etm.tm_year;
470 etm.tm_mon = ERA_STRTOL (c + 1, &c, 10) - 1;
471 etm.tm_mday = ERA_STRTOL (c + 1, &c, 10);
472 etm.tm_mday = 31;
473 etm.tm_hour = 23;
474 etm.tm_min = etm.tm_sec = 59;
475 era = c + 1;
476 }
477 if ((tim_p->tm_year > stm.tm_year
478 || (tim_p->tm_year == stm.tm_year
479 && (tim_p->tm_mon > stm.tm_mon
480 || (tim_p->tm_mon == stm.tm_mon
481 && tim_p->tm_mday >= stm.tm_mday))))
482 && (tim_p->tm_year < etm.tm_year
483 || (tim_p->tm_year == etm.tm_year
484 && (tim_p->tm_mon < etm.tm_mon
485 || (tim_p->tm_mon == etm.tm_mon
486 && tim_p->tm_mday <= etm.tm_mday)))))
487 {
488 /* Gotcha */
489 size_t len;
490
491 /* year */
492 if (*dir == '+' && stm.tm_year != INT_MIN)
493 ei->year = tim_p->tm_year - stm.tm_year + offset;
494 else
495 ei->year = etm.tm_year - tim_p->tm_year + offset;
496 /* era_C */
497 c = ERA_STRCHR (era, ':');
498 #if defined (MAKE_WCSFTIME) && !defined (__HAVE_LOCALE_INFO_EXTENDED__)
499 len = mbsnrtowcs (NULL, &era, c - era, 0, NULL);
500 if (len == (size_t) -1)
501 {
502 free (ei);
503 return NULL;
504 }
505 #else
506 len = c - era;
507 #endif
508 ei->era_C = (CHAR *) malloc ((len + 1) * sizeof (CHAR));
509 if (!ei->era_C)
510 {
511 free (ei);
512 return NULL;
513 }
514 #if defined (MAKE_WCSFTIME) && !defined (__HAVE_LOCALE_INFO_EXTENDED__)
515 len = mbsnrtowcs (ei->era_C, &era, c - era, len + 1, NULL);
516 #else
517 ERA_STRNCPY (ei->era_C, era, len);
518 era += len;
519 #endif
520 ei->era_C[len] = CQ('\0');
521 /* era_Y */
522 ++era;
523 c = ERA_STRCHR (era, ';');
524 if (!c)
525 c = ERA_STRCHR (era, '\0');
526 #if defined (MAKE_WCSFTIME) && !defined (__HAVE_LOCALE_INFO_EXTENDED__)
527 len = mbsnrtowcs (NULL, &era, c - era, 0, NULL);
528 if (len == (size_t) -1)
529 {
530 free (ei->era_C);
531 free (ei);
532 return NULL;
533 }
534 #else
535 len = c - era;
536 #endif
537 ei->era_Y = (CHAR *) malloc ((len + 1) * sizeof (CHAR));
538 if (!ei->era_Y)
539 {
540 free (ei->era_C);
541 free (ei);
542 return NULL;
543 }
544 #if defined (MAKE_WCSFTIME) && !defined (__HAVE_LOCALE_INFO_EXTENDED__)
545 len = mbsnrtowcs (ei->era_Y, &era, c - era, len + 1, NULL);
546 #else
547 ERA_STRNCPY (ei->era_Y, era, len);
548 era += len;
549 #endif
550 ei->era_Y[len] = CQ('\0');
551 return ei;
552 }
553 else
554 era = ERA_STRCHR (era, ';');
555 if (era)
556 ++era;
557 }
558 return NULL;
559 }
560
561 static void
free_era_info(era_info_t * ei)562 free_era_info (era_info_t *ei)
563 {
564 free (ei->era_C);
565 free (ei->era_Y);
566 free (ei);
567 }
568
569 typedef struct {
570 size_t num;
571 CHAR **digit;
572 CHAR *buffer;
573 } alt_digits_t;
574
575 static alt_digits_t *
576 #if defined (MAKE_WCSFTIME) && defined (__HAVE_LOCALE_INFO_EXTENDED__)
get_alt_digits(const wchar_t * alt_digits)577 get_alt_digits (const wchar_t *alt_digits)
578 #else
579 get_alt_digits (const char *alt_digits)
580 #endif
581 {
582 alt_digits_t *adi;
583 #if defined (MAKE_WCSFTIME) && defined (__HAVE_LOCALE_INFO_EXTENDED__)
584 const wchar_t *a, *e;
585 # define ALT_STRCHR(a,b) wcschr((a),(b))
586 # define ALT_STRCPY(a,b) wcscpy((a),(b))
587 # define ALT_STRLEN(a) wcslen(a)
588 #else
589 const char *a, *e;
590 # define ALT_STRCHR(a,b) strchr((a),(b))
591 # define ALT_STRCPY(a,b) strcpy((a),(b))
592 # define ALT_STRLEN(a) strlen(a)
593 #endif
594 CHAR *aa, *ae;
595 size_t len;
596
597 adi = (alt_digits_t *) calloc (1, sizeof (alt_digits_t));
598 if (!adi)
599 return NULL;
600
601 /* Compute number of alt_digits. */
602 adi->num = 1;
603 for (a = alt_digits; (e = ALT_STRCHR (a, ';')) != NULL; a = e + 1)
604 ++adi->num;
605 /* Allocate the `digit' array, which is an array of `num' pointers into
606 `buffer'. */
607 adi->digit = (CHAR **) calloc (adi->num, sizeof (CHAR *));
608 if (!adi->digit)
609 {
610 free (adi);
611 return NULL;
612 }
613 /* Compute memory required for `buffer'. */
614 #if defined (MAKE_WCSFTIME) && !defined (__HAVE_LOCALE_INFO_EXTENDED__)
615 len = mbstowcs (NULL, alt_digits, 0);
616 if (len == (size_t) -1)
617 {
618 free (adi->digit);
619 free (adi);
620 return NULL;
621 }
622 #else
623 len = ALT_STRLEN (alt_digits);
624 #endif
625 /* Allocate it. */
626 adi->buffer = (CHAR *) malloc ((len + 1) * sizeof (CHAR));
627 if (!adi->buffer)
628 {
629 free (adi->digit);
630 free (adi);
631 return NULL;
632 }
633 /* Store digits in it. */
634 #if defined (MAKE_WCSFTIME) && !defined (__HAVE_LOCALE_INFO_EXTENDED__)
635 mbstowcs (adi->buffer, alt_digits, len + 1);
636 #else
637 ALT_STRCPY (adi->buffer, alt_digits);
638 #endif
639 /* Store the pointers into `buffer' into the appropriate `digit' slot. */
640 for (len = 0, aa = adi->buffer; (ae = STRCHR (aa, CQ(';'))) != NULL;
641 ++len, aa = ae + 1)
642 {
643 *ae = '\0';
644 adi->digit[len] = aa;
645 }
646 adi->digit[len] = aa;
647 return adi;
648 }
649
650 static void
free_alt_digits(alt_digits_t * adi)651 free_alt_digits (alt_digits_t *adi)
652 {
653 free (adi->digit);
654 free (adi->buffer);
655 free (adi);
656 }
657
658 /* Return 0 if no alt_digit is available for a number.
659 Return -1 if buffer size isn't sufficient to hold alternative digit.
660 Return length of new digit otherwise. */
661 static int
conv_to_alt_digits(CHAR * buf,size_t bufsiz,unsigned num,alt_digits_t * adi)662 conv_to_alt_digits (CHAR *buf, size_t bufsiz, unsigned num, alt_digits_t *adi)
663 {
664 if (num < adi->num)
665 {
666 size_t len = STRLEN (adi->digit[num]);
667 if (bufsiz < len)
668 return -1;
669 STRCPY (buf, adi->digit[num]);
670 return (int) len;
671 }
672 return 0;
673 }
674
675 static size_t
__strftime(CHAR * s,size_t maxsize,const CHAR * format,const struct tm * tim_p,struct __locale_t * locale,era_info_t ** era_info,alt_digits_t ** alt_digits)676 __strftime (CHAR *s, size_t maxsize, const CHAR *format,
677 const struct tm *tim_p, struct __locale_t *locale,
678 era_info_t **era_info, alt_digits_t **alt_digits)
679 #else /* !_WANT_C99_TIME_FORMATS */
680 static size_t
681 __strftime (CHAR *s, size_t maxsize, const CHAR *format,
682 const struct tm *tim_p, struct __locale_t *locale)
683
684 #define __strftime(s,m,f,t,l,e,a) __strftime((s),(m),(f),(t),(l))
685 #endif /* !_WANT_C99_TIME_FORMATS */
686 {
687 size_t count = 0;
688 int len = 0;
689 const CHAR *ctloc;
690 #if defined (MAKE_WCSFTIME) && !defined (__HAVE_LOCALE_INFO_EXTENDED__)
691 CHAR ctlocbuf[CTLOCBUFLEN];
692 #endif
693 size_t i, ctloclen;
694 CHAR alt;
695 CHAR pad;
696 unsigned long width;
697 int tzset_called = 0;
698
699 const struct lc_time_T *_CurrentTimeLocale = __get_time_locale (locale);
700 for (;;)
701 {
702 while (*format && *format != CQ('%'))
703 {
704 if (count < maxsize - 1)
705 s[count++] = *format++;
706 else
707 return 0;
708 }
709 if (*format == CQ('\0'))
710 break;
711 format++;
712 pad = '\0';
713 width = 0;
714
715 /* POSIX-1.2008 feature: '0' and '+' modifiers require 0-padding with
716 slightly different semantics. */
717 if (*format == CQ('0') || *format == CQ('+'))
718 pad = *format++;
719
720 /* POSIX-1.2008 feature: A minimum field width can be specified. */
721 if (*format >= CQ('1') && *format <= CQ('9'))
722 {
723 CHAR *fp;
724 width = STRTOUL (format, &fp, 10);
725 format = fp;
726 }
727
728 alt = CQ('\0');
729 if (*format == CQ('E'))
730 {
731 alt = *format++;
732 #ifdef _WANT_C99_TIME_FORMATS
733 #if defined (MAKE_WCSFTIME) && defined (__HAVE_LOCALE_INFO_EXTENDED__)
734 if (!*era_info && *_CurrentTimeLocale->wera)
735 *era_info = get_era_info (tim_p, _CurrentTimeLocale->wera);
736 #else
737 if (!*era_info && *_CurrentTimeLocale->era)
738 *era_info = get_era_info (tim_p, _CurrentTimeLocale->era);
739 #endif
740 #endif /* _WANT_C99_TIME_FORMATS */
741 }
742 else if (*format == CQ('O'))
743 {
744 alt = *format++;
745 #ifdef _WANT_C99_TIME_FORMATS
746 #if defined (MAKE_WCSFTIME) && defined (__HAVE_LOCALE_INFO_EXTENDED__)
747 if (!*alt_digits && *_CurrentTimeLocale->walt_digits)
748 *alt_digits = get_alt_digits (_CurrentTimeLocale->walt_digits);
749 #else
750 if (!*alt_digits && *_CurrentTimeLocale->alt_digits)
751 *alt_digits = get_alt_digits (_CurrentTimeLocale->alt_digits);
752 #endif
753 #endif /* _WANT_C99_TIME_FORMATS */
754 }
755
756 switch (*format)
757 {
758 case CQ('a'):
759 _ctloc (wday[tim_p->tm_wday]);
760 for (i = 0; i < ctloclen; i++)
761 {
762 if (count < maxsize - 1)
763 s[count++] = ctloc[i];
764 else
765 return 0;
766 }
767 break;
768 case CQ('A'):
769 _ctloc (weekday[tim_p->tm_wday]);
770 for (i = 0; i < ctloclen; i++)
771 {
772 if (count < maxsize - 1)
773 s[count++] = ctloc[i];
774 else
775 return 0;
776 }
777 break;
778 case CQ('b'):
779 case CQ('h'):
780 _ctloc (mon[tim_p->tm_mon]);
781 for (i = 0; i < ctloclen; i++)
782 {
783 if (count < maxsize - 1)
784 s[count++] = ctloc[i];
785 else
786 return 0;
787 }
788 break;
789 case CQ('B'):
790 _ctloc (month[tim_p->tm_mon]);
791 for (i = 0; i < ctloclen; i++)
792 {
793 if (count < maxsize - 1)
794 s[count++] = ctloc[i];
795 else
796 return 0;
797 }
798 break;
799 case CQ('c'):
800 #ifdef _WANT_C99_TIME_FORMATS
801 if (alt == 'E' && *era_info && *_CurrentTimeLocale->era_d_t_fmt)
802 _ctloc (era_d_t_fmt);
803 else
804 #endif /* _WANT_C99_TIME_FORMATS */
805 _ctloc (c_fmt);
806 goto recurse;
807 case CQ('r'):
808 _ctloc (ampm_fmt);
809 goto recurse;
810 case CQ('x'):
811 #ifdef _WANT_C99_TIME_FORMATS
812 if (alt == 'E' && *era_info && *_CurrentTimeLocale->era_d_fmt)
813 _ctloc (era_d_fmt);
814 else
815 #endif /* _WANT_C99_TIME_FORMATS */
816 _ctloc (x_fmt);
817 goto recurse;
818 case CQ('X'):
819 #ifdef _WANT_C99_TIME_FORMATS
820 if (alt == 'E' && *era_info && *_CurrentTimeLocale->era_t_fmt)
821 _ctloc (era_t_fmt);
822 else
823 #endif /* _WANT_C99_TIME_FORMATS */
824 _ctloc (X_fmt);
825 recurse:
826 if (*ctloc)
827 {
828 /* Recurse to avoid need to replicate %Y formation. */
829 len = __strftime (&s[count], maxsize - count, ctloc, tim_p,
830 locale, era_info, alt_digits);
831 if (len > 0)
832 count += len;
833 else
834 return 0;
835 }
836 break;
837 case CQ('C'):
838 {
839 /* Examples of (tm_year + YEAR_BASE) that show how %Y == %C%y
840 with 32-bit int.
841 %Y %C %y
842 2147485547 21474855 47
843 10000 100 00
844 9999 99 99
845 0999 09 99
846 0099 00 99
847 0001 00 01
848 0000 00 00
849 -001 -0 01
850 -099 -0 99
851 -999 -9 99
852 -1000 -10 00
853 -10000 -100 00
854 -2147481748 -21474817 48
855
856 Be careful of both overflow and sign adjustment due to the
857 asymmetric range of years.
858 */
859 #ifdef _WANT_C99_TIME_FORMATS
860 if (alt == 'E' && *era_info)
861 len = t_snprintf (&s[count], maxsize - count, CQ("%" SFLG "s"),
862 (*era_info)->era_C);
863 else
864 #endif /* _WANT_C99_TIME_FORMATS */
865 {
866 CHAR *fmt = CQ("%s%.*d");
867 char *pos = "";
868 int neg = tim_p->tm_year < -YEAR_BASE;
869 int century = tim_p->tm_year >= 0
870 ? tim_p->tm_year / 100 + YEAR_BASE / 100
871 : abs (tim_p->tm_year + YEAR_BASE) / 100;
872 if (pad) /* '0' or '+' */
873 {
874 fmt = CQ("%s%0.*d");
875 if (century >= 100 && pad == CQ('+'))
876 pos = "+";
877 }
878 if (width < 2)
879 width = 2;
880 len = t_snprintf (&s[count], maxsize - count, fmt,
881 neg ? "-" : pos, width - neg, century);
882 }
883 CHECK_LENGTH ();
884 }
885 break;
886 case CQ('d'):
887 case CQ('e'):
888 #ifdef _WANT_C99_TIME_FORMATS
889 if (alt == CQ('O') && *alt_digits)
890 {
891 if (tim_p->tm_mday < 10)
892 {
893 if (*format == CQ('d'))
894 {
895 if (maxsize - count < 2) return 0;
896 len = conv_to_alt_digits (&s[count], maxsize - count,
897 0, *alt_digits);
898 CHECK_LENGTH ();
899 }
900 if (*format == CQ('e') || len == 0)
901 s[count++] = CQ(' ');
902 }
903 len = conv_to_alt_digits (&s[count], maxsize - count,
904 tim_p->tm_mday, *alt_digits);
905 CHECK_LENGTH ();
906 if (len > 0)
907 break;
908 }
909 #endif /* _WANT_C99_TIME_FORMATS */
910 len = t_snprintf (&s[count], maxsize - count,
911 *format == CQ('d') ? CQ("%.2d") : CQ("%2d"),
912 tim_p->tm_mday);
913 CHECK_LENGTH ();
914 break;
915 case CQ('D'):
916 /* %m/%d/%y */
917 len = t_snprintf (&s[count], maxsize - count,
918 CQ("%.2d/%.2d/%.2d"),
919 tim_p->tm_mon + 1, tim_p->tm_mday,
920 tim_p->tm_year >= 0 ? tim_p->tm_year % 100
921 : abs (tim_p->tm_year + YEAR_BASE) % 100);
922 CHECK_LENGTH ();
923 break;
924 case CQ('F'):
925 { /* %F is equivalent to "%+4Y-%m-%d", flags and width can change
926 that. Recurse to avoid need to replicate %Y formation. */
927 CHAR fmtbuf[32], *fmt = fmtbuf;
928
929 *fmt++ = CQ('%');
930 if (pad) /* '0' or '+' */
931 *fmt++ = pad;
932 else
933 *fmt++ = '+';
934 if (!pad)
935 width = 10;
936 if (width < 6)
937 width = 6;
938 width -= 6;
939 if (width)
940 {
941 len = t_snprintf (fmt, fmtbuf + 32 - fmt, CQ("%lu"), width);
942 if (len > 0)
943 fmt += len;
944 }
945 STRCPY (fmt, CQ("Y-%m-%d"));
946 len = __strftime (&s[count], maxsize - count, fmtbuf, tim_p,
947 locale, era_info, alt_digits);
948 if (len > 0)
949 count += len;
950 else
951 return 0;
952 }
953 break;
954 case CQ('g'):
955 /* Be careful of both overflow and negative years, thanks to
956 the asymmetric range of years. */
957 {
958 int adjust = iso_year_adjust (tim_p);
959 int year = tim_p->tm_year >= 0 ? tim_p->tm_year % 100
960 : abs (tim_p->tm_year + YEAR_BASE) % 100;
961 if (adjust < 0 && tim_p->tm_year <= -YEAR_BASE)
962 adjust = 1;
963 else if (adjust > 0 && tim_p->tm_year < -YEAR_BASE)
964 adjust = -1;
965 len = t_snprintf (&s[count], maxsize - count, CQ("%.2d"),
966 ((year + adjust) % 100 + 100) % 100);
967 CHECK_LENGTH ();
968 }
969 break;
970 case CQ('G'):
971 {
972 /* See the comments for 'C' and 'Y'; this is a variable length
973 field. Although there is no requirement for a minimum number
974 of digits, we use 4 for consistency with 'Y'. */
975 int sign = tim_p->tm_year < -YEAR_BASE;
976 int adjust = iso_year_adjust (tim_p);
977 int century = tim_p->tm_year >= 0
978 ? tim_p->tm_year / 100 + YEAR_BASE / 100
979 : abs (tim_p->tm_year + YEAR_BASE) / 100;
980 int year = tim_p->tm_year >= 0 ? tim_p->tm_year % 100
981 : abs (tim_p->tm_year + YEAR_BASE) % 100;
982 if (adjust < 0 && tim_p->tm_year <= -YEAR_BASE)
983 sign = adjust = 1;
984 else if (adjust > 0 && sign)
985 adjust = -1;
986 year += adjust;
987 if (year == -1)
988 {
989 year = 99;
990 --century;
991 }
992 else if (year == 100)
993 {
994 year = 0;
995 ++century;
996 }
997 CHAR fmtbuf[10], *fmt = fmtbuf;
998 /* int potentially overflows, so use unsigned instead. */
999 unsigned p_year = century * 100 + year;
1000 if (sign)
1001 *fmt++ = CQ('-');
1002 else if (pad == CQ('+') && p_year >= 10000)
1003 {
1004 *fmt++ = CQ('+');
1005 sign = 1;
1006 }
1007 if (width && sign)
1008 --width;
1009 *fmt++ = CQ('%');
1010 if (pad)
1011 *fmt++ = CQ('0');
1012 STRCPY (fmt, CQ(".*u"));
1013 len = t_snprintf (&s[count], maxsize - count, fmtbuf, width, p_year);
1014 if (len < 0 || (count+=len) >= maxsize)
1015 return 0;
1016 }
1017 break;
1018 case CQ('H'):
1019 #ifdef _WANT_C99_TIME_FORMATS
1020 if (alt == CQ('O') && *alt_digits)
1021 {
1022 len = conv_to_alt_digits (&s[count], maxsize - count,
1023 tim_p->tm_hour, *alt_digits);
1024 CHECK_LENGTH ();
1025 if (len > 0)
1026 break;
1027 }
1028 #endif /* _WANT_C99_TIME_FORMATS */
1029 __PICOLIBC_FALLTHROUGH;
1030 case CQ('k'): /* newlib extension */
1031 len = t_snprintf (&s[count], maxsize - count,
1032 *format == CQ('k') ? CQ("%2d") : CQ("%.2d"),
1033 tim_p->tm_hour);
1034 CHECK_LENGTH ();
1035 break;
1036 case CQ('l'): /* newlib extension */
1037 if (alt == CQ('O'))
1038 alt = CQ('\0');
1039 __PICOLIBC_FALLTHROUGH;
1040 case CQ('I'):
1041 {
1042 register int h12;
1043 h12 = (tim_p->tm_hour == 0 || tim_p->tm_hour == 12) ?
1044 12 : tim_p->tm_hour % 12;
1045 #ifdef _WANT_C99_TIME_FORMATS
1046 if (alt != CQ('O') || !*alt_digits
1047 || !(len = conv_to_alt_digits (&s[count], maxsize - count,
1048 h12, *alt_digits)))
1049 #endif /* _WANT_C99_TIME_FORMATS */
1050 len = t_snprintf (&s[count], maxsize - count,
1051 *format == CQ('I') ? CQ("%.2d") : CQ("%2d"), h12);
1052 CHECK_LENGTH ();
1053 }
1054 break;
1055 case CQ('j'):
1056 len = t_snprintf (&s[count], maxsize - count, CQ("%.3d"),
1057 tim_p->tm_yday + 1);
1058 CHECK_LENGTH ();
1059 break;
1060 case CQ('m'):
1061 #ifdef _WANT_C99_TIME_FORMATS
1062 if (alt != CQ('O') || !*alt_digits
1063 || !(len = conv_to_alt_digits (&s[count], maxsize - count,
1064 tim_p->tm_mon + 1, *alt_digits)))
1065 #endif /* _WANT_C99_TIME_FORMATS */
1066 len = t_snprintf (&s[count], maxsize - count, CQ("%.2d"),
1067 tim_p->tm_mon + 1);
1068 CHECK_LENGTH ();
1069 break;
1070 case CQ('M'):
1071 #ifdef _WANT_C99_TIME_FORMATS
1072 if (alt != CQ('O') || !*alt_digits
1073 || !(len = conv_to_alt_digits (&s[count], maxsize - count,
1074 tim_p->tm_min, *alt_digits)))
1075 #endif /* _WANT_C99_TIME_FORMATS */
1076 len = t_snprintf (&s[count], maxsize - count, CQ("%.2d"),
1077 tim_p->tm_min);
1078 CHECK_LENGTH ();
1079 break;
1080 case CQ('n'):
1081 if (count < maxsize - 1)
1082 s[count++] = CQ('\n');
1083 else
1084 return 0;
1085 break;
1086 case CQ('p'):
1087 case CQ('P'):
1088 _ctloc (am_pm[tim_p->tm_hour < 12 ? 0 : 1]);
1089 for (i = 0; i < ctloclen; i++)
1090 {
1091 if (count < maxsize - 1)
1092 s[count++] = (*format == CQ('P') ? (CHAR) TOLOWER (ctloc[i])
1093 : ctloc[i]);
1094 else
1095 return 0;
1096 }
1097 break;
1098 case CQ('q'): /* GNU quarter year */
1099 len = t_snprintf (&s[count], maxsize - count, CQ("%.1d"),
1100 tim_p->tm_mon / 3 + 1);
1101 CHECK_LENGTH ();
1102 break;
1103 case CQ('R'):
1104 len = t_snprintf (&s[count], maxsize - count, CQ("%.2d:%.2d"),
1105 tim_p->tm_hour, tim_p->tm_min);
1106 CHECK_LENGTH ();
1107 break;
1108 case CQ('s'):
1109 /*
1110 * From:
1111 * The Open Group Base Specifications Issue 7
1112 * IEEE Std 1003.1, 2013 Edition
1113 * Copyright (c) 2001-2013 The IEEE and The Open Group
1114 * XBD Base Definitions
1115 * 4. General Concepts
1116 * 4.15 Seconds Since the Epoch
1117 * A value that approximates the number of seconds that have elapsed since the
1118 * Epoch. A Coordinated Universal Time name (specified in terms of seconds
1119 * (tm_sec), minutes (tm_min), hours (tm_hour), days since January 1 of the year
1120 * (tm_yday), and calendar year minus 1900 (tm_year)) is related to a time
1121 * represented as seconds since the Epoch, according to the expression below.
1122 * If the year is <1970 or the value is negative, the relationship is undefined.
1123 * If the year is >=1970 and the value is non-negative, the value is related to a
1124 * Coordinated Universal Time name according to the C-language expression, where
1125 * tm_sec, tm_min, tm_hour, tm_yday, and tm_year are all integer types:
1126 * tm_sec + tm_min*60 + tm_hour*3600 + tm_yday*86400 +
1127 * (tm_year-70)*31536000 + ((tm_year-69)/4)*86400 -
1128 * ((tm_year-1)/100)*86400 + ((tm_year+299)/400)*86400
1129 * OR
1130 * ((((tm_year-69)/4 - (tm_year-1)/100 + (tm_year+299)/400 +
1131 * (tm_year-70)*365 + tm_yday)*24 + tm_hour)*60 + tm_min)*60 + tm_sec
1132 */
1133 /* modified from %z case by hoisting offset outside if block and initializing */
1134 {
1135 long offset = 0; /* offset < 0 => W of GMT, > 0 => E of GMT:
1136 subtract to get UTC */
1137
1138 if (tim_p->tm_isdst >= 0)
1139 {
1140 TZ_LOCK;
1141 if (!tzset_called)
1142 {
1143 _tzset_unlocked ();
1144 tzset_called = 1;
1145 }
1146
1147 #if defined (__TM_GMTOFF)
1148 offset = tim_p->__TM_GMTOFF;
1149 #else
1150 __tzinfo_type *tz = __gettzinfo ();
1151 /* The sign of this is exactly opposite the envvar TZ. We
1152 could directly use the global _timezone for tm_isdst==0,
1153 but have to use __tzrule for daylight savings. */
1154 offset = -tz->__tzrule[tim_p->tm_isdst > 0].offset;
1155 #endif
1156 TZ_UNLOCK;
1157 }
1158 len = t_snprintf (&s[count], maxsize - count, CQ("%lld"),
1159 (((((long long)tim_p->tm_year - 69)/4
1160 - (tim_p->tm_year - 1)/100
1161 + (tim_p->tm_year + 299)/400
1162 + (tim_p->tm_year - 70)*365 + tim_p->tm_yday)*24
1163 + tim_p->tm_hour)*60 + tim_p->tm_min)*60
1164 + tim_p->tm_sec - offset);
1165 CHECK_LENGTH ();
1166 }
1167 break;
1168 case CQ('S'):
1169 #ifdef _WANT_C99_TIME_FORMATS
1170 if (alt != CQ('O') || !*alt_digits
1171 || !(len = conv_to_alt_digits (&s[count], maxsize - count,
1172 tim_p->tm_sec, *alt_digits)))
1173 #endif /* _WANT_C99_TIME_FORMATS */
1174 len = t_snprintf (&s[count], maxsize - count, CQ("%.2d"),
1175 tim_p->tm_sec);
1176 CHECK_LENGTH ();
1177 break;
1178 case CQ('t'):
1179 if (count < maxsize - 1)
1180 s[count++] = CQ('\t');
1181 else
1182 return 0;
1183 break;
1184 case CQ('T'):
1185 len = t_snprintf (&s[count], maxsize - count, CQ("%.2d:%.2d:%.2d"),
1186 tim_p->tm_hour, tim_p->tm_min, tim_p->tm_sec);
1187 CHECK_LENGTH ();
1188 break;
1189 case CQ('u'):
1190 #ifdef _WANT_C99_TIME_FORMATS
1191 if (alt == CQ('O') && *alt_digits)
1192 {
1193 len = conv_to_alt_digits (&s[count], maxsize - count,
1194 tim_p->tm_wday == 0 ? 7
1195 : tim_p->tm_wday,
1196 *alt_digits);
1197 CHECK_LENGTH ();
1198 if (len > 0)
1199 break;
1200 }
1201 #endif /* _WANT_C99_TIME_FORMATS */
1202 if (count < maxsize - 1)
1203 {
1204 if (tim_p->tm_wday == 0)
1205 s[count++] = CQ('7');
1206 else
1207 s[count++] = CQ('0') + tim_p->tm_wday;
1208 }
1209 else
1210 return 0;
1211 break;
1212 case CQ('U'):
1213 #ifdef _WANT_C99_TIME_FORMATS
1214 if (alt != CQ('O') || !*alt_digits
1215 || !(len = conv_to_alt_digits (&s[count], maxsize - count,
1216 (tim_p->tm_yday + 7 -
1217 tim_p->tm_wday) / 7,
1218 *alt_digits)))
1219 #endif /* _WANT_C99_TIME_FORMATS */
1220 len = t_snprintf (&s[count], maxsize - count, CQ("%.2d"),
1221 (tim_p->tm_yday + 7 -
1222 tim_p->tm_wday) / 7);
1223 CHECK_LENGTH ();
1224 break;
1225 case CQ('V'):
1226 {
1227 int adjust = iso_year_adjust (tim_p);
1228 int wday = (tim_p->tm_wday) ? tim_p->tm_wday - 1 : 6;
1229 int week = (tim_p->tm_yday + 10 - wday) / 7;
1230 if (adjust > 0)
1231 week = 1;
1232 else if (adjust < 0)
1233 /* Previous year has 53 weeks if current year starts on
1234 Fri, and also if current year starts on Sat and
1235 previous year was leap year. */
1236 week = 52 + (4 >= (wday - tim_p->tm_yday
1237 - isleap (tim_p->tm_year
1238 + (YEAR_BASE - 1
1239 - (tim_p->tm_year < 0
1240 ? 0 : 2000)))));
1241 #ifdef _WANT_C99_TIME_FORMATS
1242 if (alt != CQ('O') || !*alt_digits
1243 || !(len = conv_to_alt_digits (&s[count], maxsize - count,
1244 week, *alt_digits)))
1245 #endif /* _WANT_C99_TIME_FORMATS */
1246 len = t_snprintf (&s[count], maxsize - count, CQ("%.2d"), week);
1247 CHECK_LENGTH ();
1248 }
1249 break;
1250 case CQ('v'): /* BSD/OSX/Ruby extension VMS/Oracle date format
1251 from Arnold Robbins strftime version 3.0 */
1252 { /* %v is equivalent to "%e-%b-%Y", flags and width can change year
1253 format. Recurse to avoid need to replicate %b and %Y formation. */
1254 CHAR fmtbuf[32], *fmt = fmtbuf;
1255 STRCPY (fmt, CQ("%e-%b-%"));
1256 fmt += STRLEN (fmt);
1257 if (pad) /* '0' or '+' */
1258 *fmt++ = pad;
1259 else
1260 *fmt++ = '+';
1261 if (!pad)
1262 width = 10;
1263 if (width < 6)
1264 width = 6;
1265 width -= 6;
1266 if (width)
1267 {
1268 len = t_snprintf (fmt, fmtbuf + 32 - fmt, CQ("%lu"), width);
1269 if (len > 0)
1270 fmt += len;
1271 }
1272 STRCPY (fmt, CQ("Y"));
1273 len = __strftime (&s[count], maxsize - count, fmtbuf, tim_p,
1274 locale, era_info, alt_digits);
1275 if (len > 0)
1276 count += len;
1277 else
1278 return 0;
1279 }
1280 break;
1281 case CQ('w'):
1282 #ifdef _WANT_C99_TIME_FORMATS
1283 if (alt == CQ('O') && *alt_digits)
1284 {
1285 len = conv_to_alt_digits (&s[count], maxsize - count,
1286 tim_p->tm_wday, *alt_digits);
1287 CHECK_LENGTH ();
1288 if (len > 0)
1289 break;
1290 }
1291 #endif /* _WANT_C99_TIME_FORMATS */
1292 if (count < maxsize - 1)
1293 s[count++] = CQ('0') + tim_p->tm_wday;
1294 else
1295 return 0;
1296 break;
1297 case CQ('W'):
1298 {
1299 int wday = (tim_p->tm_wday) ? tim_p->tm_wday - 1 : 6;
1300 wday = (tim_p->tm_yday + 7 - wday) / 7;
1301 #ifdef _WANT_C99_TIME_FORMATS
1302 if (alt != CQ('O') || !*alt_digits
1303 || !(len = conv_to_alt_digits (&s[count], maxsize - count,
1304 wday, *alt_digits)))
1305 #endif /* _WANT_C99_TIME_FORMATS */
1306 len = t_snprintf (&s[count], maxsize - count, CQ("%.2d"), wday);
1307 CHECK_LENGTH ();
1308 }
1309 break;
1310 case CQ('y'):
1311 {
1312 #ifdef _WANT_C99_TIME_FORMATS
1313 if (alt == 'E' && *era_info)
1314 len = t_snprintf (&s[count], maxsize - count, CQ("%d"),
1315 (*era_info)->year);
1316 else
1317 #endif /* _WANT_C99_TIME_FORMATS */
1318 {
1319 /* Be careful of both overflow and negative years, thanks to
1320 the asymmetric range of years. */
1321 int year = tim_p->tm_year >= 0 ? tim_p->tm_year % 100
1322 : abs (tim_p->tm_year + YEAR_BASE) % 100;
1323 #ifdef _WANT_C99_TIME_FORMATS
1324 if (alt != CQ('O') || !*alt_digits
1325 || !(len = conv_to_alt_digits (&s[count], maxsize - count,
1326 year, *alt_digits)))
1327 #endif /* _WANT_C99_TIME_FORMATS */
1328 len = t_snprintf (&s[count], maxsize - count, CQ("%.2d"),
1329 year);
1330 }
1331 CHECK_LENGTH ();
1332 }
1333 break;
1334 case CQ('Y'):
1335 #ifdef _WANT_C99_TIME_FORMATS
1336 if (alt == 'E' && *era_info)
1337 {
1338 ctloc = (*era_info)->era_Y;
1339 goto recurse;
1340 }
1341 else
1342 #endif /* _WANT_C99_TIME_FORMATS */
1343 {
1344 CHAR fmtbuf[10], *fmt = fmtbuf;
1345 int sign = tim_p->tm_year < -YEAR_BASE;
1346 /* int potentially overflows, so use unsigned instead. */
1347 register unsigned year = (unsigned) tim_p->tm_year
1348 + (unsigned) YEAR_BASE;
1349 if (sign)
1350 {
1351 *fmt++ = CQ('-');
1352 year = UINT_MAX - year + 1;
1353 }
1354 else if (pad == CQ('+') && year >= 10000)
1355 {
1356 *fmt++ = CQ('+');
1357 sign = 1;
1358 }
1359 if (width && sign)
1360 --width;
1361 *fmt++ = CQ('%');
1362 if (pad)
1363 *fmt++ = CQ('0');
1364 STRCPY (fmt, CQ(".*u"));
1365 len = t_snprintf (&s[count], maxsize - count, fmtbuf, width,
1366 year);
1367 CHECK_LENGTH ();
1368 }
1369 break;
1370 case CQ('z'):
1371 if (tim_p->tm_isdst >= 0)
1372 {
1373 long offset;
1374
1375 TZ_LOCK;
1376 if (!tzset_called)
1377 {
1378 _tzset_unlocked ();
1379 tzset_called = 1;
1380 }
1381
1382 #if defined (__TM_GMTOFF)
1383 offset = tim_p->__TM_GMTOFF;
1384 #else
1385 __tzinfo_type *tz = __gettzinfo ();
1386 /* The sign of this is exactly opposite the envvar TZ. We
1387 could directly use the global _timezone for tm_isdst==0,
1388 but have to use __tzrule for daylight savings. */
1389 offset = -tz->__tzrule[tim_p->tm_isdst > 0].offset;
1390 #endif
1391 TZ_UNLOCK;
1392 len = t_snprintf (&s[count], maxsize - count, CQ("%+03ld%.2ld"),
1393 offset / SECSPERHOUR,
1394 labs (offset / SECSPERMIN) % 60L);
1395 CHECK_LENGTH ();
1396 }
1397 break;
1398 case CQ('Z'):
1399 if (tim_p->tm_isdst >= 0)
1400 {
1401 size_t size;
1402 const char *tznam = NULL;
1403
1404 TZ_LOCK;
1405 if (!tzset_called)
1406 {
1407 _tzset_unlocked ();
1408 tzset_called = 1;
1409 }
1410 #if defined (__TM_ZONE)
1411 tznam = tim_p->__TM_ZONE;
1412 #endif
1413 if (!tznam)
1414 tznam = tzname[tim_p->tm_isdst > 0];
1415 /* Note that in case of wcsftime this loop only works for
1416 timezone abbreviations using the portable codeset (aka ASCII).
1417 This seems to be the case, but if that ever changes, this
1418 loop needs revisiting. */
1419 size = strlen (tznam);
1420 for (i = 0; i < size; i++)
1421 {
1422 if (count < maxsize - 1)
1423 s[count++] = tznam[i];
1424 else
1425 {
1426 TZ_UNLOCK;
1427 return 0;
1428 }
1429 }
1430 TZ_UNLOCK;
1431 }
1432 break;
1433 case CQ('%'):
1434 if (count < maxsize - 1)
1435 s[count++] = CQ('%');
1436 else
1437 return 0;
1438 break;
1439 default:
1440 return 0;
1441 }
1442 if (*format)
1443 format++;
1444 else
1445 break;
1446 }
1447 if (maxsize)
1448 s[count] = CQ('\0');
1449
1450 return count;
1451 }
1452
1453 size_t
strftime(CHAR * __restrict s,size_t maxsize,const CHAR * __restrict format,const struct tm * __restrict tim_p)1454 strftime (CHAR *__restrict s,
1455 size_t maxsize,
1456 const CHAR *__restrict format,
1457 const struct tm *__restrict tim_p)
1458 {
1459 #ifdef _WANT_C99_TIME_FORMATS
1460 era_info_t *era_info = NULL;
1461 alt_digits_t *alt_digits = NULL;
1462 size_t ret = __strftime (s, maxsize, format, tim_p, __get_current_locale (),
1463 &era_info, &alt_digits);
1464 if (era_info)
1465 free_era_info (era_info);
1466 if (alt_digits)
1467 free_alt_digits (alt_digits);
1468 return ret;
1469 #else /* !_WANT_C99_TIME_FORMATS */
1470 return __strftime (s, maxsize, format, tim_p, __get_current_locale (),
1471 NULL, NULL);
1472 #endif /* !_WANT_C99_TIME_FORMATS */
1473 }
1474
1475 size_t
strftime_l(CHAR * __restrict s,size_t maxsize,const CHAR * __restrict format,const struct tm * __restrict tim_p,struct __locale_t * locale)1476 strftime_l (CHAR *__restrict s, size_t maxsize, const CHAR *__restrict format,
1477 const struct tm *__restrict tim_p, struct __locale_t *locale)
1478 {
1479 #ifdef _WANT_C99_TIME_FORMATS
1480 era_info_t *era_info = NULL;
1481 alt_digits_t *alt_digits = NULL;
1482 size_t ret = __strftime (s, maxsize, format, tim_p, locale,
1483 &era_info, &alt_digits);
1484 if (era_info)
1485 free_era_info (era_info);
1486 if (alt_digits)
1487 free_alt_digits (alt_digits);
1488 return ret;
1489 #else /* !_WANT_C99_TIME_FORMATS */
1490 return __strftime (s, maxsize, format, tim_p, locale, NULL, NULL);
1491 #endif /* !_WANT_C99_TIME_FORMATS */
1492 }
1493
1494 /* The remainder of this file can serve as a regression test. Compile
1495 * with -D_REGRESSION_TEST. */
1496 #if defined(_REGRESSION_TEST) /* [Test code: */
1497
1498 /* This test code relies on ANSI C features, in particular on the ability
1499 * of adjacent strings to be pasted together into one string. */
1500
1501 /* Test output buffer size (should be larger than all expected results) */
1502 #define OUTSIZE 256
1503
1504 struct test {
1505 CHAR *fmt; /* Testing format */
1506 size_t max; /* Testing maxsize */
1507 size_t ret; /* Expected return value */
1508 CHAR *out; /* Expected output string */
1509 };
1510 struct list {
1511 const struct tm *tms; /* Time used for these vectors */
1512 const struct test *vec; /* Test vectors */
1513 int cnt; /* Number of vectors */
1514 };
1515
1516 const char TZ[]="TZ=EST5EDT";
1517
1518 /* Define list of test inputs and expected outputs, for the given time zone
1519 * and time. */
1520 const struct tm tm0 = {
1521 /* Tue Dec 30 10:53:47 EST 2008 (time_t=1230648827) */
1522 .tm_sec = 47,
1523 .tm_min = 53,
1524 .tm_hour = 9,
1525 .tm_mday = 30,
1526 .tm_mon = 11,
1527 .tm_year = 108,
1528 .tm_wday = 2,
1529 .tm_yday = 364,
1530 .tm_isdst = 0
1531 };
1532 const struct test Vec0[] = {
1533 /* Testing fields one at a time, expecting to pass, using exact
1534 * allowed length as what is needed. */
1535 /* Using tm0 for time: */
1536 #define EXP(s) sizeof(s)/sizeof(CHAR)-1, s
1537 { CQ("%a"), 3+1, EXP(CQ("Tue")) },
1538 { CQ("%A"), 7+1, EXP(CQ("Tuesday")) },
1539 { CQ("%b"), 3+1, EXP(CQ("Dec")) },
1540 { CQ("%B"), 8+1, EXP(CQ("December")) },
1541 { CQ("%c"), 24+1, EXP(CQ("Tue Dec 30 09:53:47 2008")) },
1542 { CQ("%C"), 2+1, EXP(CQ("20")) },
1543 { CQ("%d"), 2+1, EXP(CQ("30")) },
1544 { CQ("%D"), 8+1, EXP(CQ("12/30/08")) },
1545 { CQ("%e"), 2+1, EXP(CQ("30")) },
1546 { CQ("%F"), 10+1, EXP(CQ("2008-12-30")) },
1547 { CQ("%g"), 2+1, EXP(CQ("09")) },
1548 { CQ("%G"), 4+1, EXP(CQ("2009")) },
1549 { CQ("%h"), 3+1, EXP(CQ("Dec")) },
1550 { CQ("%H"), 2+1, EXP(CQ("09")) },
1551 { CQ("%I"), 2+1, EXP(CQ("09")) },
1552 { CQ("%j"), 3+1, EXP(CQ("365")) },
1553 { CQ("%k"), 2+1, EXP(CQ(" 9")) },
1554 { CQ("%l"), 2+1, EXP(CQ(" 9")) },
1555 { CQ("%m"), 2+1, EXP(CQ("12")) },
1556 { CQ("%M"), 2+1, EXP(CQ("53")) },
1557 { CQ("%n"), 1+1, EXP(CQ("\n")) },
1558 { CQ("%p"), 2+1, EXP(CQ("AM")) },
1559 { CQ("%q"), 1+1, EXP(CQ("4")) },
1560 { CQ("%r"), 11+1, EXP(CQ("09:53:47 AM")) },
1561 { CQ("%R"), 5+1, EXP(CQ("09:53")) },
1562 { CQ("%s"), 2+1, EXP(CQ("1230648827")) },
1563 { CQ("%S"), 2+1, EXP(CQ("47")) },
1564 { CQ("%t"), 1+1, EXP(CQ("\t")) },
1565 { CQ("%T"), 8+1, EXP(CQ("09:53:47")) },
1566 { CQ("%u"), 1+1, EXP(CQ("2")) },
1567 { CQ("%U"), 2+1, EXP(CQ("52")) },
1568 { CQ("%V"), 2+1, EXP(CQ("01")) },
1569 { CQ("%v"), 11+1, EXP(CQ("30-Dec-2008")) },
1570 { CQ("%w"), 1+1, EXP(CQ("2")) },
1571 { CQ("%W"), 2+1, EXP(CQ("52")) },
1572 { CQ("%x"), 8+1, EXP(CQ("12/30/08")) },
1573 { CQ("%X"), 8+1, EXP(CQ("09:53:47")) },
1574 { CQ("%y"), 2+1, EXP(CQ("08")) },
1575 { CQ("%Y"), 4+1, EXP(CQ("2008")) },
1576 { CQ("%z"), 5+1, EXP(CQ("-0500")) },
1577 { CQ("%Z"), 3+1, EXP(CQ("EST")) },
1578 { CQ("%%"), 1+1, EXP(CQ("%")) },
1579 #undef EXP
1580 };
1581 /* Define list of test inputs and expected outputs, for the given time zone
1582 * and time. */
1583 const struct tm tm1 = {
1584 /* Wed Jul 2 23:01:13 EDT 2008 (time_t=1215054073) */
1585 .tm_sec = 13,
1586 .tm_min = 1,
1587 .tm_hour = 23,
1588 .tm_mday = 2,
1589 .tm_mon = 6,
1590 .tm_year = 108,
1591 .tm_wday = 3,
1592 .tm_yday = 183,
1593 .tm_isdst = 1
1594 };
1595 const struct test Vec1[] = {
1596 /* Testing fields one at a time, expecting to pass, using exact
1597 * allowed length as what is needed. */
1598 /* Using tm1 for time: */
1599 #define EXP(s) sizeof(s)/sizeof(CHAR)-1, s
1600 { CQ("%a"), 3+1, EXP(CQ("Wed")) },
1601 { CQ("%A"), 9+1, EXP(CQ("Wednesday")) },
1602 { CQ("%b"), 3+1, EXP(CQ("Jul")) },
1603 { CQ("%B"), 4+1, EXP(CQ("July")) },
1604 { CQ("%c"), 24+1, EXP(CQ("Wed Jul 2 23:01:13 2008")) },
1605 { CQ("%C"), 2+1, EXP(CQ("20")) },
1606 { CQ("%d"), 2+1, EXP(CQ("02")) },
1607 { CQ("%D"), 8+1, EXP(CQ("07/02/08")) },
1608 { CQ("%e"), 2+1, EXP(CQ(" 2")) },
1609 { CQ("%F"), 10+1, EXP(CQ("2008-07-02")) },
1610 { CQ("%g"), 2+1, EXP(CQ("08")) },
1611 { CQ("%G"), 4+1, EXP(CQ("2008")) },
1612 { CQ("%h"), 3+1, EXP(CQ("Jul")) },
1613 { CQ("%H"), 2+1, EXP(CQ("23")) },
1614 { CQ("%I"), 2+1, EXP(CQ("11")) },
1615 { CQ("%j"), 3+1, EXP(CQ("184")) },
1616 { CQ("%k"), 2+1, EXP(CQ("23")) },
1617 { CQ("%l"), 2+1, EXP(CQ("11")) },
1618 { CQ("%m"), 2+1, EXP(CQ("07")) },
1619 { CQ("%M"), 2+1, EXP(CQ("01")) },
1620 { CQ("%n"), 1+1, EXP(CQ("\n")) },
1621 { CQ("%p"), 2+1, EXP(CQ("PM")) },
1622 { CQ("%q"), 1+1, EXP(CQ("3")) },
1623 { CQ("%r"), 11+1, EXP(CQ("11:01:13 PM")) },
1624 { CQ("%R"), 5+1, EXP(CQ("23:01")) },
1625 { CQ("%s"), 2+1, EXP(CQ("1215054073")) },
1626 { CQ("%S"), 2+1, EXP(CQ("13")) },
1627 { CQ("%t"), 1+1, EXP(CQ("\t")) },
1628 { CQ("%T"), 8+1, EXP(CQ("23:01:13")) },
1629 { CQ("%u"), 1+1, EXP(CQ("3")) },
1630 { CQ("%U"), 2+1, EXP(CQ("26")) },
1631 { CQ("%V"), 2+1, EXP(CQ("27")) },
1632 { CQ("%v"), 11+1, EXP(CQ(" 2-Jul-2008")) },
1633 { CQ("%w"), 1+1, EXP(CQ("3")) },
1634 { CQ("%W"), 2+1, EXP(CQ("26")) },
1635 { CQ("%x"), 8+1, EXP(CQ("07/02/08")) },
1636 { CQ("%X"), 8+1, EXP(CQ("23:01:13")) },
1637 { CQ("%y"), 2+1, EXP(CQ("08")) },
1638 { CQ("%Y"), 4+1, EXP(CQ("2008")) },
1639 { CQ("%z"), 5+1, EXP(CQ("-0400")) },
1640 { CQ("%Z"), 3+1, EXP(CQ("EDT")) },
1641 { CQ("%%"), 1+1, EXP(CQ("%")) },
1642 #undef EXP
1643 #define VEC(s) s, sizeof(s)/sizeof(CHAR), sizeof(s)/sizeof(CHAR)-1, s
1644 #define EXP(s) sizeof(s)/sizeof(CHAR), sizeof(s)/sizeof(CHAR)-1, s
1645 { VEC(CQ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")) },
1646 { CQ("0123456789%%%h:`~"), EXP(CQ("0123456789%Jul:`~")) },
1647 { CQ("%R%h:`~ %x %w"), EXP(CQ("23:01Jul:`~ 07/02/08 3")) },
1648 #undef VEC
1649 #undef EXP
1650 };
1651
1652 #if YEAR_BASE == 1900 /* ( */
1653 /* Checks for very large years. YEAR_BASE value relied upon so that the
1654 * answer strings can be predetermined.
1655 * Years more than 4 digits are not mentioned in the standard for %C, so the
1656 * test for those cases are based on the design intent (which is to print the
1657 * whole number, being the century). */
1658 const struct tm tmyr0 = {
1659 /* Wed Jul 2 23:01:13 EDT [HUGE#] */
1660 .tm_sec = 13,
1661 .tm_min = 1,
1662 .tm_hour = 23,
1663 .tm_mday = 2,
1664 .tm_mon = 6,
1665 .tm_year = INT_MAX - YEAR_BASE/2,
1666 .tm_wday = 3,
1667 .tm_yday = 183,
1668 .tm_isdst = 1
1669 };
1670 #if INT_MAX == 32767
1671 # define YEAR CQ("33717") /* INT_MAX + YEAR_BASE/2 */
1672 # define CENT CQ("337")
1673 # define Year CQ("17")
1674 # elif INT_MAX == 2147483647
1675 # define YEAR CQ("2147484597")
1676 # define CENT CQ("21474845")
1677 # define Year CQ("97")
1678 # elif INT_MAX == 9223372036854775807
1679 # define YEAR CQ("9223372036854776757")
1680 # define CENT CQ("92233720368547777")
1681 # define Year CQ("57")
1682 # else
1683 # error "Unrecognized INT_MAX value: enhance me to recognize what you have"
1684 #endif
1685 const struct test Vecyr0[] = {
1686 /* Testing fields one at a time, expecting to pass, using a larger
1687 * allowed length than what is needed. */
1688 /* Using tmyr0 for time: */
1689 #define EXP(s) sizeof(s)/sizeof(CHAR)-1, s
1690 { CQ("%C"), OUTSIZE, EXP(CENT) },
1691 { CQ("%c"), OUTSIZE, EXP(CQ("Wed Jul 2 23:01:13 ")YEAR) },
1692 { CQ("%D"), OUTSIZE, EXP(CQ("07/02/")Year) },
1693 { CQ("%F"), OUTSIZE, EXP(YEAR CQ("-07-02")) },
1694 { CQ("%v"), OUTSIZE, EXP(CQ(" 2-Jul-")YEAR) },
1695 { CQ("%x"), OUTSIZE, EXP(CQ("07/02/")Year) },
1696 { CQ("%y"), OUTSIZE, EXP(Year) },
1697 { CQ("%Y"), OUTSIZE, EXP(YEAR) },
1698 #undef EXP
1699 };
1700 #undef YEAR
1701 #undef CENT
1702 #undef Year
1703 /* Checks for very large negative years. YEAR_BASE value relied upon so that
1704 * the answer strings can be predetermined. */
1705 const struct tm tmyr1 = {
1706 /* Wed Jul 2 23:01:13 EDT [HUGE#] */
1707 .tm_sec = 13,
1708 .tm_min = 1,
1709 .tm_hour = 23,
1710 .tm_mday = 2,
1711 .tm_mon = 6,
1712 .tm_year = INT_MIN,
1713 .tm_wday = 3,
1714 .tm_yday = 183,
1715 .tm_isdst = 1
1716 };
1717 #if INT_MAX == 32767
1718 # define YEAR CQ("-30868") /* INT_MIN + YEAR_BASE */
1719 # define CENT CQ("-308")
1720 # define Year CQ("68")
1721 # elif INT_MAX == 2147483647
1722 # define YEAR CQ("-2147481748")
1723 # define CENT CQ("-21474817")
1724 # define Year CQ("48")
1725 # elif INT_MAX == 9223372036854775807
1726 # define YEAR CQ("-9223372036854773908")
1727 # define CENT CQ("-92233720368547739")
1728 # define Year CQ("08")
1729 # else
1730 # error "Unrecognized INT_MAX value: enhance me to recognize what you have"
1731 #endif
1732 const struct test Vecyr1[] = {
1733 /* Testing fields one at a time, expecting to pass, using a larger
1734 * allowed length than what is needed. */
1735 /* Using tmyr1 for time: */
1736 #define EXP(s) sizeof(s)/sizeof(CHAR)-1, s
1737 { CQ("%C"), OUTSIZE, EXP(CENT) },
1738 { CQ("%c"), OUTSIZE, EXP(CQ("Wed Jul 2 23:01:13 ")YEAR) },
1739 { CQ("%D"), OUTSIZE, EXP(CQ("07/02/")Year) },
1740 { CQ("%F"), OUTSIZE, EXP(YEAR CQ("-07-02")) },
1741 { CQ("%v"), OUTSIZE, EXP(CQ(" 2-Jul-")YEAR) },
1742 { CQ("%x"), OUTSIZE, EXP(CQ("07/02/")Year) },
1743 { CQ("%y"), OUTSIZE, EXP(Year) },
1744 { CQ("%Y"), OUTSIZE, EXP(YEAR) },
1745 #undef EXP
1746 };
1747 #undef YEAR
1748 #undef CENT
1749 #undef Year
1750 #endif /* YEAR_BASE ) */
1751
1752 /* Checks for years just over zero (also test for s=60).
1753 * Years less than 4 digits are not mentioned for %Y in the standard, so the
1754 * test for that case is based on the design intent. */
1755 const struct tm tmyrzp = {
1756 /* Wed Jul 2 23:01:60 EDT 0007 */
1757 .tm_sec = 60,
1758 .tm_min = 1,
1759 .tm_hour = 23,
1760 .tm_mday = 2,
1761 .tm_mon = 6,
1762 .tm_year = 7-YEAR_BASE,
1763 .tm_wday = 3,
1764 .tm_yday = 183,
1765 .tm_isdst = 1
1766 };
1767 #define YEAR CQ("0007") /* Design intent: %Y=%C%y */
1768 #define CENT CQ("00")
1769 #define Year CQ("07")
1770 const struct test Vecyrzp[] = {
1771 /* Testing fields one at a time, expecting to pass, using a larger
1772 * allowed length than what is needed. */
1773 /* Using tmyrzp for time: */
1774 #define EXP(s) sizeof(s)/sizeof(CHAR)-1, s
1775 { CQ("%C"), OUTSIZE, EXP(CENT) },
1776 { CQ("%c"), OUTSIZE, EXP(CQ("Wed Jul 2 23:01:60 ")YEAR) },
1777 { CQ("%D"), OUTSIZE, EXP(CQ("07/02/")Year) },
1778 { CQ("%F"), OUTSIZE, EXP(YEAR CQ("-07-02")) },
1779 { CQ("%v"), OUTSIZE, EXP(CQ(" 2-Jul-")YEAR) },
1780 { CQ("%x"), OUTSIZE, EXP(CQ("07/02/")Year) },
1781 { CQ("%y"), OUTSIZE, EXP(Year) },
1782 { CQ("%Y"), OUTSIZE, EXP(YEAR) },
1783 #undef EXP
1784 };
1785 #undef YEAR
1786 #undef CENT
1787 #undef Year
1788 /* Checks for years just under zero.
1789 * Negative years are not handled by the standard, so the vectors here are
1790 * verifying the chosen implemtation. */
1791 const struct tm tmyrzn = {
1792 /* Wed Jul 2 23:01:00 EDT -004 */
1793 .tm_sec = 00,
1794 .tm_min = 1,
1795 .tm_hour = 23,
1796 .tm_mday = 2,
1797 .tm_mon = 6,
1798 .tm_year = -4-YEAR_BASE,
1799 .tm_wday = 3,
1800 .tm_yday = 183,
1801 .tm_isdst = 1
1802 };
1803 #define YEAR CQ("-004")
1804 #define CENT CQ("-0")
1805 #define Year CQ("04")
1806 const struct test Vecyrzn[] = {
1807 /* Testing fields one at a time, expecting to pass, using a larger
1808 * allowed length than what is needed. */
1809 /* Using tmyrzn for time: */
1810 #define EXP(s) sizeof(s)/sizeof(CHAR)-1, s
1811 { CQ("%C"), OUTSIZE, EXP(CENT) },
1812 { CQ("%c"), OUTSIZE, EXP(CQ("Wed Jul 2 23:01:00 ")YEAR) },
1813 { CQ("%D"), OUTSIZE, EXP(CQ("07/02/")Year) },
1814 { CQ("%F"), OUTSIZE, EXP(YEAR CQ("-07-02")) },
1815 { CQ("%v"), OUTSIZE, EXP(CQ(" 2-Jul-")YEAR) },
1816 { CQ("%x"), OUTSIZE, EXP(CQ("07/02/")Year) },
1817 { CQ("%y"), OUTSIZE, EXP(Year) },
1818 { CQ("%Y"), OUTSIZE, EXP(YEAR) },
1819 #undef EXP
1820 };
1821 #undef YEAR
1822 #undef CENT
1823 #undef Year
1824
1825 const struct list ListYr[] = {
1826 { &tmyrzp, Vecyrzp, sizeof(Vecyrzp)/sizeof(Vecyrzp[0]) },
1827 { &tmyrzn, Vecyrzn, sizeof(Vecyrzn)/sizeof(Vecyrzn[0]) },
1828 #if YEAR_BASE == 1900
1829 { &tmyr0, Vecyr0, sizeof(Vecyr0)/sizeof(Vecyr0[0]) },
1830 { &tmyr1, Vecyr1, sizeof(Vecyr1)/sizeof(Vecyr1[0]) },
1831 #endif
1832 };
1833
1834
1835 /* List of tests to be run */
1836 const struct list List[] = {
1837 { &tm0, Vec0, sizeof(Vec0)/sizeof(Vec0[0]) },
1838 { &tm1, Vec1, sizeof(Vec1)/sizeof(Vec1[0]) },
1839 };
1840
1841 int
main(void)1842 main(void)
1843 {
1844 int i, l, errr=0, erro=0, tot=0;
1845 const char *cp;
1846 CHAR out[OUTSIZE];
1847 size_t ret;
1848
1849 /* Set timezone so that %z and %Z tests come out right */
1850 cp = TZ;
1851 if((i=putenv(cp))) {
1852 printf( "putenv(%s) FAILED, ret %d\n", cp, i);
1853 return(-1);
1854 }
1855 if(strcmp(getenv("TZ"),strchr(TZ,'=')+1)) {
1856 printf( "TZ not set properly in environment\n");
1857 return(-2);
1858 }
1859 tzset();
1860
1861 #if defined(VERBOSE)
1862 printf("_timezone=%d, _daylight=%d, _tzname[0]=%s, _tzname[1]=%s\n", _timezone, _daylight, _tzname[0], _tzname[1]);
1863 {
1864 long offset;
1865 __tzinfo_type *tz = __gettzinfo ();
1866 /* The sign of this is exactly opposite the envvar TZ. We
1867 could directly use the global _timezone for tm_isdst==0,
1868 but have to use __tzrule for daylight savings. */
1869 printf("tz->__tzrule[0].offset=%d, tz->__tzrule[1].offset=%d\n", tz->__tzrule[0].offset, tz->__tzrule[1].offset);
1870 }
1871 #endif
1872
1873 /* Run all of the exact-length tests as-given--results should match */
1874 for(l=0; l<sizeof(List)/sizeof(List[0]); l++) {
1875 const struct list *test = &List[l];
1876 for(i=0; i<test->cnt; i++) {
1877 tot++; /* Keep track of number of tests */
1878 ret = strftime(out, test->vec[i].max, test->vec[i].fmt, test->tms);
1879 if(ret != test->vec[i].ret) {
1880 errr++;
1881 fprintf(stderr,
1882 "ERROR: return %d != %d expected for List[%d].vec[%d]\n",
1883 ret, test->vec[i].ret, l, i);
1884 }
1885 if(t_strncmp(out, test->vec[i].out, test->vec[i].max-1)) {
1886 erro++;
1887 fprintf(stderr,
1888 "ERROR: \"%"SFLG"s\" != \"%"SFLG"s\" expected for List[%d].vec[%d]\n",
1889 out, test->vec[i].out, l, i);
1890 }
1891 }
1892 }
1893
1894 /* Run all of the exact-length tests with the length made too short--expect to
1895 * fail. */
1896 for(l=0; l<sizeof(List)/sizeof(List[0]); l++) {
1897 const struct list *test = &List[l];
1898 for(i=0; i<test->cnt; i++) {
1899 tot++; /* Keep track of number of tests */
1900 ret = strftime(out, test->vec[i].max-1, test->vec[i].fmt, test->tms);
1901 if(ret != 0) {
1902 errr++;
1903 fprintf(stderr,
1904 "ERROR: return %d != %d expected for List[%d].vec[%d]\n",
1905 ret, 0, l, i);
1906 }
1907 /* Almost every conversion puts out as many characters as possible, so
1908 * go ahead and test the output even though have failed. (The test
1909 * times chosen happen to not hit any of the cases that fail this, so it
1910 * works.) */
1911 if(t_strncmp(out, test->vec[i].out, test->vec[i].max-1-1)) {
1912 erro++;
1913 fprintf(stderr,
1914 "ERROR: \"%"SFLG"s\" != \"%"SFLG"s\" expected for List[%d].vec[%d]\n",
1915 out, test->vec[i].out, l, i);
1916 }
1917 }
1918 }
1919
1920 /* Run all of the special year test cases */
1921 for(l=0; l<sizeof(ListYr)/sizeof(ListYr[0]); l++) {
1922 const struct list *test = &ListYr[l];
1923 for(i=0; i<test->cnt; i++) {
1924 tot++; /* Keep track of number of tests */
1925 ret = strftime(out, test->vec[i].max, test->vec[i].fmt, test->tms);
1926 if(ret != test->vec[i].ret) {
1927 errr++;
1928 fprintf(stderr,
1929 "ERROR: return %d != %d expected for ListYr[%d].vec[%d]\n",
1930 ret, test->vec[i].ret, l, i);
1931 }
1932 if(t_strncmp(out, test->vec[i].out, test->vec[i].max-1)) {
1933 erro++;
1934 fprintf(stderr,
1935 "ERROR: \"%"SFLG"s\" != \"%"SFLG"s\" expected for ListYr[%d].vec[%d]\n",
1936 out, test->vec[i].out, l, i);
1937 }
1938 }
1939 }
1940
1941 #define STRIZE(f) #f
1942 #define NAME(f) STRIZE(f)
1943 printf(NAME(strftime) "() test ");
1944 if(errr || erro) printf("FAILED %d/%d of", errr, erro);
1945 else printf("passed");
1946 printf(" %d test cases.\n", tot);
1947
1948 return(errr || erro);
1949 }
1950 #endif /* defined(_REGRESSION_TEST) ] */
1951