Home | History | Annotate | Line # | Download | only in time
strftime.c revision 1.39
      1  1.39  christos /*	$NetBSD: strftime.c,v 1.39 2017/03/11 18:23:14 christos Exp $	*/
      2  1.36  christos 
      3  1.39  christos /* Convert a broken-down timestamp to a string.  */
      4  1.36  christos 
      5  1.36  christos /* Copyright 1989 The Regents of the University of California.
      6  1.36  christos    All rights reserved.
      7  1.36  christos 
      8  1.36  christos    Redistribution and use in source and binary forms, with or without
      9  1.36  christos    modification, are permitted provided that the following conditions
     10  1.36  christos    are met:
     11  1.36  christos    1. Redistributions of source code must retain the above copyright
     12  1.36  christos       notice, this list of conditions and the following disclaimer.
     13  1.36  christos    2. Redistributions in binary form must reproduce the above copyright
     14  1.36  christos       notice, this list of conditions and the following disclaimer in the
     15  1.36  christos       documentation and/or other materials provided with the distribution.
     16  1.36  christos    3. Neither the name of the University nor the names of its contributors
     17  1.36  christos       may be used to endorse or promote products derived from this software
     18  1.36  christos       without specific prior written permission.
     19  1.36  christos 
     20  1.36  christos    THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND
     21  1.36  christos    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     22  1.36  christos    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     23  1.36  christos    ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     24  1.36  christos    FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     25  1.36  christos    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     26  1.36  christos    OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     27  1.36  christos    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     28  1.36  christos    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     29  1.36  christos    OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     30  1.36  christos    SUCH DAMAGE.  */
     31   1.1       mrg 
     32   1.3  christos #include <sys/cdefs.h>
     33   1.1       mrg #if defined(LIBC_SCCS) && !defined(lint)
     34   1.3  christos #if 0
     35  1.13    kleink static char	elsieid[] = "@(#)strftime.c	7.64";
     36  1.20   mlelstv static char	elsieid[] = "@(#)strftime.c	8.3";
     37   1.3  christos #else
     38  1.39  christos __RCSID("$NetBSD: strftime.c,v 1.39 2017/03/11 18:23:14 christos Exp $");
     39   1.3  christos #endif
     40   1.1       mrg #endif /* LIBC_SCCS and not lint */
     41   1.1       mrg 
     42   1.4       jtc #include "namespace.h"
     43  1.12    kleink 
     44  1.25     joerg #include <stddef.h>
     45  1.30  christos #include <assert.h>
     46  1.25     joerg #include <locale.h>
     47  1.25     joerg #include "setlocale_local.h"
     48  1.25     joerg 
     49  1.12    kleink /*
     50  1.36  christos ** Based on the UCB version with the copyright notice appearing above.
     51  1.24  christos **
     52  1.12    kleink ** This is ANSIish only when "multibyte character == plain character".
     53  1.12    kleink */
     54  1.12    kleink 
     55  1.11      taca #include "private.h"
     56  1.12    kleink 
     57  1.16    kleink /*
     58  1.16    kleink ** We don't use these extensions in strftime operation even when
     59  1.16    kleink ** supported by the local tzcode configuration.  A strictly
     60  1.16    kleink ** conforming C application may leave them in undefined state.
     61  1.16    kleink */
     62  1.16    kleink 
     63  1.15    kleink #ifdef _LIBC
     64  1.15    kleink #undef TM_ZONE
     65  1.16    kleink #undef TM_GMTOFF
     66  1.15    kleink #endif
     67  1.15    kleink 
     68  1.39  christos #include <fcntl.h>
     69  1.39  christos #include <locale.h>
     70  1.12    kleink 
     71  1.21  christos #ifdef __weak_alias
     72  1.25     joerg __weak_alias(strftime_l, _strftime_l)
     73  1.25     joerg __weak_alias(strftime_lz, _strftime_lz)
     74  1.21  christos __weak_alias(strftime_z, _strftime_z)
     75  1.21  christos #endif
     76  1.21  christos 
     77  1.12    kleink #include "sys/localedef.h"
     78  1.25     joerg #define _TIME_LOCALE(loc) \
     79  1.25     joerg     ((_TimeLocale *)((loc)->part_impl[(size_t)LC_TIME]))
     80  1.20   mlelstv #define c_fmt   d_t_fmt
     81  1.12    kleink 
     82  1.20   mlelstv static char *	_add(const char *, char *, const char *);
     83  1.20   mlelstv static char *	_conv(int, const char *, char *, const char *);
     84  1.21  christos static char *	_fmt(const timezone_t, const char *, const struct tm *, char *,
     85  1.25     joerg 			const char *, int *, locale_t);
     86  1.33  christos static char *	_yconv(int, int, bool, bool, char *, const char *);
     87  1.12    kleink 
     88  1.36  christos #if !HAVE_POSIX_DECLS
     89  1.20   mlelstv extern char *	tzname[];
     90  1.36  christos #endif
     91  1.12    kleink 
     92  1.12    kleink #ifndef YEAR_2000_NAME
     93  1.12    kleink #define YEAR_2000_NAME	"CHECK_STRFTIME_FORMATS_FOR_TWO_DIGIT_YEARS"
     94  1.12    kleink #endif /* !defined YEAR_2000_NAME */
     95  1.12    kleink 
     96  1.12    kleink #define IN_NONE	0
     97  1.12    kleink #define IN_SOME	1
     98  1.12    kleink #define IN_THIS	2
     99  1.12    kleink #define IN_ALL	3
    100   1.1       mrg 
    101   1.1       mrg size_t
    102  1.25     joerg strftime_z(const timezone_t sp, char * __restrict s, size_t maxsize,
    103  1.25     joerg     const char * __restrict format, const struct tm * __restrict t)
    104  1.25     joerg {
    105  1.26     joerg 	return strftime_lz(sp, s, maxsize, format, t, _current_locale());
    106  1.25     joerg }
    107  1.25     joerg 
    108  1.33  christos #if HAVE_STRFTIME_L
    109  1.33  christos size_t
    110  1.33  christos strftime_l(char *s, size_t maxsize, char const *format, struct tm const *t,
    111  1.33  christos 	   locale_t locale)
    112  1.33  christos {
    113  1.33  christos   /* Just call strftime, as only the C locale is supported.  */
    114  1.33  christos   return strftime(s, maxsize, format, t);
    115  1.33  christos }
    116  1.33  christos #endif
    117  1.33  christos 
    118  1.25     joerg size_t
    119  1.25     joerg strftime_lz(const timezone_t sp, char *const s, const size_t maxsize,
    120  1.25     joerg     const char *const format, const struct tm *const t, locale_t loc)
    121   1.1       mrg {
    122  1.12    kleink 	char *	p;
    123  1.12    kleink 	int	warn;
    124   1.5    kleink 
    125  1.12    kleink 	warn = IN_NONE;
    126  1.37  christos 	p = _fmt(sp, format, t, s, s + maxsize, &warn, loc);
    127  1.12    kleink #ifndef NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU
    128  1.12    kleink 	if (warn != IN_NONE && getenv(YEAR_2000_NAME) != NULL) {
    129  1.12    kleink 		(void) fprintf(stderr, "\n");
    130  1.37  christos 		(void) fprintf(stderr, "strftime format \"%s\" ", format);
    131  1.12    kleink 		(void) fprintf(stderr, "yields only two digits of years in ");
    132  1.12    kleink 		if (warn == IN_SOME)
    133  1.12    kleink 			(void) fprintf(stderr, "some locales");
    134  1.12    kleink 		else if (warn == IN_THIS)
    135  1.12    kleink 			(void) fprintf(stderr, "the current locale");
    136  1.12    kleink 		else	(void) fprintf(stderr, "all locales");
    137  1.12    kleink 		(void) fprintf(stderr, "\n");
    138  1.12    kleink 	}
    139  1.12    kleink #endif /* !defined NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU */
    140  1.12    kleink 	if (p == s + maxsize)
    141  1.12    kleink 		return 0;
    142  1.12    kleink 	*p = '\0';
    143  1.12    kleink 	return p - s;
    144   1.1       mrg }
    145   1.1       mrg 
    146  1.12    kleink static char *
    147  1.34  christos _fmt(const timezone_t sp, const char *format, const struct tm *t, char *pt,
    148  1.34  christos      const char *ptlim, int *warnp, locale_t loc)
    149   1.1       mrg {
    150  1.12    kleink 	for ( ; *format; ++format) {
    151   1.1       mrg 		if (*format == '%') {
    152  1.12    kleink label:
    153  1.12    kleink 			switch (*++format) {
    154   1.1       mrg 			case '\0':
    155   1.1       mrg 				--format;
    156   1.1       mrg 				break;
    157   1.1       mrg 			case 'A':
    158  1.12    kleink 				pt = _add((t->tm_wday < 0 ||
    159  1.12    kleink 					t->tm_wday >= DAYSPERWEEK) ?
    160  1.25     joerg 					"?" : _TIME_LOCALE(loc)->day[t->tm_wday],
    161  1.12    kleink 					pt, ptlim);
    162   1.1       mrg 				continue;
    163   1.1       mrg 			case 'a':
    164  1.12    kleink 				pt = _add((t->tm_wday < 0 ||
    165  1.12    kleink 					t->tm_wday >= DAYSPERWEEK) ?
    166  1.25     joerg 					"?" : _TIME_LOCALE(loc)->abday[t->tm_wday],
    167  1.12    kleink 					pt, ptlim);
    168   1.1       mrg 				continue;
    169   1.1       mrg 			case 'B':
    170  1.12    kleink 				pt = _add((t->tm_mon < 0 ||
    171  1.12    kleink 					t->tm_mon >= MONSPERYEAR) ?
    172  1.25     joerg 					"?" : _TIME_LOCALE(loc)->mon[t->tm_mon],
    173  1.12    kleink 					pt, ptlim);
    174   1.1       mrg 				continue;
    175   1.1       mrg 			case 'b':
    176   1.1       mrg 			case 'h':
    177  1.12    kleink 				pt = _add((t->tm_mon < 0 ||
    178  1.12    kleink 					t->tm_mon >= MONSPERYEAR) ?
    179  1.25     joerg 					"?" : _TIME_LOCALE(loc)->abmon[t->tm_mon],
    180  1.12    kleink 					pt, ptlim);
    181   1.1       mrg 				continue;
    182   1.1       mrg 			case 'C':
    183  1.12    kleink 				/*
    184  1.12    kleink 				** %C used to do a...
    185  1.12    kleink 				**	_fmt("%a %b %e %X %Y", t);
    186  1.12    kleink 				** ...whereas now POSIX 1003.2 calls for
    187  1.12    kleink 				** something completely different.
    188  1.12    kleink 				** (ado, 1993-05-24)
    189  1.12    kleink 				*/
    190  1.33  christos 				pt = _yconv(t->tm_year, TM_YEAR_BASE,
    191  1.33  christos 					    true, false, pt, ptlim);
    192   1.1       mrg 				continue;
    193   1.1       mrg 			case 'c':
    194  1.12    kleink 				{
    195  1.12    kleink 				int warn2 = IN_SOME;
    196  1.12    kleink 
    197  1.25     joerg 				pt = _fmt(sp, _TIME_LOCALE(loc)->c_fmt, t, pt,
    198  1.25     joerg 				    ptlim, &warn2, loc);
    199  1.12    kleink 				if (warn2 == IN_ALL)
    200  1.12    kleink 					warn2 = IN_THIS;
    201  1.12    kleink 				if (warn2 > *warnp)
    202  1.12    kleink 					*warnp = warn2;
    203  1.12    kleink 				}
    204   1.1       mrg 				continue;
    205   1.1       mrg 			case 'D':
    206  1.25     joerg 				pt = _fmt(sp, "%m/%d/%y", t, pt, ptlim, warnp,
    207  1.25     joerg 				    loc);
    208   1.1       mrg 				continue;
    209   1.1       mrg 			case 'd':
    210  1.12    kleink 				pt = _conv(t->tm_mday, "%02d", pt, ptlim);
    211   1.1       mrg 				continue;
    212  1.12    kleink 			case 'E':
    213  1.12    kleink 			case 'O':
    214  1.12    kleink 				/*
    215  1.12    kleink 				** C99 locale modifiers.
    216  1.12    kleink 				** The sequences
    217  1.12    kleink 				**	%Ec %EC %Ex %EX %Ey %EY
    218  1.12    kleink 				**	%Od %oe %OH %OI %Om %OM
    219  1.12    kleink 				**	%OS %Ou %OU %OV %Ow %OW %Oy
    220  1.12    kleink 				** are supposed to provide alternate
    221  1.12    kleink 				** representations.
    222  1.12    kleink 				*/
    223  1.12    kleink 				goto label;
    224   1.1       mrg 			case 'e':
    225  1.12    kleink 				pt = _conv(t->tm_mday, "%2d", pt, ptlim);
    226  1.10    kleink 				continue;
    227  1.10    kleink 			case 'F':
    228  1.25     joerg 				pt = _fmt(sp, "%Y-%m-%d", t, pt, ptlim, warnp,
    229  1.25     joerg 				    loc);
    230   1.1       mrg 				continue;
    231   1.1       mrg 			case 'H':
    232  1.12    kleink 				pt = _conv(t->tm_hour, "%02d", pt, ptlim);
    233   1.1       mrg 				continue;
    234   1.1       mrg 			case 'I':
    235  1.12    kleink 				pt = _conv((t->tm_hour % 12) ?
    236  1.12    kleink 					(t->tm_hour % 12) : 12,
    237  1.12    kleink 					"%02d", pt, ptlim);
    238   1.1       mrg 				continue;
    239   1.1       mrg 			case 'j':
    240  1.12    kleink 				pt = _conv(t->tm_yday + 1, "%03d", pt, ptlim);
    241   1.1       mrg 				continue;
    242   1.1       mrg 			case 'k':
    243  1.12    kleink 				/*
    244  1.12    kleink 				** This used to be...
    245  1.12    kleink 				**	_conv(t->tm_hour % 12 ?
    246  1.12    kleink 				**		t->tm_hour % 12 : 12, 2, ' ');
    247  1.12    kleink 				** ...and has been changed to the below to
    248  1.12    kleink 				** match SunOS 4.1.1 and Arnold Robbins'
    249  1.20   mlelstv 				** strftime version 3.0. That is, "%k" and
    250  1.12    kleink 				** "%l" have been swapped.
    251  1.12    kleink 				** (ado, 1993-05-24)
    252  1.12    kleink 				*/
    253  1.12    kleink 				pt = _conv(t->tm_hour, "%2d", pt, ptlim);
    254  1.12    kleink 				continue;
    255  1.12    kleink #ifdef KITCHEN_SINK
    256  1.12    kleink 			case 'K':
    257  1.12    kleink 				/*
    258  1.12    kleink 				** After all this time, still unclaimed!
    259  1.12    kleink 				*/
    260  1.12    kleink 				pt = _add("kitchen sink", pt, ptlim);
    261   1.1       mrg 				continue;
    262  1.12    kleink #endif /* defined KITCHEN_SINK */
    263   1.1       mrg 			case 'l':
    264  1.12    kleink 				/*
    265  1.12    kleink 				** This used to be...
    266  1.12    kleink 				**	_conv(t->tm_hour, 2, ' ');
    267  1.12    kleink 				** ...and has been changed to the below to
    268  1.12    kleink 				** match SunOS 4.1.1 and Arnold Robbin's
    269  1.20   mlelstv 				** strftime version 3.0. That is, "%k" and
    270  1.12    kleink 				** "%l" have been swapped.
    271  1.12    kleink 				** (ado, 1993-05-24)
    272  1.12    kleink 				*/
    273  1.12    kleink 				pt = _conv((t->tm_hour % 12) ?
    274  1.12    kleink 					(t->tm_hour % 12) : 12,
    275  1.12    kleink 					"%2d", pt, ptlim);
    276   1.1       mrg 				continue;
    277   1.1       mrg 			case 'M':
    278  1.12    kleink 				pt = _conv(t->tm_min, "%02d", pt, ptlim);
    279   1.1       mrg 				continue;
    280   1.1       mrg 			case 'm':
    281  1.12    kleink 				pt = _conv(t->tm_mon + 1, "%02d", pt, ptlim);
    282   1.1       mrg 				continue;
    283   1.1       mrg 			case 'n':
    284  1.12    kleink 				pt = _add("\n", pt, ptlim);
    285   1.1       mrg 				continue;
    286   1.1       mrg 			case 'p':
    287  1.12    kleink 				pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ?
    288  1.25     joerg 					_TIME_LOCALE(loc)->am_pm[1] :
    289  1.25     joerg 					_TIME_LOCALE(loc)->am_pm[0],
    290  1.12    kleink 					pt, ptlim);
    291   1.1       mrg 				continue;
    292   1.1       mrg 			case 'R':
    293  1.25     joerg 				pt = _fmt(sp, "%H:%M", t, pt, ptlim, warnp,
    294  1.25     joerg 				    loc);
    295   1.1       mrg 				continue;
    296   1.1       mrg 			case 'r':
    297  1.25     joerg 				pt = _fmt(sp, _TIME_LOCALE(loc)->t_fmt_ampm, t,
    298  1.25     joerg 				    pt, ptlim, warnp, loc);
    299   1.1       mrg 				continue;
    300   1.1       mrg 			case 'S':
    301  1.12    kleink 				pt = _conv(t->tm_sec, "%02d", pt, ptlim);
    302   1.1       mrg 				continue;
    303   1.1       mrg 			case 's':
    304  1.12    kleink 				{
    305  1.12    kleink 					struct tm	tm;
    306  1.12    kleink 					char		buf[INT_STRLEN_MAXIMUM(
    307  1.12    kleink 								time_t) + 1];
    308  1.12    kleink 					time_t		mkt;
    309  1.12    kleink 
    310  1.12    kleink 					tm = *t;
    311  1.12    kleink 					mkt = mktime(&tm);
    312  1.12    kleink 					/* CONSTCOND */
    313  1.12    kleink 					if (TYPE_SIGNED(time_t))
    314  1.27  christos 						(void)snprintf(buf, sizeof(buf),
    315  1.27  christos 						    "%jd", (intmax_t) mkt);
    316  1.27  christos 					else	(void)snprintf(buf, sizeof(buf),
    317  1.27  christos 						    "%ju", (uintmax_t) mkt);
    318  1.12    kleink 					pt = _add(buf, pt, ptlim);
    319  1.12    kleink 				}
    320   1.1       mrg 				continue;
    321   1.1       mrg 			case 'T':
    322  1.25     joerg 				pt = _fmt(sp, "%H:%M:%S", t, pt, ptlim, warnp,
    323  1.25     joerg 				    loc);
    324   1.1       mrg 				continue;
    325   1.1       mrg 			case 't':
    326  1.12    kleink 				pt = _add("\t", pt, ptlim);
    327   1.1       mrg 				continue;
    328   1.1       mrg 			case 'U':
    329  1.12    kleink 				pt = _conv((t->tm_yday + DAYSPERWEEK -
    330  1.12    kleink 					t->tm_wday) / DAYSPERWEEK,
    331  1.12    kleink 					"%02d", pt, ptlim);
    332   1.1       mrg 				continue;
    333   1.1       mrg 			case 'u':
    334  1.12    kleink 				/*
    335  1.12    kleink 				** From Arnold Robbins' strftime version 3.0:
    336  1.12    kleink 				** "ISO 8601: Weekday as a decimal number
    337  1.12    kleink 				** [1 (Monday) - 7]"
    338  1.12    kleink 				** (ado, 1993-05-24)
    339  1.12    kleink 				*/
    340  1.12    kleink 				pt = _conv((t->tm_wday == 0) ?
    341  1.12    kleink 					DAYSPERWEEK : t->tm_wday,
    342  1.12    kleink 					"%d", pt, ptlim);
    343   1.1       mrg 				continue;
    344   1.8  augustss 			case 'V':	/* ISO 8601 week number */
    345   1.8  augustss 			case 'G':	/* ISO 8601 year (four digits) */
    346   1.8  augustss 			case 'g':	/* ISO 8601 year (two digits) */
    347   1.8  augustss /*
    348  1.20   mlelstv ** From Arnold Robbins' strftime version 3.0: "the week number of the
    349   1.8  augustss ** year (the first Monday as the first day of week 1) as a decimal number
    350   1.8  augustss ** (01-53)."
    351   1.8  augustss ** (ado, 1993-05-24)
    352   1.8  augustss **
    353  1.31  christos ** From <http://www.ft.uni-erlangen.de/~mskuhn/iso-time.html> by Markus Kuhn:
    354   1.8  augustss ** "Week 01 of a year is per definition the first week which has the
    355   1.8  augustss ** Thursday in this year, which is equivalent to the week which contains
    356   1.8  augustss ** the fourth day of January. In other words, the first week of a new year
    357   1.8  augustss ** is the week which has the majority of its days in the new year. Week 01
    358   1.8  augustss ** might also contain days from the previous year and the week before week
    359   1.8  augustss ** 01 of a year is the last week (52 or 53) of the previous year even if
    360   1.8  augustss ** it contains days from the new year. A week starts with Monday (day 1)
    361  1.20   mlelstv ** and ends with Sunday (day 7). For example, the first week of the year
    362   1.8  augustss ** 1997 lasts from 1996-12-30 to 1997-01-05..."
    363   1.8  augustss ** (ado, 1996-01-02)
    364   1.8  augustss */
    365   1.1       mrg 				{
    366   1.8  augustss 					int	year;
    367  1.20   mlelstv 					int	base;
    368   1.8  augustss 					int	yday;
    369   1.8  augustss 					int	wday;
    370   1.8  augustss 					int	w;
    371   1.8  augustss 
    372  1.20   mlelstv 					year = t->tm_year;
    373  1.20   mlelstv 					base = TM_YEAR_BASE;
    374   1.8  augustss 					yday = t->tm_yday;
    375   1.8  augustss 					wday = t->tm_wday;
    376   1.8  augustss 					for ( ; ; ) {
    377   1.8  augustss 						int	len;
    378   1.8  augustss 						int	bot;
    379   1.8  augustss 						int	top;
    380   1.8  augustss 
    381  1.20   mlelstv 						len = isleap_sum(year, base) ?
    382   1.8  augustss 							DAYSPERLYEAR :
    383   1.8  augustss 							DAYSPERNYEAR;
    384   1.8  augustss 						/*
    385   1.8  augustss 						** What yday (-3 ... 3) does
    386   1.8  augustss 						** the ISO year begin on?
    387   1.8  augustss 						*/
    388   1.8  augustss 						bot = ((yday + 11 - wday) %
    389   1.8  augustss 							DAYSPERWEEK) - 3;
    390   1.8  augustss 						/*
    391   1.8  augustss 						** What yday does the NEXT
    392   1.8  augustss 						** ISO year begin on?
    393   1.8  augustss 						*/
    394   1.8  augustss 						top = bot -
    395   1.8  augustss 							(len % DAYSPERWEEK);
    396   1.8  augustss 						if (top < -3)
    397   1.8  augustss 							top += DAYSPERWEEK;
    398   1.8  augustss 						top += len;
    399   1.8  augustss 						if (yday >= top) {
    400  1.20   mlelstv 							++base;
    401   1.8  augustss 							w = 1;
    402   1.8  augustss 							break;
    403   1.8  augustss 						}
    404   1.8  augustss 						if (yday >= bot) {
    405   1.8  augustss 							w = 1 + ((yday - bot) /
    406   1.8  augustss 								DAYSPERWEEK);
    407   1.8  augustss 							break;
    408   1.8  augustss 						}
    409  1.20   mlelstv 						--base;
    410  1.20   mlelstv 						yday += isleap_sum(year, base) ?
    411   1.8  augustss 							DAYSPERLYEAR :
    412   1.8  augustss 							DAYSPERNYEAR;
    413   1.8  augustss 					}
    414   1.8  augustss #ifdef XPG4_1994_04_09
    415  1.20   mlelstv 					if ((w == 52 &&
    416  1.20   mlelstv 						t->tm_mon == TM_JANUARY) ||
    417  1.20   mlelstv 						(w == 1 &&
    418  1.20   mlelstv 						t->tm_mon == TM_DECEMBER))
    419  1.20   mlelstv 							w = 53;
    420   1.8  augustss #endif /* defined XPG4_1994_04_09 */
    421  1.12    kleink 					if (*format == 'V')
    422  1.12    kleink 						pt = _conv(w, "%02d",
    423  1.12    kleink 							pt, ptlim);
    424  1.12    kleink 					else if (*format == 'g') {
    425  1.12    kleink 						*warnp = IN_ALL;
    426  1.33  christos 						pt = _yconv(year, base,
    427  1.33  christos 							false, true,
    428  1.12    kleink 							pt, ptlim);
    429  1.33  christos 					} else	pt = _yconv(year, base,
    430  1.33  christos 							true, true,
    431  1.12    kleink 							pt, ptlim);
    432   1.1       mrg 				}
    433   1.1       mrg 				continue;
    434  1.12    kleink 			case 'v':
    435  1.12    kleink 				/*
    436  1.12    kleink 				** From Arnold Robbins' strftime version 3.0:
    437  1.12    kleink 				** "date as dd-bbb-YYYY"
    438  1.12    kleink 				** (ado, 1993-05-24)
    439  1.12    kleink 				*/
    440  1.25     joerg 				pt = _fmt(sp, "%e-%b-%Y", t, pt, ptlim, warnp,
    441  1.25     joerg 				    loc);
    442  1.12    kleink 				continue;
    443   1.1       mrg 			case 'W':
    444  1.12    kleink 				pt = _conv((t->tm_yday + DAYSPERWEEK -
    445  1.12    kleink 					(t->tm_wday ?
    446  1.12    kleink 					(t->tm_wday - 1) :
    447  1.12    kleink 					(DAYSPERWEEK - 1))) / DAYSPERWEEK,
    448  1.12    kleink 					"%02d", pt, ptlim);
    449   1.1       mrg 				continue;
    450   1.1       mrg 			case 'w':
    451  1.12    kleink 				pt = _conv(t->tm_wday, "%d", pt, ptlim);
    452  1.12    kleink 				continue;
    453  1.12    kleink 			case 'X':
    454  1.25     joerg 				pt = _fmt(sp, _TIME_LOCALE(loc)->t_fmt, t, pt,
    455  1.25     joerg 				    ptlim, warnp, loc);
    456   1.1       mrg 				continue;
    457   1.1       mrg 			case 'x':
    458  1.12    kleink 				{
    459  1.12    kleink 				int	warn2 = IN_SOME;
    460  1.12    kleink 
    461  1.25     joerg 				pt = _fmt(sp, _TIME_LOCALE(loc)->d_fmt, t, pt,
    462  1.25     joerg 				    ptlim, &warn2, loc);
    463  1.12    kleink 				if (warn2 == IN_ALL)
    464  1.12    kleink 					warn2 = IN_THIS;
    465  1.12    kleink 				if (warn2 > *warnp)
    466  1.12    kleink 					*warnp = warn2;
    467  1.12    kleink 				}
    468   1.1       mrg 				continue;
    469   1.1       mrg 			case 'y':
    470  1.12    kleink 				*warnp = IN_ALL;
    471  1.33  christos 				pt = _yconv(t->tm_year, TM_YEAR_BASE,
    472  1.33  christos 					false, true,
    473  1.20   mlelstv 					pt, ptlim);
    474   1.1       mrg 				continue;
    475   1.1       mrg 			case 'Y':
    476  1.33  christos 				pt = _yconv(t->tm_year, TM_YEAR_BASE,
    477  1.33  christos 					true, true,
    478  1.12    kleink 					pt, ptlim);
    479   1.1       mrg 				continue;
    480   1.1       mrg 			case 'Z':
    481  1.11      taca #ifdef TM_ZONE
    482  1.35  christos 				pt = _add(t->TM_ZONE, pt, ptlim);
    483  1.12    kleink #endif /* defined TM_ZONE */
    484  1.12    kleink 				if (t->tm_isdst >= 0)
    485  1.21  christos 					pt = _add((sp ?
    486  1.21  christos 					    tzgetname(sp, t->tm_isdst) :
    487  1.21  christos 					    tzname[t->tm_isdst != 0]),
    488  1.21  christos 					    pt, ptlim);
    489  1.12    kleink 				/*
    490  1.12    kleink 				** C99 says that %Z must be replaced by the
    491  1.12    kleink 				** empty string if the time zone is not
    492  1.12    kleink 				** determinable.
    493  1.12    kleink 				*/
    494  1.12    kleink 				continue;
    495  1.12    kleink 			case 'z':
    496  1.38  christos #if defined TM_GMTOFF || defined USG_COMPAT || defined ALTZONE
    497  1.12    kleink 				{
    498  1.30  christos 				long		diff;
    499  1.12    kleink 				char const *	sign;
    500  1.39  christos 				bool negative;
    501  1.12    kleink 
    502  1.12    kleink 				if (t->tm_isdst < 0)
    503  1.12    kleink 					continue;
    504  1.38  christos # ifdef TM_GMTOFF
    505  1.12    kleink 				diff = (int)t->TM_GMTOFF;
    506  1.38  christos # else
    507  1.12    kleink 				/*
    508  1.29  christos 				** C99 says that the UT offset must
    509  1.12    kleink 				** be computed by looking only at
    510  1.20   mlelstv 				** tm_isdst. This requirement is
    511  1.12    kleink 				** incorrect, since it means the code
    512  1.12    kleink 				** must rely on magic (in this case
    513  1.12    kleink 				** altzone and timezone), and the
    514  1.12    kleink 				** magic might not have the correct
    515  1.20   mlelstv 				** offset. Doing things correctly is
    516  1.12    kleink 				** tricky and requires disobeying C99;
    517  1.12    kleink 				** see GNU C strftime for details.
    518  1.12    kleink 				** For now, punt and conform to the
    519  1.12    kleink 				** standard, even though it's incorrect.
    520  1.12    kleink 				**
    521  1.12    kleink 				** C99 says that %z must be replaced by the
    522  1.12    kleink 				** empty string if the time zone is not
    523  1.12    kleink 				** determinable, so output nothing if the
    524  1.12    kleink 				** appropriate variables are not available.
    525  1.12    kleink 				*/
    526  1.38  christos #  ifndef STD_INSPIRED
    527  1.12    kleink 				if (t->tm_isdst == 0)
    528  1.38  christos #   ifdef USG_COMPAT
    529  1.12    kleink 					diff = -timezone;
    530  1.38  christos #   else
    531  1.12    kleink 					continue;
    532  1.38  christos #   endif
    533  1.12    kleink 				else
    534  1.38  christos #   ifdef ALTZONE
    535  1.12    kleink 					diff = -altzone;
    536  1.38  christos #   else
    537  1.12    kleink 					continue;
    538  1.38  christos #   endif
    539  1.38  christos #  else
    540  1.16    kleink 				{
    541  1.16    kleink 					struct tm tmp;
    542  1.16    kleink 					time_t lct, gct;
    543  1.16    kleink 
    544  1.16    kleink 					/*
    545  1.16    kleink 					** Get calendar time from t
    546  1.16    kleink 					** being treated as local.
    547  1.16    kleink 					*/
    548  1.16    kleink 					tmp = *t; /* mktime discards const */
    549  1.16    kleink 					lct = mktime(&tmp);
    550  1.16    kleink 
    551  1.16    kleink 					if (lct == (time_t)-1)
    552  1.16    kleink 						continue;
    553  1.16    kleink 
    554  1.16    kleink 					/*
    555  1.16    kleink 					** Get calendar time from t
    556  1.16    kleink 					** being treated as GMT.
    557  1.16    kleink 					**/
    558  1.16    kleink 					tmp = *t; /* mktime discards const */
    559  1.16    kleink 					gct = timegm(&tmp);
    560  1.16    kleink 
    561  1.16    kleink 					if (gct == (time_t)-1)
    562  1.16    kleink 						continue;
    563  1.16    kleink 
    564  1.16    kleink 					/* LINTED difference will fit int */
    565  1.16    kleink 					diff = (intmax_t)gct - (intmax_t)lct;
    566  1.16    kleink 				}
    567  1.38  christos #  endif
    568  1.38  christos # endif
    569  1.39  christos 				negative = diff < 0;
    570  1.39  christos 				if (diff == 0) {
    571  1.39  christos #ifdef TM_ZONE
    572  1.39  christos 				  negative = t->TM_ZONE[0] == '-';
    573  1.39  christos #else
    574  1.39  christos 				  negative
    575  1.39  christos 				    = (t->tm_isdst < 0
    576  1.39  christos 				       || tzname[t->tm_isdst != 0][0] == '-');
    577  1.39  christos #endif
    578  1.39  christos 				}
    579  1.39  christos 				if (negative) {
    580  1.12    kleink 					sign = "-";
    581  1.12    kleink 					diff = -diff;
    582  1.12    kleink 				} else	sign = "+";
    583  1.12    kleink 				pt = _add(sign, pt, ptlim);
    584  1.20   mlelstv 				diff /= SECSPERMIN;
    585  1.20   mlelstv 				diff = (diff / MINSPERHOUR) * 100 +
    586  1.20   mlelstv 					(diff % MINSPERHOUR);
    587  1.30  christos 				_DIAGASSERT(__type_fit(int, diff));
    588  1.30  christos 				pt = _conv((int)diff, "%04d", pt, ptlim);
    589  1.12    kleink 				}
    590  1.38  christos #endif
    591   1.1       mrg 				continue;
    592  1.12    kleink #if 0
    593  1.12    kleink 			case '+':
    594  1.25     joerg 				pt = _fmt(sp, _TIME_LOCALE(loc)->date_fmt, t,
    595  1.25     joerg 				    pt, ptlim, warnp, loc);
    596  1.12    kleink 				continue;
    597  1.12    kleink #endif
    598   1.1       mrg 			case '%':
    599   1.1       mrg 			/*
    600  1.12    kleink 			** X311J/88-090 (4.12.3.5): if conversion char is
    601  1.20   mlelstv 			** undefined, behavior is undefined. Print out the
    602  1.12    kleink 			** character itself as printf(3) also does.
    603  1.12    kleink 			*/
    604   1.1       mrg 			default:
    605   1.1       mrg 				break;
    606   1.1       mrg 			}
    607   1.1       mrg 		}
    608  1.12    kleink 		if (pt == ptlim)
    609  1.12    kleink 			break;
    610  1.12    kleink 		*pt++ = *format;
    611   1.1       mrg 	}
    612  1.12    kleink 	return pt;
    613   1.1       mrg }
    614   1.1       mrg 
    615  1.21  christos size_t
    616  1.34  christos strftime(char *s, size_t maxsize, const char *format, const struct tm *t)
    617  1.21  christos {
    618  1.21  christos 	tzset();
    619  1.21  christos 	return strftime_z(NULL, s, maxsize, format, t);
    620  1.21  christos }
    621  1.21  christos 
    622  1.25     joerg size_t
    623  1.25     joerg strftime_l(char * __restrict s, size_t maxsize, const char * __restrict format,
    624  1.25     joerg     const struct tm * __restrict t, locale_t loc)
    625  1.25     joerg {
    626  1.25     joerg 	tzset();
    627  1.25     joerg 	return strftime_lz(NULL, s, maxsize, format, t, loc);
    628  1.25     joerg }
    629  1.25     joerg 
    630  1.12    kleink static char *
    631  1.34  christos _conv(int n, const char *format, char *pt, const char *ptlim)
    632   1.1       mrg {
    633  1.12    kleink 	char	buf[INT_STRLEN_MAXIMUM(int) + 1];
    634   1.1       mrg 
    635  1.19  christos 	(void) snprintf(buf, sizeof(buf), format, n);
    636  1.12    kleink 	return _add(buf, pt, ptlim);
    637   1.1       mrg }
    638   1.1       mrg 
    639  1.12    kleink static char *
    640  1.34  christos _add(const char *str, char *pt, const char *ptlim)
    641   1.1       mrg {
    642  1.12    kleink 	while (pt < ptlim && (*pt = *str++) != '\0')
    643  1.12    kleink 		++pt;
    644  1.12    kleink 	return pt;
    645   1.1       mrg }
    646  1.20   mlelstv 
    647  1.20   mlelstv /*
    648  1.20   mlelstv ** POSIX and the C Standard are unclear or inconsistent about
    649  1.20   mlelstv ** what %C and %y do if the year is negative or exceeds 9999.
    650  1.20   mlelstv ** Use the convention that %C concatenated with %y yields the
    651  1.20   mlelstv ** same output as %Y, and that %Y contains at least 4 bytes,
    652  1.20   mlelstv ** with more only if necessary.
    653  1.20   mlelstv */
    654  1.20   mlelstv 
    655  1.20   mlelstv static char *
    656  1.33  christos _yconv(int a, int b, bool convert_top, bool convert_yy,
    657  1.34  christos     char *pt, const char * ptlim)
    658  1.20   mlelstv {
    659  1.28  christos 	int	lead;
    660  1.28  christos 	int	trail;
    661  1.20   mlelstv 
    662  1.20   mlelstv #define DIVISOR	100
    663  1.20   mlelstv 	trail = a % DIVISOR + b % DIVISOR;
    664  1.20   mlelstv 	lead = a / DIVISOR + b / DIVISOR + trail / DIVISOR;
    665  1.20   mlelstv 	trail %= DIVISOR;
    666  1.20   mlelstv 	if (trail < 0 && lead > 0) {
    667  1.20   mlelstv 		trail += DIVISOR;
    668  1.20   mlelstv 		--lead;
    669  1.20   mlelstv 	} else if (lead < 0 && trail > 0) {
    670  1.20   mlelstv 		trail -= DIVISOR;
    671  1.20   mlelstv 		++lead;
    672  1.20   mlelstv 	}
    673  1.20   mlelstv 	if (convert_top) {
    674  1.20   mlelstv 		if (lead == 0 && trail < 0)
    675  1.20   mlelstv 			pt = _add("-0", pt, ptlim);
    676  1.20   mlelstv 		else	pt = _conv(lead, "%02d", pt, ptlim);
    677  1.20   mlelstv 	}
    678  1.20   mlelstv 	if (convert_yy)
    679  1.20   mlelstv 		pt = _conv(((trail < 0) ? -trail : trail), "%02d", pt, ptlim);
    680  1.20   mlelstv 	return pt;
    681  1.20   mlelstv }
    682