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 time zone (EDT), or nothing if no time zone is determinable
56 %a locale's abbreviated weekday name (Sun..Sat)
57 %A locale's full weekday name, variable length (Sunday..Saturday)
58 %b locale's abbreviated month name (Jan..Dec)
59 %B locale's full month name, variable length (January..December)
60 %c locale's date and time (Sat Nov 04 12:02:33 EST 1989)
62 %d day of month (01..31)
63 %e day of month ( 1..31)
66 %j day of year (001..366)
68 %U week number of year with Sunday as first day of week (00..53)
70 %W week number of year with Monday as first day of week (00..53)
71 %x locale's date representation (mm/dd/yy)
72 %y last two digits of year (00..99)
75 David MacKenzie <djm@gnu.ai.mit.edu> */
78 #define CONFIG_BROKETS
82 #if defined (CONFIG_BROKETS)
83 /* We use <config.h> instead of "config.h" so that a compilation
84 using -I. -I$srcdir will use ./config.h rather than $srcdir/config.h
85 (which it would do because it found this file in $srcdir). */
93 #include <sys/types.h>
94 #if defined(TM_IN_SYS_TIME) || (!defined(HAVE_TM_ZONE) && !defined(HAVE_TZNAME))
104 #if defined(HAVE_TZNAME)
105 extern char *tzname[2];
108 /* Types of padding for numbers in date and time. */
114 static char const* const days[] =
116 "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
119 static char const * const months[] =
121 "January", "February", "March", "April", "May", "June",
122 "July", "August", "September", "October", "November", "December"
125 /* Add character C to STRING and increment LENGTH,
126 unless LENGTH would exceed MAX. */
128 #define add_char(c) \
131 if (length + 1 <= max) \
132 string[length++] = (c); \
136 /* Add a 2 digit number to STRING, padding if specified.
137 Return the number of characters added, up to MAX. */
140 add_num2 (string, num, max, pad)
149 if (top == 0 && pad == blank)
151 else if (top != 0 || pad == zero)
152 add_char (top + '0');
153 add_char (num % 10 + '0');
157 /* Add a 3 digit number to STRING, padding if specified.
158 Return the number of characters added, up to MAX. */
161 add_num3 (string, num, max, pad)
168 int mid = (num - top * 100) / 10;
171 if (top == 0 && pad == blank)
173 else if (top != 0 || pad == zero)
174 add_char (top + '0');
175 if (mid == 0 && top == 0 && pad == blank)
177 else if (mid != 0 || top != 0 || pad == zero)
178 add_char (mid + '0');
179 add_char (num % 10 + '0');
183 /* Like strncpy except return the number of characters copied. */
186 add_str (to, from, max)
193 for (i = 0; from[i] && i <= max; ++i)
199 add_num_time_t (string, max, num)
204 /* This buffer is large enough to hold the character representation
205 (including the trailing NUL) of any unsigned decimal quantity
206 whose binary representation fits in 128 bits. */
210 if (sizeof (num) > 16)
212 sprintf (buf, "%lu", (unsigned long) num);
213 length = add_str (string, buf, max);
217 /* Return the week in the year of the time in TM, with the weeks
218 starting on Sundays. */
226 /* Set `dl' to the day in the year of the last day of the week previous
227 to the one containing the day specified in TM. If the day specified
228 in TM is in the first week of the year, `dl' will be negative or 0.
229 Otherwise, calculate the number of complete weeks before our week
230 (dl / 7) and add any partial week at the start of the year (dl % 7). */
231 dl = tm->tm_yday - tm->tm_wday;
232 return dl <= 0 ? 0 : dl / 7 + (dl % 7 != 0);
235 /* Return the week in the year of the time in TM, with the weeks
236 starting on Mondays. */
244 if (tm->tm_wday == 0)
247 wday = tm->tm_wday - 1;
248 dl = tm->tm_yday - wday;
249 return dl <= 0 ? 0 : dl / 7 + (dl % 7 != 0);
252 #if !defined(HAVE_TM_ZONE) && !defined(HAVE_TZNAME)
261 gettimeofday (&tv, &tz);
262 return timezone (tz.tz_minuteswest, tp->tm_isdst);
266 /* Format the time given in TM according to FORMAT, and put the
268 Return the number of characters (not including terminating null)
269 that were put into STRING, or 0 if the length would have
273 strftime (string, max, format, tm)
279 enum padding pad; /* Type of padding to apply. */
280 size_t length = 0; /* Characters put in STRING so far. */
282 for (; *format && length < max; ++format)
295 else if (*format == '_')
305 /* Literal character fields: */
324 add_num2 (&string[length], tm->tm_hour, max - length,
325 *format == 'H' ? pad : blank);
332 if (tm->tm_hour == 0)
334 else if (tm->tm_hour > 12)
335 hour12 = tm->tm_hour - 12;
337 hour12 = tm->tm_hour;
339 add_num2 (&string[length], hour12, max - length,
340 *format == 'I' ? pad : blank);
345 add_num2 (&string[length], tm->tm_min, max - length, pad);
348 if (tm->tm_hour < 12)
356 strftime (&string[length], max - length, "%I:%M:%S %p", tm);
360 strftime (&string[length], max - length, "%H:%M", tm);
365 struct tm writable_tm;
367 length += add_num_time_t (&string[length], max - length,
368 mktime (&writable_tm));
374 add_num2 (&string[length], tm->tm_sec, max - length, pad);
378 strftime (&string[length], max - length, "%H:%M:%S", tm);
382 strftime (&string[length], max - length, "%H:%M:%S", tm);
386 length += add_str (&string[length], tm->tm_zone, max - length);
389 if (tm->tm_isdst && tzname[1] && *tzname[1])
390 length += add_str (&string[length], tzname[1], max - length);
392 length += add_str (&string[length], tzname[0], max - length);
394 length += add_str (&string[length], zone_name (tm), max - length);
401 add_char (days[tm->tm_wday][0]);
402 add_char (days[tm->tm_wday][1]);
403 add_char (days[tm->tm_wday][2]);
407 add_str (&string[length], days[tm->tm_wday], max - length);
411 add_char (months[tm->tm_mon][0]);
412 add_char (months[tm->tm_mon][1]);
413 add_char (months[tm->tm_mon][2]);
417 add_str (&string[length], months[tm->tm_mon], max - length);
421 strftime (&string[length], max - length,
422 "%a %b %d %H:%M:%S %Z %Y", tm);
426 add_num2 (&string[length], (tm->tm_year + 1900) / 100,
431 add_num2 (&string[length], tm->tm_mday, max - length, pad);
435 add_num2 (&string[length], tm->tm_mday, max - length, blank);
439 strftime (&string[length], max - length, "%m/%d/%y", tm);
443 add_num3 (&string[length], tm->tm_yday + 1, max - length, pad);
447 add_num2 (&string[length], tm->tm_mon + 1, max - length, pad);
451 add_num2 (&string[length], sun_week (tm), max - length, pad);
454 add_char (tm->tm_wday + '0');
458 add_num2 (&string[length], mon_week (tm), max - length, pad);
462 strftime (&string[length], max - length, "%m/%d/%y", tm);
466 add_num2 (&string[length], tm->tm_year % 100,
470 add_char ((tm->tm_year + 1900) / 1000 + '0');
472 add_num3 (&string[length],
473 (1900 + tm->tm_year) % 1000, max - length, zero);