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];
87 /* Types of padding for numbers in date and time. */
93 static char const* const days[] =
95 "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
98 static char const * const months[] =
100 "January", "February", "March", "April", "May", "June",
101 "July", "August", "September", "October", "November", "December"
104 /* Add character C to STRING and increment LENGTH,
105 unless LENGTH would exceed MAX. */
107 #define add_char(c) \
110 if (length + 1 <= max) \
111 string[length++] = (c); \
115 /* Add a 2 digit number to STRING, padding if specified.
116 Return the number of characters added, up to MAX. */
119 add_num2 (string, num, max, pad)
128 if (top == 0 && pad == blank)
130 else if (top != 0 || pad == zero)
131 add_char (top + '0');
132 add_char (num % 10 + '0');
136 /* Add a 3 digit number to STRING, padding if specified.
137 Return the number of characters added, up to MAX. */
140 add_num3 (string, num, max, pad)
147 int mid = (num - top * 100) / 10;
150 if (top == 0 && pad == blank)
152 else if (top != 0 || pad == zero)
153 add_char (top + '0');
154 if (mid == 0 && top == 0 && pad == blank)
156 else if (mid != 0 || top != 0 || pad == zero)
157 add_char (mid + '0');
158 add_char (num % 10 + '0');
162 /* Like strncpy except return the number of characters copied. */
165 add_str (to, from, max)
172 for (i = 0; from[i] && i <= max; ++i)
177 /* Return the week in the year of the time in TM, with the weeks
178 starting on Sundays. */
186 /* Set `dl' to the day in the year of the last day of the week previous
187 to the one containing the day specified in TM. If the day specified
188 in TM is in the first week of the year, `dl' will be negative or 0.
189 Otherwise, calculate the number of complete weeks before our week
190 (dl / 7) and add any partial week at the start of the year (dl % 7). */
191 dl = tm->tm_yday - tm->tm_wday;
192 return dl <= 0 ? 0 : dl / 7 + (dl % 7 != 0);
195 /* Return the week in the year of the time in TM, with the weeks
196 starting on Mondays. */
204 if (tm->tm_wday == 0)
207 wday = tm->tm_wday - 1;
208 dl = tm->tm_yday - wday;
209 return dl <= 0 ? 0 : dl / 7 + (dl % 7 != 0);
212 #if !defined(HAVE_TM_ZONE) && !defined(HAVE_TZNAME)
221 gettimeofday (&tv, &tz);
222 return timezone (tz.tz_minuteswest, tp->tm_isdst);
226 /* Format the time given in TM according to FORMAT, and put the
228 Return the number of characters (not including terminating null)
229 that were put into STRING, or 0 if the length would have
233 strftime (string, max, format, tm)
239 enum padding pad; /* Type of padding to apply. */
240 size_t length = 0; /* Characters put in STRING so far. */
242 for (; *format && length < max; ++format)
255 else if (*format == '_')
265 /* Literal character fields: */
284 add_num2 (&string[length], tm->tm_hour, max - length,
285 *format == 'H' ? pad : blank);
292 if (tm->tm_hour == 0)
294 else if (tm->tm_hour > 12)
295 hour12 = tm->tm_hour - 12;
297 hour12 = tm->tm_hour;
299 add_num2 (&string[length], hour12, max - length,
300 *format == 'I' ? pad : blank);
305 add_num2 (&string[length], tm->tm_min, max - length, pad);
308 if (tm->tm_hour < 12)
316 strftime (&string[length], max - length, "%I:%M:%S %p", tm);
320 strftime (&string[length], max - length, "%H:%M", tm);
324 add_num2 (&string[length], tm->tm_sec, max - length, pad);
328 strftime (&string[length], max - length, "%H:%M:%S", tm);
332 strftime (&string[length], max - length, "%H:%M:%S", tm);
336 length += add_str (&string[length], tm->tm_zone, max - length);
339 if (tm->tm_isdst && tzname[1] && *tzname[1])
340 length += add_str (&string[length], tzname[1], max - length);
342 length += add_str (&string[length], tzname[0], max - length);
344 length += add_str (&string[length], zone_name (tm), max - length);
351 add_char (days[tm->tm_wday][0]);
352 add_char (days[tm->tm_wday][1]);
353 add_char (days[tm->tm_wday][2]);
357 add_str (&string[length], days[tm->tm_wday], max - length);
361 add_char (months[tm->tm_mon][0]);
362 add_char (months[tm->tm_mon][1]);
363 add_char (months[tm->tm_mon][2]);
367 add_str (&string[length], months[tm->tm_mon], max - length);
371 strftime (&string[length], max - length,
372 "%a %b %d %H:%M:%S %Z %Y", tm);
376 add_num2 (&string[length], (tm->tm_year + 1900) / 100,
381 add_num2 (&string[length], tm->tm_mday, max - length, pad);
385 add_num2 (&string[length], tm->tm_mday, max - length, blank);
389 strftime (&string[length], max - length, "%m/%d/%y", tm);
393 add_num3 (&string[length], tm->tm_yday + 1, max - length, pad);
397 add_num2 (&string[length], tm->tm_mon + 1, max - length, pad);
401 add_num2 (&string[length], sun_week (tm), max - length, pad);
404 add_char (tm->tm_wday + '0');
408 add_num2 (&string[length], mon_week (tm), max - length, pad);
412 strftime (&string[length], max - length, "%m/%d/%y", tm);
416 add_num2 (&string[length], tm->tm_year % 100,
420 add_char ((tm->tm_year + 1900) / 1000 + '0');
422 add_num3 (&string[length],
423 (1900 + tm->tm_year) % 1000, max - length, zero);