Subversion Repositories Kolibri OS

Rev

Rev 4921 | Details | Compare with Previous | Last modification | View Log | RSS feed

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