blob: c05f6b5bd60effaca94e6f262bedec141c0b81fc [file] [log] [blame]
Elliott Hughes0a610d02016-07-29 14:04:17 -07001/* Convert a broken-down time stamp to a string. */
2
3/* Copyright 1989 The Regents of the University of California.
4 All rights reserved.
5
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
8 are met:
9 1. Redistributions of source code must retain the above copyright
10 notice, this list of conditions and the following disclaimer.
11 2. Redistributions in binary form must reproduce the above copyright
12 notice, this list of conditions and the following disclaimer in the
13 documentation and/or other materials provided with the distribution.
14 3. Neither the name of the University nor the names of its contributors
15 may be used to endorse or promote products derived from this software
16 without specific prior written permission.
17
18 THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND
19 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 SUCH DAMAGE. */
29
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080030/*
Elliott Hughes0a610d02016-07-29 14:04:17 -070031** Based on the UCB version with the copyright notice appearing above.
Elliott Hughes5f564542014-06-19 13:54:10 -070032**
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080033** This is ANSIish only when "multibyte character == plain character".
34*/
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080035
36#include "private.h"
37
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080038#include "tzfile.h"
39#include "fcntl.h"
40#include "locale.h"
Elliott Hughes5f564542014-06-19 13:54:10 -070041
Elliott Hughesa9209d72016-09-16 18:16:47 -070042#if defined(__BIONIC__)
Elliott Hughes905e6d52014-07-25 11:55:59 -070043
Elliott Hughes905e6d52014-07-25 11:55:59 -070044/* LP32 had a 32-bit time_t, so we need to work around that here. */
Elliott Hughes52defb72014-05-05 17:14:02 -070045#if defined(__LP64__)
46#define time64_t time_t
47#define mktime64 mktime
48#else
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -070049#include <time64.h>
Elliott Hughes52defb72014-05-05 17:14:02 -070050#endif
Elliott Hughes905e6d52014-07-25 11:55:59 -070051
Elliott Hughes5f564542014-06-19 13:54:10 -070052#include <ctype.h>
Elliott Hughes905e6d52014-07-25 11:55:59 -070053
Elliott Hughes39d903a2014-07-25 15:50:31 -070054#endif
Elliott Hughes905e6d52014-07-25 11:55:59 -070055
The Android Open Source Project1dc9e472009-03-03 19:28:35 -080056struct lc_time_T {
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -070057 const char * mon[MONSPERYEAR];
58 const char * month[MONSPERYEAR];
59 const char * wday[DAYSPERWEEK];
60 const char * weekday[DAYSPERWEEK];
61 const char * X_fmt;
62 const char * x_fmt;
63 const char * c_fmt;
64 const char * am;
65 const char * pm;
66 const char * date_fmt;
67};
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -070068
69#define Locale (&C_time_locale)
70
71static const struct lc_time_T C_time_locale = {
72 {
73 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
74 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
75 }, {
76 "January", "February", "March", "April", "May", "June",
77 "July", "August", "September", "October", "November", "December"
78 }, {
79 "Sun", "Mon", "Tue", "Wed",
80 "Thu", "Fri", "Sat"
81 }, {
82 "Sunday", "Monday", "Tuesday", "Wednesday",
83 "Thursday", "Friday", "Saturday"
84 },
85
86 /* X_fmt */
87 "%H:%M:%S",
88
89 /*
90 ** x_fmt
91 ** C99 requires this format.
92 ** Using just numbers (as here) makes Quakers happier;
93 ** it's also compatible with SVR4.
94 */
95 "%m/%d/%y",
96
97 /*
98 ** c_fmt
99 ** C99 requires this format.
100 ** Previously this code used "%D %X", but we now conform to C99.
101 ** Note that
102 ** "%a %b %d %H:%M:%S %Y"
103 ** is used by Solaris 2.3.
104 */
105 "%a %b %e %T %Y",
106
107 /* am */
108 "AM",
109
110 /* pm */
111 "PM",
112
113 /* date_fmt */
114 "%a %b %e %H:%M:%S %Z %Y"
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800115};
116
Elliott Hughesce4783c2013-07-12 17:31:11 -0700117static char * _add(const char *, char *, const char *, int);
118static char * _conv(int, const char *, char *, const char *);
119static char * _fmt(const char *, const struct tm *, char *, const char *,
Elliott Hughes39d903a2014-07-25 15:50:31 -0700120 int *);
Elliott Hughes9fb22a32015-10-07 17:13:40 -0700121static char * _yconv(int, int, bool, bool, char *, const char *, int);
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800122
Elliott Hughes0a610d02016-07-29 14:04:17 -0700123#if !HAVE_POSIX_DECLS
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700124extern char * tzname[];
Elliott Hughes0a610d02016-07-29 14:04:17 -0700125#endif
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800126
127#ifndef YEAR_2000_NAME
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700128#define YEAR_2000_NAME "CHECK_STRFTIME_FORMATS_FOR_TWO_DIGIT_YEARS"
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800129#endif /* !defined YEAR_2000_NAME */
130
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700131#define IN_NONE 0
132#define IN_SOME 1
133#define IN_THIS 2
134#define IN_ALL 3
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800135
Elliott Hughes0a610d02016-07-29 14:04:17 -0700136#if HAVE_STRFTIME_L
137size_t
138strftime_l(char *s, size_t maxsize, char const *format, struct tm const *t,
139 locale_t locale)
140{
141 /* Just call strftime, as only the C locale is supported. */
142 return strftime(s, maxsize, format, t);
143}
144#endif
145
146#define FORCE_LOWER_CASE 0x100 /* Android extension. */
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800147
148size_t
Elliott Hughes9fb22a32015-10-07 17:13:40 -0700149strftime(char *s, size_t maxsize, const char *format, const struct tm *t)
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700150{
151 char * p;
152 int warn;
153
154 tzset();
155 warn = IN_NONE;
Elliott Hughes39d903a2014-07-25 15:50:31 -0700156 p = _fmt(((format == NULL) ? "%c" : format), t, s, s + maxsize, &warn);
Elliott Hughes9a5a3e82014-05-05 20:28:28 -0700157#ifndef NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700158 if (warn != IN_NONE && getenv(YEAR_2000_NAME) != NULL) {
Elliott Hughes9fb22a32015-10-07 17:13:40 -0700159 fprintf(stderr, "\n");
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700160 if (format == NULL)
Elliott Hughes9fb22a32015-10-07 17:13:40 -0700161 fprintf(stderr, "NULL strftime format ");
162 else fprintf(stderr, "strftime format \"%s\" ",
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700163 format);
Elliott Hughes9fb22a32015-10-07 17:13:40 -0700164 fprintf(stderr, "yields only two digits of years in ");
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700165 if (warn == IN_SOME)
Elliott Hughes9fb22a32015-10-07 17:13:40 -0700166 fprintf(stderr, "some locales");
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700167 else if (warn == IN_THIS)
Elliott Hughes9fb22a32015-10-07 17:13:40 -0700168 fprintf(stderr, "the current locale");
169 else fprintf(stderr, "all locales");
170 fprintf(stderr, "\n");
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700171 }
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800172#endif /* !defined NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU */
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700173 if (p == s + maxsize)
174 return 0;
175 *p = '\0';
176 return p - s;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800177}
178
179static char *getformat(int modifier, char *normal, char *underscore,
180 char *dash, char *zero) {
181 switch (modifier) {
182 case '_':
183 return underscore;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800184 case '-':
185 return dash;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800186 case '0':
187 return zero;
188 }
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800189 return normal;
190}
191
192static char *
Elliott Hughes9fb22a32015-10-07 17:13:40 -0700193_fmt(const char *format, const struct tm *t, char *pt,
194 const char *ptlim, int *warnp)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800195{
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700196 for ( ; *format; ++format) {
197 if (*format == '%') {
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800198 int modifier = 0;
199label:
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700200 switch (*++format) {
201 case '\0':
202 --format;
203 break;
204 case 'A':
205 pt = _add((t->tm_wday < 0 ||
206 t->tm_wday >= DAYSPERWEEK) ?
Elliott Hughes39d903a2014-07-25 15:50:31 -0700207 "?" : Locale->weekday[t->tm_wday],
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700208 pt, ptlim, modifier);
209 continue;
210 case 'a':
211 pt = _add((t->tm_wday < 0 ||
212 t->tm_wday >= DAYSPERWEEK) ?
Elliott Hughes39d903a2014-07-25 15:50:31 -0700213 "?" : Locale->wday[t->tm_wday],
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700214 pt, ptlim, modifier);
215 continue;
216 case 'B':
Elliott Hughes39d903a2014-07-25 15:50:31 -0700217 pt = _add((t->tm_mon < 0 ||
Eric Fischera48fa7f2009-05-15 13:33:20 -0700218 t->tm_mon >= MONSPERYEAR) ?
Elliott Hughes39d903a2014-07-25 15:50:31 -0700219 "?" : Locale->month[t->tm_mon],
Eric Fischera48fa7f2009-05-15 13:33:20 -0700220 pt, ptlim, modifier);
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700221 continue;
222 case 'b':
223 case 'h':
224 pt = _add((t->tm_mon < 0 ||
225 t->tm_mon >= MONSPERYEAR) ?
Elliott Hughes39d903a2014-07-25 15:50:31 -0700226 "?" : Locale->mon[t->tm_mon],
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700227 pt, ptlim, modifier);
228 continue;
229 case 'C':
230 /*
231 ** %C used to do a...
232 ** _fmt("%a %b %e %X %Y", t);
233 ** ...whereas now POSIX 1003.2 calls for
234 ** something completely different.
235 ** (ado, 1993-05-24)
236 */
Elliott Hughes9fb22a32015-10-07 17:13:40 -0700237 pt = _yconv(t->tm_year, TM_YEAR_BASE,
238 true, false, pt, ptlim, modifier);
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700239 continue;
240 case 'c':
241 {
242 int warn2 = IN_SOME;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800243
Elliott Hughes39d903a2014-07-25 15:50:31 -0700244 pt = _fmt(Locale->c_fmt, t, pt, ptlim, &warn2);
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700245 if (warn2 == IN_ALL)
246 warn2 = IN_THIS;
247 if (warn2 > *warnp)
248 *warnp = warn2;
249 }
250 continue;
251 case 'D':
Elliott Hughes39d903a2014-07-25 15:50:31 -0700252 pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp);
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700253 continue;
254 case 'd':
Elliott Hughes9fb22a32015-10-07 17:13:40 -0700255 pt = _conv(t->tm_mday, getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim);
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700256 continue;
257 case 'E':
258 case 'O':
259 /*
260 ** C99 locale modifiers.
261 ** The sequences
262 ** %Ec %EC %Ex %EX %Ey %EY
263 ** %Od %oe %OH %OI %Om %OM
264 ** %OS %Ou %OU %OV %Ow %OW %Oy
265 ** are supposed to provide alternate
266 ** representations.
267 */
268 goto label;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800269 case '_':
270 case '-':
271 case '0':
272 case '^':
273 case '#':
274 modifier = *format;
275 goto label;
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700276 case 'e':
Elliott Hughes9fb22a32015-10-07 17:13:40 -0700277 pt = _conv(t->tm_mday, getformat(modifier, "%2d", "%2d", "%d", "%02d"), pt, ptlim);
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700278 continue;
279 case 'F':
Elliott Hughes39d903a2014-07-25 15:50:31 -0700280 pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp);
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700281 continue;
282 case 'H':
Elliott Hughes9fb22a32015-10-07 17:13:40 -0700283 pt = _conv(t->tm_hour, getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim);
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700284 continue;
285 case 'I':
286 pt = _conv((t->tm_hour % 12) ?
287 (t->tm_hour % 12) : 12,
Elliott Hughes9fb22a32015-10-07 17:13:40 -0700288 getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim);
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700289 continue;
290 case 'j':
Elliott Hughes9fb22a32015-10-07 17:13:40 -0700291 pt = _conv(t->tm_yday + 1, getformat(modifier, "%03d", "%3d", "%d", "%03d"), pt, ptlim);
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700292 continue;
293 case 'k':
294 /*
295 ** This used to be...
296 ** _conv(t->tm_hour % 12 ?
297 ** t->tm_hour % 12 : 12, 2, ' ');
298 ** ...and has been changed to the below to
299 ** match SunOS 4.1.1 and Arnold Robbins'
300 ** strftime version 3.0. That is, "%k" and
301 ** "%l" have been swapped.
302 ** (ado, 1993-05-24)
303 */
Elliott Hughes9fb22a32015-10-07 17:13:40 -0700304 pt = _conv(t->tm_hour, getformat(modifier, "%2d", "%2d", "%d", "%02d"), pt, ptlim);
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700305 continue;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800306#ifdef KITCHEN_SINK
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700307 case 'K':
308 /*
309 ** After all this time, still unclaimed!
310 */
Elliott Hughes39d903a2014-07-25 15:50:31 -0700311 pt = _add("kitchen sink", pt, ptlim);
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700312 continue;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800313#endif /* defined KITCHEN_SINK */
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700314 case 'l':
315 /*
316 ** This used to be...
317 ** _conv(t->tm_hour, 2, ' ');
318 ** ...and has been changed to the below to
319 ** match SunOS 4.1.1 and Arnold Robbin's
320 ** strftime version 3.0. That is, "%k" and
321 ** "%l" have been swapped.
322 ** (ado, 1993-05-24)
323 */
324 pt = _conv((t->tm_hour % 12) ?
325 (t->tm_hour % 12) : 12,
Elliott Hughes9fb22a32015-10-07 17:13:40 -0700326 getformat(modifier, "%2d", "%2d", "%d", "%02d"), pt, ptlim);
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700327 continue;
328 case 'M':
Elliott Hughes9fb22a32015-10-07 17:13:40 -0700329 pt = _conv(t->tm_min, getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim);
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700330 continue;
331 case 'm':
Elliott Hughes9fb22a32015-10-07 17:13:40 -0700332 pt = _conv(t->tm_mon + 1, getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim);
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700333 continue;
334 case 'n':
335 pt = _add("\n", pt, ptlim, modifier);
336 continue;
Elliott Hughes9fb22a32015-10-07 17:13:40 -0700337 case 'P':
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700338 case 'p':
339 pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ?
Elliott Hughes39d903a2014-07-25 15:50:31 -0700340 Locale->pm :
341 Locale->am,
Elliott Hughes9fb22a32015-10-07 17:13:40 -0700342 pt, ptlim, (*format == 'P') ? FORCE_LOWER_CASE : modifier);
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700343 continue;
344 case 'R':
Elliott Hughes39d903a2014-07-25 15:50:31 -0700345 pt = _fmt("%H:%M", t, pt, ptlim, warnp);
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700346 continue;
347 case 'r':
Elliott Hughes39d903a2014-07-25 15:50:31 -0700348 pt = _fmt("%I:%M:%S %p", t, pt, ptlim, warnp);
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700349 continue;
350 case 'S':
Elliott Hughes9fb22a32015-10-07 17:13:40 -0700351 pt = _conv(t->tm_sec, getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim);
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700352 continue;
353 case 's':
354 {
355 struct tm tm;
356 char buf[INT_STRLEN_MAXIMUM(
357 time64_t) + 1];
358 time64_t mkt;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800359
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700360 tm = *t;
361 mkt = mktime64(&tm);
362 if (TYPE_SIGNED(time64_t))
Elliott Hughes9fb22a32015-10-07 17:13:40 -0700363 snprintf(buf, sizeof(buf), "%"PRIdMAX,
364 (intmax_t) mkt);
365 else snprintf(buf, sizeof(buf), "%"PRIuMAX,
366 (uintmax_t) mkt);
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700367 pt = _add(buf, pt, ptlim, modifier);
368 }
369 continue;
370 case 'T':
Elliott Hughes39d903a2014-07-25 15:50:31 -0700371 pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp);
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700372 continue;
373 case 't':
374 pt = _add("\t", pt, ptlim, modifier);
375 continue;
376 case 'U':
377 pt = _conv((t->tm_yday + DAYSPERWEEK -
378 t->tm_wday) / DAYSPERWEEK,
Elliott Hughes9fb22a32015-10-07 17:13:40 -0700379 getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim);
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700380 continue;
381 case 'u':
382 /*
383 ** From Arnold Robbins' strftime version 3.0:
384 ** "ISO 8601: Weekday as a decimal number
385 ** [1 (Monday) - 7]"
386 ** (ado, 1993-05-24)
387 */
388 pt = _conv((t->tm_wday == 0) ?
Elliott Hughes9fb22a32015-10-07 17:13:40 -0700389 DAYSPERWEEK : t->tm_wday,
390 "%d", pt, ptlim);
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700391 continue;
392 case 'V': /* ISO 8601 week number */
393 case 'G': /* ISO 8601 year (four digits) */
394 case 'g': /* ISO 8601 year (two digits) */
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800395/*
396** From Arnold Robbins' strftime version 3.0: "the week number of the
397** year (the first Monday as the first day of week 1) as a decimal number
398** (01-53)."
399** (ado, 1993-05-24)
400**
Elliott Hughes39d903a2014-07-25 15:50:31 -0700401** From <http://www.ft.uni-erlangen.de/~mskuhn/iso-time.html> by Markus Kuhn:
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800402** "Week 01 of a year is per definition the first week which has the
403** Thursday in this year, which is equivalent to the week which contains
404** the fourth day of January. In other words, the first week of a new year
405** is the week which has the majority of its days in the new year. Week 01
406** might also contain days from the previous year and the week before week
407** 01 of a year is the last week (52 or 53) of the previous year even if
408** it contains days from the new year. A week starts with Monday (day 1)
409** and ends with Sunday (day 7). For example, the first week of the year
410** 1997 lasts from 1996-12-30 to 1997-01-05..."
411** (ado, 1996-01-02)
412*/
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700413 {
414 int year;
415 int base;
416 int yday;
417 int wday;
418 int w;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800419
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700420 year = t->tm_year;
421 base = TM_YEAR_BASE;
422 yday = t->tm_yday;
423 wday = t->tm_wday;
424 for ( ; ; ) {
425 int len;
426 int bot;
427 int top;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800428
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700429 len = isleap_sum(year, base) ?
430 DAYSPERLYEAR :
431 DAYSPERNYEAR;
432 /*
433 ** What yday (-3 ... 3) does
434 ** the ISO year begin on?
435 */
436 bot = ((yday + 11 - wday) %
437 DAYSPERWEEK) - 3;
438 /*
439 ** What yday does the NEXT
440 ** ISO year begin on?
441 */
442 top = bot -
443 (len % DAYSPERWEEK);
444 if (top < -3)
445 top += DAYSPERWEEK;
446 top += len;
447 if (yday >= top) {
448 ++base;
449 w = 1;
450 break;
451 }
452 if (yday >= bot) {
453 w = 1 + ((yday - bot) /
454 DAYSPERWEEK);
455 break;
456 }
457 --base;
458 yday += isleap_sum(year, base) ?
459 DAYSPERLYEAR :
460 DAYSPERNYEAR;
461 }
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800462#ifdef XPG4_1994_04_09
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700463 if ((w == 52 &&
464 t->tm_mon == TM_JANUARY) ||
465 (w == 1 &&
466 t->tm_mon == TM_DECEMBER))
467 w = 53;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800468#endif /* defined XPG4_1994_04_09 */
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700469 if (*format == 'V')
Elliott Hughes9fb22a32015-10-07 17:13:40 -0700470 pt = _conv(w, getformat(modifier, "%02d", "%2d", "%d", "%02d"),
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700471 pt, ptlim);
472 else if (*format == 'g') {
473 *warnp = IN_ALL;
Elliott Hughes9fb22a32015-10-07 17:13:40 -0700474 pt = _yconv(year, base,
475 false, true,
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700476 pt, ptlim, modifier);
Elliott Hughes9fb22a32015-10-07 17:13:40 -0700477 } else pt = _yconv(year, base,
478 true, true,
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700479 pt, ptlim, modifier);
480 }
481 continue;
482 case 'v':
483 /*
484 ** From Arnold Robbins' strftime version 3.0:
485 ** "date as dd-bbb-YYYY"
486 ** (ado, 1993-05-24)
487 */
Elliott Hughes39d903a2014-07-25 15:50:31 -0700488 pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp);
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700489 continue;
490 case 'W':
491 pt = _conv((t->tm_yday + DAYSPERWEEK -
492 (t->tm_wday ?
493 (t->tm_wday - 1) :
494 (DAYSPERWEEK - 1))) / DAYSPERWEEK,
Elliott Hughes9fb22a32015-10-07 17:13:40 -0700495 getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim);
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700496 continue;
497 case 'w':
498 pt = _conv(t->tm_wday, "%d", pt, ptlim);
499 continue;
500 case 'X':
Elliott Hughes39d903a2014-07-25 15:50:31 -0700501 pt = _fmt(Locale->X_fmt, t, pt, ptlim, warnp);
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700502 continue;
503 case 'x':
504 {
505 int warn2 = IN_SOME;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800506
Elliott Hughes39d903a2014-07-25 15:50:31 -0700507 pt = _fmt(Locale->x_fmt, t, pt, ptlim, &warn2);
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700508 if (warn2 == IN_ALL)
509 warn2 = IN_THIS;
510 if (warn2 > *warnp)
511 *warnp = warn2;
512 }
513 continue;
514 case 'y':
515 *warnp = IN_ALL;
Elliott Hughes9fb22a32015-10-07 17:13:40 -0700516 pt = _yconv(t->tm_year, TM_YEAR_BASE,
517 false, true,
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700518 pt, ptlim, modifier);
519 continue;
520 case 'Y':
Elliott Hughes9fb22a32015-10-07 17:13:40 -0700521 pt = _yconv(t->tm_year, TM_YEAR_BASE,
522 true, true,
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700523 pt, ptlim, modifier);
524 continue;
525 case 'Z':
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800526#ifdef TM_ZONE
Elliott Hughesa9cac4c2015-11-12 16:51:31 -0800527 // BEGIN: Android-changed.
528 {
529 const char* zone = t->TM_ZONE;
530 if (!zone || !*zone) {
531 // "The value of tm_isdst shall be positive if Daylight Savings Time is
532 // in effect, 0 if Daylight Savings Time is not in effect, and negative
533 // if the information is not available."
534 if (t->tm_isdst == 0) zone = tzname[0];
535 else if (t->tm_isdst > 0) zone = tzname[1];
536
537 // "Replaced by the timezone name or abbreviation, or by no bytes if no
538 // timezone information exists."
539 if (!zone || !*zone) zone = "";
540 }
541 pt = _add(zone, pt, ptlim, modifier);
542 }
543 // END: Android-changed.
Elliott Hughes9fb22a32015-10-07 17:13:40 -0700544#else
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700545 if (t->tm_isdst >= 0)
546 pt = _add(tzname[t->tm_isdst != 0],
Elliott Hughes9fb22a32015-10-07 17:13:40 -0700547 pt, ptlim);
548#endif
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700549 /*
550 ** C99 says that %Z must be replaced by the
551 ** empty string if the time zone is not
552 ** determinable.
553 */
554 continue;
555 case 'z':
556 {
Elliott Hughese0d0b152013-09-27 00:04:30 -0700557 long diff;
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700558 char const * sign;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800559
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700560 if (t->tm_isdst < 0)
561 continue;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800562#ifdef TM_GMTOFF
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700563 diff = t->TM_GMTOFF;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800564#else /* !defined TM_GMTOFF */
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700565 /*
Elliott Hughese0d0b152013-09-27 00:04:30 -0700566 ** C99 says that the UT offset must
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700567 ** be computed by looking only at
568 ** tm_isdst. This requirement is
569 ** incorrect, since it means the code
570 ** must rely on magic (in this case
571 ** altzone and timezone), and the
572 ** magic might not have the correct
573 ** offset. Doing things correctly is
574 ** tricky and requires disobeying C99;
575 ** see GNU C strftime for details.
576 ** For now, punt and conform to the
577 ** standard, even though it's incorrect.
578 **
579 ** C99 says that %z must be replaced by the
580 ** empty string if the time zone is not
581 ** determinable, so output nothing if the
582 ** appropriate variables are not available.
583 */
584 if (t->tm_isdst == 0)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800585#ifdef USG_COMPAT
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700586 diff = -timezone;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800587#else /* !defined USG_COMPAT */
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700588 continue;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800589#endif /* !defined USG_COMPAT */
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700590 else
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800591#ifdef ALTZONE
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700592 diff = -altzone;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800593#else /* !defined ALTZONE */
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700594 continue;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800595#endif /* !defined ALTZONE */
596#endif /* !defined TM_GMTOFF */
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700597 if (diff < 0) {
598 sign = "-";
599 diff = -diff;
600 } else sign = "+";
601 pt = _add(sign, pt, ptlim, modifier);
602 diff /= SECSPERMIN;
603 diff = (diff / MINSPERHOUR) * 100 +
604 (diff % MINSPERHOUR);
Elliott Hughes9fb22a32015-10-07 17:13:40 -0700605 pt = _conv(diff, getformat(modifier, "%04d", "%4d", "%d", "%04d"), pt, ptlim);
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700606 }
607 continue;
608 case '+':
Elliott Hughes39d903a2014-07-25 15:50:31 -0700609 pt = _fmt(Locale->date_fmt, t, pt, ptlim,
610 warnp);
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700611 continue;
612 case '%':
613 /*
614 ** X311J/88-090 (4.12.3.5): if conversion char is
615 ** undefined, behavior is undefined. Print out the
616 ** character itself as printf(3) also does.
617 */
618 default:
619 break;
620 }
621 }
622 if (pt == ptlim)
623 break;
624 *pt++ = *format;
625 }
626 return pt;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800627}
628
629static char *
Elliott Hughes9fb22a32015-10-07 17:13:40 -0700630_conv(int n, const char *format, char *pt, const char *ptlim)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800631{
Elliott Hughes9fb22a32015-10-07 17:13:40 -0700632 char buf[INT_STRLEN_MAXIMUM(int) + 1];
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800633
Elliott Hughes9fb22a32015-10-07 17:13:40 -0700634 snprintf(buf, sizeof(buf), format, n);
635 return _add(buf, pt, ptlim, 0);
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800636}
637
638static char *
Elliott Hughes39d903a2014-07-25 15:50:31 -0700639_add(const char *str, char *pt, const char *const ptlim, int modifier)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800640{
641 int c;
642
643 switch (modifier) {
644 case FORCE_LOWER_CASE:
645 while (pt < ptlim && (*pt = tolower(*str++)) != '\0') {
646 ++pt;
647 }
648 break;
649
650 case '^':
651 while (pt < ptlim && (*pt = toupper(*str++)) != '\0') {
652 ++pt;
653 }
654 break;
655
656 case '#':
657 while (pt < ptlim && (c = *str++) != '\0') {
658 if (isupper(c)) {
659 c = tolower(c);
660 } else if (islower(c)) {
661 c = toupper(c);
662 }
663 *pt = c;
664 ++pt;
665 }
666
667 break;
668
669 default:
670 while (pt < ptlim && (*pt = *str++) != '\0') {
671 ++pt;
672 }
673 }
674
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700675 return pt;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800676}
677
678/*
679** POSIX and the C Standard are unclear or inconsistent about
680** what %C and %y do if the year is negative or exceeds 9999.
681** Use the convention that %C concatenated with %y yields the
682** same output as %Y, and that %Y contains at least 4 bytes,
683** with more only if necessary.
684*/
685
686static char *
Elliott Hughes9fb22a32015-10-07 17:13:40 -0700687_yconv(int a, int b, bool convert_top, bool convert_yy,
688 char *pt, const char *ptlim, int modifier)
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800689{
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700690 register int lead;
691 register int trail;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800692
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700693#define DIVISOR 100
694 trail = a % DIVISOR + b % DIVISOR;
695 lead = a / DIVISOR + b / DIVISOR + trail / DIVISOR;
696 trail %= DIVISOR;
697 if (trail < 0 && lead > 0) {
698 trail += DIVISOR;
699 --lead;
700 } else if (lead < 0 && trail > 0) {
701 trail -= DIVISOR;
702 ++lead;
703 }
704 if (convert_top) {
705 if (lead == 0 && trail < 0)
706 pt = _add("-0", pt, ptlim, modifier);
Elliott Hughes39d903a2014-07-25 15:50:31 -0700707 else pt = _conv(lead, getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim);
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700708 }
709 if (convert_yy)
Elliott Hughes39d903a2014-07-25 15:50:31 -0700710 pt = _conv(((trail < 0) ? -trail : trail), getformat(modifier, "%02d", "%2d", "%d", "%02d"), pt, ptlim);
The Android Open Source Projectedbe7fc2009-03-18 22:20:24 -0700711 return pt;
The Android Open Source Project1dc9e472009-03-03 19:28:35 -0800712}