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)
50 %T time, 24-hour (hh:mm:ss)
51 %X locale's time representation (%H:%M:%S)
52 %Z time zone (EDT), or nothing if no time zone is determinable
55 %a locale's abbreviated weekday name (Sun..Sat)
56 %A locale's full weekday name, variable length (Sunday..Saturday)
57 %b locale's abbreviated month name (Jan..Dec)
58 %B locale's full month name, variable length (January..December)
59 %c locale's date and time (Sat Nov 04 12:02:33 EST 1989)
61 %d day of month (01..31)
62 %e day of month ( 1..31)
65 %j day of year (001..366)
67 %U week number of year with Sunday as first day of week (00..53)
69 %W week number of year with Monday as first day of week (00..53)
70 %x locale's date representation (mm/dd/yy)
71 %y last two digits of year (00..99)
74 David MacKenzie <djm@gnu.ai.mit.edu> */
76 #include <sys/types.h>
77 #if defined(TM_IN_SYS_TIME) || (!defined(HAVE_TM_ZONE) && !defined(HAVE_TZNAME))
83 #if defined(HAVE_TZNAME)
84 extern char *tzname[2];
91 /* Types of padding for numbers in date and time. */
97 static char const* const days[] =
99 "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
102 static char const * const months[] =
104 "January", "February", "March", "April", "May", "June",
105 "July", "August", "September", "October", "November", "December"
108 /* Add character C to STRING and increment LENGTH,
109 unless LENGTH would exceed MAX. */
111 #define add_char(c) (length + 1 <= max) && (string[length++] = (c))
113 /* Add a 2 digit number to STRING, padding if specified.
114 Return the number of characters added, up to MAX. */
117 add_num2 (string, num, max, pad)
126 if (top == 0 && pad == blank)
128 else if (top != 0 || pad == zero)
129 add_char (top + '0');
130 add_char (num % 10 + '0');
134 /* Add a 3 digit number to STRING, padding if specified.
135 Return the number of characters added, up to MAX. */
138 add_num3 (string, num, max, pad)
145 int mid = (num - top * 100) / 10;
148 if (top == 0 && pad == blank)
150 else if (top != 0 || pad == zero)
151 add_char (top + '0');
152 if (mid == 0 && top == 0 && pad == blank)
154 else if (mid != 0 || top != 0 || pad == zero)
155 add_char (mid + '0');
156 add_char (num % 10 + '0');
160 /* Like strncpy except return the number of characters copied. */
163 add_str (to, from, max)
170 for (i = 0; from[i] && i <= max; ++i)
175 /* Return the week in the year of the time in TM, with the weeks
176 starting on Sundays. */
184 /* Set `dl' to the day in the year of the last day of the week previous
185 to the one containing the day specified in TM. If the day specified
186 in TM is in the first week of the year, `dl' will be negative or 0.
187 Otherwise, calculate the number of complete weeks before our week
188 (dl / 7) and add any partial week at the start of the year (dl % 7). */
189 dl = tm->tm_yday - tm->tm_wday;
190 return dl <= 0 ? 0 : dl / 7 + (dl % 7 != 0);
193 /* Return the week in the year of the time in TM, with the weeks
194 starting on Mondays. */
202 if (tm->tm_wday == 0)
205 wday = tm->tm_wday - 1;
206 dl = tm->tm_yday - wday;
207 return dl <= 0 ? 0 : dl / 7 + (dl % 7 != 0);
210 #if !defined(HAVE_TM_ZONE) && !defined(HAVE_TZNAME)
219 gettimeofday (&tv, &tz);
220 return timezone (tz.tz_minuteswest, tp->tm_isdst);
224 /* Format the time given in TM according to FORMAT, and put the
226 Return the number of characters (not including terminating null)
227 that were put into STRING, or 0 if the length would have
231 strftime (string, max, format, tm)
237 enum padding pad; /* Type of padding to apply. */
238 size_t length = 0; /* Characters put in STRING so far. */
240 for (; *format && length < max; ++format)
253 else if (*format == '_')
263 /* Literal character fields: */
282 add_num2 (&string[length], tm->tm_hour, max - length,
283 *format == 'H' ? pad : blank);
290 if (tm->tm_hour == 0)
292 else if (tm->tm_hour > 12)
293 hour12 = tm->tm_hour - 12;
295 hour12 = tm->tm_hour;
297 add_num2 (&string[length], hour12, max - length,
298 *format == 'I' ? pad : blank);
303 add_num2 (&string[length], tm->tm_min, max - length, pad);
306 if (tm->tm_hour < 12)
314 strftime (&string[length], max - length, "%I:%M:%S %p", tm);
318 strftime (&string[length], max - length, "%H:%M", tm);
322 add_num2 (&string[length], tm->tm_sec, max - length, pad);
326 strftime (&string[length], max - length, "%H:%M:%S", tm);
330 strftime (&string[length], max - length, "%H:%M:%S", tm);
334 length += add_str (&string[length], tm->tm_zone, max - length);
337 if (tm->tm_isdst && tzname[1] && *tzname[1])
338 length += add_str (&string[length], tzname[1], max - length);
340 length += add_str (&string[length], tzname[0], max - length);
342 length += add_str (&string[length], zone_name (tm), max - length);
349 add_char (days[tm->tm_wday][0]);
350 add_char (days[tm->tm_wday][1]);
351 add_char (days[tm->tm_wday][2]);
355 add_str (&string[length], days[tm->tm_wday], max - length);
359 add_char (months[tm->tm_mon][0]);
360 add_char (months[tm->tm_mon][1]);
361 add_char (months[tm->tm_mon][2]);
365 add_str (&string[length], months[tm->tm_mon], max - length);
369 strftime (&string[length], max - length,
370 "%a %b %d %H:%M:%S %Z %Y", tm);
374 add_num2 (&string[length], (tm->tm_year + 1900) / 100,
379 add_num2 (&string[length], tm->tm_mday, max - length, pad);
383 add_num2 (&string[length], tm->tm_mday, max - length, blank);
387 strftime (&string[length], max - length, "%m/%d/%y", tm);
391 add_num3 (&string[length], tm->tm_yday + 1, max - length, pad);
395 add_num2 (&string[length], tm->tm_mon + 1, max - length, pad);
399 add_num2 (&string[length], sun_week (tm), max - length, pad);
402 add_char (tm->tm_wday + '0');
406 add_num2 (&string[length], mon_week (tm), max - length, pad);
410 strftime (&string[length], max - length, "%m/%d/%y", tm);
414 add_num2 (&string[length], tm->tm_year % 100,
418 add_char ((tm->tm_year + 1900) / 1000 + '0');
420 add_num3 (&string[length],
421 (1900 + tm->tm_year) % 1000, max - length, zero);