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
39 %s time in seconds since 00:00:00, Jan 1, 1970
48 %r time, 12-hour (hh:mm:ss [AP]M)
49 %R time, 24-hour (hh:mm)
50 %s time in seconds since 00:00:00, Jan 1, 1970 (a nonstandard extension)
52 %T time, 24-hour (hh:mm:ss)
53 %X locale's time representation (%H:%M:%S)
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> */
79 #if defined (CONFIG_BROKETS)
80 /* We use <config.h> instead of "config.h" so that a compilation
81 using -I. -I$srcdir will use ./config.h rather than $srcdir/config.h
82 (which it would do because it found this file in $srcdir). */
90 #include <sys/types.h>
91 #if defined(TM_IN_SYS_TIME) || (!defined(HAVE_TM_ZONE) && !defined(HAVE_TZNAME))
101 #if defined(HAVE_TZNAME)
102 extern char *tzname[2];
105 /* Types of padding for numbers in date and time. */
111 static char const* const days[] =
113 "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
116 static char const * const months[] =
118 "January", "February", "March", "April", "May", "June",
119 "July", "August", "September", "October", "November", "December"
122 /* Add character C to STRING and increment LENGTH,
123 unless LENGTH would exceed MAX. */
125 #define add_char(c) \
128 if (length + 1 <= max) \
129 string[length++] = (c); \
133 /* Add a 2 digit number to STRING, padding if specified.
134 Return the number of characters added, up to MAX. */
137 add_num2 (string, num, max, pad)
146 if (top == 0 && pad == blank)
148 else if (top != 0 || pad == zero)
149 add_char (top + '0');
150 add_char (num % 10 + '0');
154 /* Add a 3 digit number to STRING, padding if specified.
155 Return the number of characters added, up to MAX. */
158 add_num3 (string, num, max, pad)
165 int mid = (num - top * 100) / 10;
168 if (top == 0 && pad == blank)
170 else if (top != 0 || pad == zero)
171 add_char (top + '0');
172 if (mid == 0 && top == 0 && pad == blank)
174 else if (mid != 0 || top != 0 || pad == zero)
175 add_char (mid + '0');
176 add_char (num % 10 + '0');
180 /* Like strncpy except return the number of characters copied. */
183 add_str (to, from, max)
190 for (i = 0; from[i] && i <= max; ++i)
196 add_num_time_t (string, max, num)
201 /* This buffer is large enough to hold the character representation
202 (including the trailing NUL) of any unsigned decimal quantity
203 whose binary representation fits in 128 bits. */
207 if (sizeof (num) > 16)
209 sprintf (buf, "%lu", (unsigned long) num);
210 length = add_str (string, buf, max);
214 /* Return the week in the year of the time in TM, with the weeks
215 starting on Sundays. */
223 /* Set `dl' to the day in the year of the last day of the week previous
224 to the one containing the day specified in TM. If the day specified
225 in TM is in the first week of the year, `dl' will be negative or 0.
226 Otherwise, calculate the number of complete weeks before our week
227 (dl / 7) and add any partial week at the start of the year (dl % 7). */
228 dl = tm->tm_yday - tm->tm_wday;
229 return dl <= 0 ? 0 : dl / 7 + (dl % 7 != 0);
232 /* Return the week in the year of the time in TM, with the weeks
233 starting on Mondays. */
241 if (tm->tm_wday == 0)
244 wday = tm->tm_wday - 1;
245 dl = tm->tm_yday - wday;
246 return dl <= 0 ? 0 : dl / 7 + (dl % 7 != 0);
249 #if !defined(HAVE_TM_ZONE) && !defined(HAVE_TZNAME)
258 gettimeofday (&tv, &tz);
259 return timezone (tz.tz_minuteswest, tp->tm_isdst);
263 /* Format the time given in TM according to FORMAT, and put the
265 Return the number of characters (not including terminating null)
266 that were put into STRING, or 0 if the length would have
270 strftime (string, max, format, tm)
276 enum padding pad; /* Type of padding to apply. */
277 size_t length = 0; /* Characters put in STRING so far. */
279 for (; *format && length < max; ++format)
292 else if (*format == '_')
302 /* Literal character fields: */
321 add_num2 (&string[length], tm->tm_hour, max - length,
322 *format == 'H' ? pad : blank);
329 if (tm->tm_hour == 0)
331 else if (tm->tm_hour > 12)
332 hour12 = tm->tm_hour - 12;
334 hour12 = tm->tm_hour;
336 add_num2 (&string[length], hour12, max - length,
337 *format == 'I' ? pad : blank);
342 add_num2 (&string[length], tm->tm_min, max - length, pad);
345 if (tm->tm_hour < 12)
353 strftime (&string[length], max - length, "%I:%M:%S %p", tm);
357 strftime (&string[length], max - length, "%H:%M", tm);
362 struct tm writable_tm;
364 length += add_num_time_t (&string[length], max - length,
365 mktime (&writable_tm));
371 add_num2 (&string[length], tm->tm_sec, max - length, pad);
375 strftime (&string[length], max - length, "%H:%M:%S", tm);
379 strftime (&string[length], max - length, "%H:%M:%S", tm);
383 length += add_str (&string[length], tm->tm_zone, max - length);
386 if (tm->tm_isdst && tzname[1] && *tzname[1])
387 length += add_str (&string[length], tzname[1], max - length);
389 length += add_str (&string[length], tzname[0], max - length);
391 length += add_str (&string[length], zone_name (tm), max - length);
398 add_char (days[tm->tm_wday][0]);
399 add_char (days[tm->tm_wday][1]);
400 add_char (days[tm->tm_wday][2]);
404 add_str (&string[length], days[tm->tm_wday], max - length);
408 add_char (months[tm->tm_mon][0]);
409 add_char (months[tm->tm_mon][1]);
410 add_char (months[tm->tm_mon][2]);
414 add_str (&string[length], months[tm->tm_mon], max - length);
418 strftime (&string[length], max - length,
419 "%a %b %d %H:%M:%S %Z %Y", tm);
423 add_num2 (&string[length], (tm->tm_year + 1900) / 100,
428 add_num2 (&string[length], tm->tm_mday, max - length, pad);
432 add_num2 (&string[length], tm->tm_mday, max - length, blank);
436 strftime (&string[length], max - length, "%m/%d/%y", tm);
440 add_num3 (&string[length], tm->tm_yday + 1, max - length, pad);
444 add_num2 (&string[length], tm->tm_mon + 1, max - length, pad);
448 add_num2 (&string[length], sun_week (tm), max - length, pad);
451 add_char (tm->tm_wday + '0');
455 add_num2 (&string[length], mon_week (tm), max - length, pad);
459 strftime (&string[length], max - length, "%m/%d/%y", tm);
463 add_num2 (&string[length], tm->tm_year % 100,
467 add_char ((tm->tm_year + 1900) / 1000 + '0');
469 add_num3 (&string[length],
470 (1900 + tm->tm_year) % 1000, max - length, zero);