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; see the file COPYING. If not, write to
16 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 Boston, MA 02111-1307, USA. */
19 /* Synched up with: FSF 19.30. */
21 /* Note: this version of strftime lacks locale support,
24 Performs `%' substitutions similar to those in printf. Except
25 where noted, substituted fields have a fixed size; numeric fields are
26 padded if necessary. Padding is with zeros by default; for fields
27 that display a single number, padding can be changed or inhibited by
28 following the `%' with one of the modifiers described below. Unknown
29 field specifiers are copied as normal characters. All other
30 characters are copied to the output without change.
32 Supports a superset of the ANSI C field specifiers.
34 Literal character fields:
39 Numeric modifiers (a nonstandard extension):
40 - do not pad the field
41 _ pad the field with spaces
50 %r time, 12-hour (hh:mm:ss [AP]M)
51 %R time, 24-hour (hh:mm)
52 %s time in seconds since 00:00:00, Jan 1, 1970 (a nonstandard extension)
54 %T time, 24-hour (hh:mm:ss)
55 %X locale's time representation (%H:%M:%S)
56 %Z time zone (EDT), or nothing if no time zone is determinable
59 %a locale's abbreviated weekday name (Sun..Sat)
60 %A locale's full weekday name, variable length (Sunday..Saturday)
61 %b locale's abbreviated month name (Jan..Dec)
62 %B locale's full month name, variable length (January..December)
63 %c locale's date and time (Sat Nov 04 12:02:33 EST 1989)
65 %d day of month (01..31)
66 %e day of month ( 1..31)
69 %j day of year (001..366)
71 %U week number of year with Sunday as first day of week (00..53)
73 %W week number of year with Monday as first day of week (00..53)
74 %x locale's date representation (mm/dd/yy)
75 %y last two digits of year (00..99)
78 David MacKenzie <djm@gnu.ai.mit.edu> */
86 #include <sys/types.h>
87 #if defined(TM_IN_SYS_TIME) || (!defined(HAVE_TM_ZONE) && !defined(HAVE_TZNAME))
97 #if defined(WIN32_NATIVE) || defined(CYGWIN)
100 #if defined(HAVE_TZNAME)
101 extern char *tzname[2];
103 #endif /* WIN32_NATIVE */
106 #define strftime emacs_strftime
109 /* Types of padding for numbers in date and time. */
115 static char const* const days[] =
117 "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
120 static char const * const months[] =
122 "January", "February", "March", "April", "May", "June",
123 "July", "August", "September", "October", "November", "December"
126 /* Add character C to STRING and increment LENGTH,
127 unless LENGTH would exceed MAX. */
129 #define add_char(c) do \
131 if (length + 1 <= max) \
132 string[length++] = (c); \
135 /* Add a 2 digit number to STRING, padding if specified.
136 Return the number of characters added, up to MAX. */
139 add_num2 (char *string, int num, int max, enum padding pad)
144 if (top == 0 && pad == blank)
146 else if (top != 0 || pad == zero)
147 add_char (top + '0');
148 add_char (num % 10 + '0');
152 /* Add a 3 digit number to STRING, padding if specified.
153 Return the number of characters added, up to MAX. */
156 add_num3 (char *string, int num, int max, enum padding pad)
159 int mid = (num - top * 100) / 10;
162 if (top == 0 && pad == blank)
164 else if (top != 0 || pad == zero)
165 add_char (top + '0');
166 if (mid == 0 && top == 0 && pad == blank)
168 else if (mid != 0 || top != 0 || pad == zero)
169 add_char (mid + '0');
170 add_char (num % 10 + '0');
174 /* Like strncpy except return the number of characters copied. */
177 add_str (char *to, const char *from, int max)
181 for (i = 0; from[i] && i <= max; ++i)
187 add_num_time_t (char *string, int max, time_t num)
189 /* This buffer is large enough to hold the character representation
190 (including the trailing NUL) of any unsigned decimal quantity
191 whose binary representation fits in 128 bits. */
195 if (sizeof (num) > 16)
197 sprintf (buf, "%lu", (unsigned long) num);
198 length = add_str (string, buf, max);
202 /* Return the week in the year of the time in TM, with the weeks
203 starting on Sundays. */
206 sun_week (const struct tm *tm)
210 /* Set `dl' to the day in the year of the last day of the week previous
211 to the one containing the day specified in TM. If the day specified
212 in TM is in the first week of the year, `dl' will be negative or 0.
213 Otherwise, calculate the number of complete weeks before our week
214 (dl / 7) and add any partial week at the start of the year (dl % 7). */
215 dl = tm->tm_yday - tm->tm_wday;
216 return dl <= 0 ? 0 : dl / 7 + (dl % 7 != 0);
219 /* Return the week in the year of the time in TM, with the weeks
220 starting on Mondays. */
223 mon_week (const struct tm *tm)
227 if (tm->tm_wday == 0)
230 wday = tm->tm_wday - 1;
231 dl = tm->tm_yday - wday;
232 return dl <= 0 ? 0 : dl / 7 + (dl % 7 != 0);
235 #if !defined(HAVE_TM_ZONE) && !defined(HAVE_TZNAME)
236 char *zone_name (const struct tm *tp);
238 zone_name (const struct tm *tp)
244 gettimeofday (&tv, &tz);
245 return timezone (tz.tz_minuteswest, tp->tm_isdst);
249 /* Format the time given in TM according to FORMAT, and put the
251 Return the number of characters (not including terminating null)
252 that were put into STRING, or 0 if the length would have
255 size_t strftime (char *string, size_t max, const char *format,
256 const struct tm *tm);
259 strftime (char *string, size_t max, const char *format, const struct tm *tm)
261 enum padding pad; /* Type of padding to apply. */
262 size_t length = 0; /* Characters put in STRING so far. */
264 for (; *format && length < max; ++format)
277 else if (*format == '_')
287 /* Literal character fields: */
306 add_num2 (&string[length], tm->tm_hour, max - length,
307 *format == 'H' ? pad : blank);
314 if (tm->tm_hour == 0)
316 else if (tm->tm_hour > 12)
317 hour12 = tm->tm_hour - 12;
319 hour12 = tm->tm_hour;
321 add_num2 (&string[length], hour12, max - length,
322 *format == 'I' ? pad : blank);
327 add_num2 (&string[length], tm->tm_min, max - length, pad);
330 if (tm->tm_hour < 12)
338 strftime (&string[length], max - length, "%I:%M:%S %p", tm);
342 strftime (&string[length], max - length, "%H:%M", tm);
347 struct tm writable_tm;
349 length += add_num_time_t (&string[length], max - length,
350 mktime (&writable_tm));
356 add_num2 (&string[length], tm->tm_sec, max - length, pad);
360 strftime (&string[length], max - length, "%H:%M:%S", tm);
364 strftime (&string[length], max - length, "%H:%M:%S", tm);
368 length += add_str (&string[length], tm->tm_zone, max - length);
371 if (tm->tm_isdst && tzname[1] && *tzname[1])
372 length += add_str (&string[length], tzname[1], max - length);
374 length += add_str (&string[length], tzname[0], max - length);
376 length += add_str (&string[length], zone_name (tm), max - length);
383 add_char (days[tm->tm_wday][0]);
384 add_char (days[tm->tm_wday][1]);
385 add_char (days[tm->tm_wday][2]);
389 add_str (&string[length], days[tm->tm_wday], max - length);
393 add_char (months[tm->tm_mon][0]);
394 add_char (months[tm->tm_mon][1]);
395 add_char (months[tm->tm_mon][2]);
399 add_str (&string[length], months[tm->tm_mon], max - length);
403 strftime (&string[length], max - length,
404 "%a %b %d %H:%M:%S %Z %Y", tm);
408 add_num2 (&string[length], (tm->tm_year + 1900) / 100,
413 add_num2 (&string[length], tm->tm_mday, max - length, pad);
417 add_num2 (&string[length], tm->tm_mday, max - length, blank);
421 strftime (&string[length], max - length, "%m/%d/%y", tm);
425 add_num3 (&string[length], tm->tm_yday + 1, max - length, pad);
429 add_num2 (&string[length], tm->tm_mon + 1, max - length, pad);
433 add_num2 (&string[length], sun_week (tm), max - length, pad);
436 add_char (tm->tm_wday + '0');
440 add_num2 (&string[length], mon_week (tm), max - length, pad);
444 strftime (&string[length], max - length, "%m/%d/%y", tm);
448 add_num2 (&string[length], tm->tm_year % 100,
452 add_char ((tm->tm_year + 1900) / 1000 + '0');
454 add_num3 (&string[length],
455 (1900 + tm->tm_year) % 1000, max - length, zero);