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> */
82 #include <sys/types.h>
83 #if defined(TM_IN_SYS_TIME) || (!defined(HAVE_TM_ZONE) && !defined(HAVE_TZNAME))
93 #if defined(HAVE_TZNAME)
94 extern char *tzname
[2];
97 /* Types of padding for numbers in date and time. */
103 static char const* const days
[] =
105 "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
108 static char const * const months
[] =
110 "January", "February", "March", "April", "May", "June",
111 "July", "August", "September", "October", "November", "December"
114 /* Add character C to STRING and increment LENGTH,
115 unless LENGTH would exceed MAX. */
117 #define add_char(c) \
120 if (length + 1 <= max) \
121 string[length++] = (c); \
125 /* Add a 2 digit number to STRING, padding if specified.
126 Return the number of characters added, up to MAX. */
129 add_num2 (string
, num
, max
, pad
)
138 if (top
== 0 && pad
== blank
)
140 else if (top
!= 0 || pad
== zero
)
141 add_char (top
+ '0');
142 add_char (num
% 10 + '0');
146 /* Add a 3 digit number to STRING, padding if specified.
147 Return the number of characters added, up to MAX. */
150 add_num3 (string
, num
, max
, pad
)
157 int mid
= (num
- top
* 100) / 10;
160 if (top
== 0 && pad
== blank
)
162 else if (top
!= 0 || pad
== zero
)
163 add_char (top
+ '0');
164 if (mid
== 0 && top
== 0 && pad
== blank
)
166 else if (mid
!= 0 || top
!= 0 || pad
== zero
)
167 add_char (mid
+ '0');
168 add_char (num
% 10 + '0');
172 /* Like strncpy except return the number of characters copied. */
175 add_str (to
, from
, max
)
182 for (i
= 0; from
[i
] && i
<= max
; ++i
)
188 add_num_time_t (string
, max
, num
)
193 /* This buffer is large enough to hold the character representation
194 (including the trailing NUL) of any unsigned decimal quantity
195 whose binary representation fits in 128 bits. */
199 if (sizeof (num
) > 16)
201 sprintf (buf
, "%lu", (unsigned long) num
);
202 length
= add_str (string
, buf
, max
);
206 /* Return the week in the year of the time in TM, with the weeks
207 starting on Sundays. */
215 /* Set `dl' to the day in the year of the last day of the week previous
216 to the one containing the day specified in TM. If the day specified
217 in TM is in the first week of the year, `dl' will be negative or 0.
218 Otherwise, calculate the number of complete weeks before our week
219 (dl / 7) and add any partial week at the start of the year (dl % 7). */
220 dl
= tm
->tm_yday
- tm
->tm_wday
;
221 return dl
<= 0 ? 0 : dl
/ 7 + (dl
% 7 != 0);
224 /* Return the week in the year of the time in TM, with the weeks
225 starting on Mondays. */
233 if (tm
->tm_wday
== 0)
236 wday
= tm
->tm_wday
- 1;
237 dl
= tm
->tm_yday
- wday
;
238 return dl
<= 0 ? 0 : dl
/ 7 + (dl
% 7 != 0);
241 #if !defined(HAVE_TM_ZONE) && !defined(HAVE_TZNAME)
250 gettimeofday (&tv
, &tz
);
251 return timezone (tz
.tz_minuteswest
, tp
->tm_isdst
);
255 /* Format the time given in TM according to FORMAT, and put the
257 Return the number of characters (not including terminating null)
258 that were put into STRING, or 0 if the length would have
262 strftime (string
, max
, format
, tm
)
268 enum padding pad
; /* Type of padding to apply. */
269 size_t length
= 0; /* Characters put in STRING so far. */
271 for (; *format
&& length
< max
; ++format
)
284 else if (*format
== '_')
294 /* Literal character fields: */
313 add_num2 (&string
[length
], tm
->tm_hour
, max
- length
,
314 *format
== 'H' ? pad
: blank
);
321 if (tm
->tm_hour
== 0)
323 else if (tm
->tm_hour
> 12)
324 hour12
= tm
->tm_hour
- 12;
326 hour12
= tm
->tm_hour
;
328 add_num2 (&string
[length
], hour12
, max
- length
,
329 *format
== 'I' ? pad
: blank
);
334 add_num2 (&string
[length
], tm
->tm_min
, max
- length
, pad
);
337 if (tm
->tm_hour
< 12)
345 strftime (&string
[length
], max
- length
, "%I:%M:%S %p", tm
);
349 strftime (&string
[length
], max
- length
, "%H:%M", tm
);
354 struct tm writable_tm
;
356 length
+= add_num_time_t (&string
[length
], max
- length
,
357 mktime (&writable_tm
));
363 add_num2 (&string
[length
], tm
->tm_sec
, max
- length
, pad
);
367 strftime (&string
[length
], max
- length
, "%H:%M:%S", tm
);
371 strftime (&string
[length
], max
- length
, "%H:%M:%S", tm
);
375 length
+= add_str (&string
[length
], tm
->tm_zone
, max
- length
);
378 if (tm
->tm_isdst
&& tzname
[1] && *tzname
[1])
379 length
+= add_str (&string
[length
], tzname
[1], max
- length
);
381 length
+= add_str (&string
[length
], tzname
[0], max
- length
);
383 length
+= add_str (&string
[length
], zone_name (tm
), max
- length
);
390 add_char (days
[tm
->tm_wday
][0]);
391 add_char (days
[tm
->tm_wday
][1]);
392 add_char (days
[tm
->tm_wday
][2]);
396 add_str (&string
[length
], days
[tm
->tm_wday
], max
- length
);
400 add_char (months
[tm
->tm_mon
][0]);
401 add_char (months
[tm
->tm_mon
][1]);
402 add_char (months
[tm
->tm_mon
][2]);
406 add_str (&string
[length
], months
[tm
->tm_mon
], max
- length
);
410 strftime (&string
[length
], max
- length
,
411 "%a %b %d %H:%M:%S %Z %Y", tm
);
415 add_num2 (&string
[length
], (tm
->tm_year
+ 1900) / 100,
420 add_num2 (&string
[length
], tm
->tm_mday
, max
- length
, pad
);
424 add_num2 (&string
[length
], tm
->tm_mday
, max
- length
, blank
);
428 strftime (&string
[length
], max
- length
, "%m/%d/%y", tm
);
432 add_num3 (&string
[length
], tm
->tm_yday
+ 1, max
- length
, pad
);
436 add_num2 (&string
[length
], tm
->tm_mon
+ 1, max
- length
, pad
);
440 add_num2 (&string
[length
], sun_week (tm
), max
- length
, pad
);
443 add_char (tm
->tm_wday
+ '0');
447 add_num2 (&string
[length
], mon_week (tm
), max
- length
, pad
);
451 strftime (&string
[length
], max
- length
, "%m/%d/%y", tm
);
455 add_num2 (&string
[length
], tm
->tm_year
% 100,
459 add_char ((tm
->tm_year
+ 1900) / 1000 + '0');
461 add_num3 (&string
[length
],
462 (1900 + tm
->tm_year
) % 1000, max
- length
, zero
);