1 /* strftime - custom formatting of date and/or time
2 Copyright (C) 1989, 1991, 1992 Free Software Foundation, Inc.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
18 /* Note: this version of strftime lacks locale support,
21 Performs `%' substitutions similar to those in printf. Except
22 where noted, substituted fields have a fixed size; numeric fields are
23 padded if necessary. Padding is with zeros by default; for fields
24 that display a single number, padding can be changed or inhibited by
25 following the `%' with one of the modifiers described below. Unknown
26 field specifiers are copied as normal characters. All other
27 characters are copied to the output without change.
29 Supports a superset of the ANSI C field specifiers.
31 Literal character fields:
36 Numeric modifiers (a nonstandard extension):
37 - do not pad the field
38 _ pad the field with spaces
47 %r time, 12-hour (hh:mm:ss [AP]M)
48 %R time, 24-hour (hh:mm)
49 %s time in seconds since 00:00:00, Jan 1, 1970 (a nonstandard extension)
51 %T time, 24-hour (hh:mm:ss)
52 %X locale's time representation (%H:%M:%S)
53 %z RFC-822 style numeric timezone (-0500) (a nonstandard extension)
54 %Z time zone (EDT), or nothing if no time zone is determinable
57 %a locale's abbreviated weekday name (Sun..Sat)
58 %A locale's full weekday name, variable length (Sunday..Saturday)
59 %b locale's abbreviated month name (Jan..Dec)
60 %B locale's full month name, variable length (January..December)
61 %c locale's date and time (Sat Nov 04 12:02:33 EST 1989)
63 %d day of month (01..31)
64 %e day of month ( 1..31)
67 %j day of year (001..366)
69 %U week number of year with Sunday as first day of week (00..53)
71 %W week number of year with Monday as first day of week (00..53)
72 %x locale's date representation (mm/dd/yy)
73 %y last two digits of year (00..99)
76 David MacKenzie <djm@gnu.ai.mit.edu> */
83 #include <sys/types.h>
84 #if defined(TM_IN_SYS_TIME) || (!defined(HAVE_TM_ZONE) && !defined(HAVE_TZNAME))
94 #if defined(HAVE_TZNAME)
95 extern char *tzname[2];
98 /* Types of padding for numbers in date and time. */
104 static char const* const days[] =
106 "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
109 static char const * const months[] =
111 "January", "February", "March", "April", "May", "June",
112 "July", "August", "September", "October", "November", "December"
115 /* Add character C to STRING and increment LENGTH,
116 unless LENGTH would exceed MAX. */
118 #define add_char(c) \
121 if (length + 1 <= max) \
122 string[length++] = (c); \
126 /* Add a 2 digit number to STRING, padding if specified.
127 Return the number of characters added, up to MAX. */
130 add_num2 (string, num, max, pad)
139 if (top == 0 && pad == blank)
141 else if (top != 0 || pad == zero)
142 add_char (top + '0');
143 add_char (num % 10 + '0');
147 /* Add a 3 digit number to STRING, padding if specified.
148 Return the number of characters added, up to MAX. */
151 add_num3 (string, num, max, pad)
158 int mid = (num - top * 100) / 10;
161 if (top == 0 && pad == blank)
163 else if (top != 0 || pad == zero)
164 add_char (top + '0');
165 if (mid == 0 && top == 0 && pad == blank)
167 else if (mid != 0 || top != 0 || pad == zero)
168 add_char (mid + '0');
169 add_char (num % 10 + '0');
173 /* Like strncpy except return the number of characters copied. */
176 add_str (to, from, max)
183 for (i = 0; from[i] && i <= max; ++i)
189 add_num_time_t (string, max, num)
194 /* This buffer is large enough to hold the character representation
195 (including the trailing NUL) of any unsigned decimal quantity
196 whose binary representation fits in 128 bits. */
200 if (sizeof (num) > 16)
202 sprintf (buf, "%lu", (unsigned long) num);
203 length = add_str (string, buf, max);
207 /* Convert MINUTES_EAST into a string suitable for use as the RFC-822
208 timezone indicator. Write no more than MAX bytes into STRING.
209 Return the number of bytes written into STRING. */
212 add_num_tz (string, max, minutes_east)
222 if (minutes_east < 0)
225 minutes_east = -minutes_east;
230 length = 1 + add_num2 (&string[1], (minutes_east / 60) % 24, max - 1, zero);
231 length += add_num2 (&string[length], minutes_east % 60, max - length, zero);
236 /* Return the week in the year of the time in TM, with the weeks
237 starting on Sundays. */
245 /* %U Week of the year (Sunday as the first day of the week) as a decimal
246 number [00-53]. All days in a new year preceding the first Sunday are
247 considered to be in week 0. */
249 dl = tm->tm_yday - tm->tm_wday;
250 return dl < 0 ? 0 : dl / 7 + 1;
253 /* Return the week in the year of the time in TM, with the weeks
254 starting on Mondays. */
262 if (tm->tm_wday == 0)
265 wday = tm->tm_wday - 1;
266 dl = tm->tm_yday - wday;
267 return dl <= 0 ? 0 : dl / 7 + (dl % 7 != 0);
270 #if !defined(HAVE_TM_ZONE) && !defined(HAVE_TZNAME)
279 gettimeofday (&tv, &tz);
280 return timezone (tz.tz_minuteswest, tp->tm_isdst);
284 /* Format the time given in TM according to FORMAT, and put the
286 Return the number of characters (not including terminating null)
287 that were put into STRING, or 0 if the length would have
291 strftime (string, max, format, tm)
297 enum padding pad; /* Type of padding to apply. */
298 size_t length = 0; /* Characters put in STRING so far. */
300 for (; *format && length < max; ++format)
313 else if (*format == '_')
323 /* Literal character fields: */
342 add_num2 (&string[length], tm->tm_hour, max - length,
343 *format == 'H' ? pad : blank);
350 if (tm->tm_hour == 0)
352 else if (tm->tm_hour > 12)
353 hour12 = tm->tm_hour - 12;
355 hour12 = tm->tm_hour;
357 add_num2 (&string[length], hour12, max - length,
358 *format == 'I' ? pad : blank);
363 add_num2 (&string[length], tm->tm_min, max - length, pad);
366 if (tm->tm_hour < 12)
374 strftime (&string[length], max - length, "%I:%M:%S %p", tm);
378 strftime (&string[length], max - length, "%H:%M", tm);
383 struct tm writable_tm;
385 length += add_num_time_t (&string[length], max - length,
386 mktime (&writable_tm));
392 add_num2 (&string[length], tm->tm_sec, max - length, pad);
396 strftime (&string[length], max - length, "%H:%M:%S", tm);
400 strftime (&string[length], max - length, "%H:%M:%S", tm);
410 tml = *localtime (&t); /* Canonicalize the local time */
413 /* Compute the difference */
415 diff = tml.tm_min - tmg.tm_min;
416 diff += 60 * (tml.tm_hour - tmg.tm_hour);
418 if (tml.tm_mon != tmg.tm_mon)
420 /* We assume no timezone differs from UTC by more than
421 +- 23 hours. This should be safe. */
422 if (tmg.tm_mday == 1)
424 else /* tml.tm_mday == 1 */
428 diff += 1440 * (tml.tm_mday - tmg.tm_mday);
430 length += add_num_tz (&string[length], max - length, diff);
435 length += add_str (&string[length], tm->tm_zone, max - length);
438 if (tm->tm_isdst && tzname[1] && *tzname[1])
439 length += add_str (&string[length], tzname[1], max - length);
441 length += add_str (&string[length], tzname[0], max - length);
443 length += add_str (&string[length], zone_name (tm), max - length);
450 add_char (days[tm->tm_wday][0]);
451 add_char (days[tm->tm_wday][1]);
452 add_char (days[tm->tm_wday][2]);
456 add_str (&string[length], days[tm->tm_wday], max - length);
460 add_char (months[tm->tm_mon][0]);
461 add_char (months[tm->tm_mon][1]);
462 add_char (months[tm->tm_mon][2]);
466 add_str (&string[length], months[tm->tm_mon], max - length);
470 strftime (&string[length], max - length,
471 "%a %b %d %H:%M:%S %Z %Y", tm);
475 add_num2 (&string[length], (tm->tm_year + 1900) / 100,
480 add_num2 (&string[length], tm->tm_mday, max - length, pad);
484 add_num2 (&string[length], tm->tm_mday, max - length, blank);
488 strftime (&string[length], max - length, "%m/%d/%y", tm);
492 add_num3 (&string[length], tm->tm_yday + 1, max - length, pad);
496 add_num2 (&string[length], tm->tm_mon + 1, max - length, pad);
500 add_num2 (&string[length], sun_week (tm), max - length, pad);
503 add_char (tm->tm_wday + '0');
507 add_num2 (&string[length], mon_week (tm), max - length, pad);
511 strftime (&string[length], max - length, "%m/%d/%y", tm);
515 add_num2 (&string[length], tm->tm_year % 100,
519 add_char ((tm->tm_year + 1900) / 1000 + '0');
521 add_num3 (&string[length],
522 (1900 + tm->tm_year) % 1000, max - length, zero);