comparison src/strftime.c @ 0:376386a54a3c r19-14

Import from CVS: tag r19-14
author cvs
date Mon, 13 Aug 2007 08:45:50 +0200
parents
children 4be1180a9e89
comparison
equal deleted inserted replaced
-1:000000000000 0:376386a54a3c
1 /* strftime - custom formatting of date and/or time
2 Copyright (C) 1989, 1991, 1992 Free Software Foundation, Inc.
3
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)
7 any later version.
8
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.
13
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. */
18
19 /* Synched up with: FSF 19.30. */
20
21 /* Note: this version of strftime lacks locale support,
22 but it is standalone.
23
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.
31
32 Supports a superset of the ANSI C field specifiers.
33
34 Literal character fields:
35 % %
36 n newline
37 t tab
38
39 Numeric modifiers (a nonstandard extension):
40 - do not pad the field
41 _ pad the field with spaces
42
43 Time fields:
44 %H hour (00..23)
45 %I hour (01..12)
46 %k hour ( 0..23)
47 %l hour ( 1..12)
48 %M minute (00..59)
49 %p locale's AM or PM
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)
53 %S second (00..61)
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
57
58 Date fields:
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)
64 %C century (00..99)
65 %d day of month (01..31)
66 %e day of month ( 1..31)
67 %D date (mm/dd/yy)
68 %h same as %b
69 %j day of year (001..366)
70 %m month (01..12)
71 %U week number of year with Sunday as first day of week (00..53)
72 %w day of week (0..6)
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)
76 %Y year (1970...)
77
78 David MacKenzie <djm@gnu.ai.mit.edu> */
79
80 #ifdef HAVE_CONFIG_H
81 #include <config.h>
82 #include "lisp.h"
83 #endif
84
85 #include <stdio.h>
86 #include <sys/types.h>
87 #if defined(TM_IN_SYS_TIME) || (!defined(HAVE_TM_ZONE) && !defined(HAVE_TZNAME))
88 #include <sys/time.h>
89 #else
90 #include <time.h>
91 #endif
92
93 #ifndef STDC_HEADERS
94 time_t mktime ();
95 #endif
96
97 #if defined(HAVE_TZNAME)
98 extern char *tzname[2];
99 #endif
100
101 #ifdef emacs
102 #define strftime emacs_strftime
103 #endif
104
105 /* Types of padding for numbers in date and time. */
106 enum padding
107 {
108 none, blank, zero
109 };
110
111 static char CONST* CONST days[] =
112 {
113 "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
114 };
115
116 static char CONST * CONST months[] =
117 {
118 "January", "February", "March", "April", "May", "June",
119 "July", "August", "September", "October", "November", "December"
120 };
121
122 /* Add character C to STRING and increment LENGTH,
123 unless LENGTH would exceed MAX. */
124
125 #define add_char(c) do \
126 { \
127 if (length + 1 <= max) \
128 string[length++] = (c); \
129 } while (0)
130
131 /* Add a 2 digit number to STRING, padding if specified.
132 Return the number of characters added, up to MAX. */
133
134 static int
135 add_num2 (char *string, int num, int max, enum padding pad)
136 {
137 int top = num / 10;
138 int length = 0;
139
140 if (top == 0 && pad == blank)
141 add_char (' ');
142 else if (top != 0 || pad == zero)
143 add_char (top + '0');
144 add_char (num % 10 + '0');
145 return length;
146 }
147
148 /* Add a 3 digit number to STRING, padding if specified.
149 Return the number of characters added, up to MAX. */
150
151 static int
152 add_num3 (char *string, int num, int max, enum padding pad)
153 {
154 int top = num / 100;
155 int mid = (num - top * 100) / 10;
156 int length = 0;
157
158 if (top == 0 && pad == blank)
159 add_char (' ');
160 else if (top != 0 || pad == zero)
161 add_char (top + '0');
162 if (mid == 0 && top == 0 && pad == blank)
163 add_char (' ');
164 else if (mid != 0 || top != 0 || pad == zero)
165 add_char (mid + '0');
166 add_char (num % 10 + '0');
167 return length;
168 }
169
170 /* Like strncpy except return the number of characters copied. */
171
172 static int
173 add_str (char *to, CONST char *from, int max)
174 {
175 int i;
176
177 for (i = 0; from[i] && i <= max; ++i)
178 to[i] = from[i];
179 return i;
180 }
181
182 static int
183 add_num_time_t (char *string, int max, time_t num)
184 {
185 /* This buffer is large enough to hold the character representation
186 (including the trailing NUL) of any unsigned decimal quantity
187 whose binary representation fits in 128 bits. */
188 char buf[40];
189 int length;
190
191 if (sizeof (num) > 16)
192 abort ();
193 sprintf (buf, "%lu", (unsigned long) num);
194 length = add_str (string, buf, max);
195 return length;
196 }
197
198 /* Return the week in the year of the time in TM, with the weeks
199 starting on Sundays. */
200
201 static int
202 sun_week (CONST struct tm *tm)
203 {
204 int dl;
205
206 /* Set `dl' to the day in the year of the last day of the week previous
207 to the one containing the day specified in TM. If the day specified
208 in TM is in the first week of the year, `dl' will be negative or 0.
209 Otherwise, calculate the number of complete weeks before our week
210 (dl / 7) and add any partial week at the start of the year (dl % 7). */
211 dl = tm->tm_yday - tm->tm_wday;
212 return dl <= 0 ? 0 : dl / 7 + (dl % 7 != 0);
213 }
214
215 /* Return the week in the year of the time in TM, with the weeks
216 starting on Mondays. */
217
218 static int
219 mon_week (CONST struct tm *tm)
220 {
221 int dl, wday;
222
223 if (tm->tm_wday == 0)
224 wday = 6;
225 else
226 wday = tm->tm_wday - 1;
227 dl = tm->tm_yday - wday;
228 return dl <= 0 ? 0 : dl / 7 + (dl % 7 != 0);
229 }
230
231 #if !defined(HAVE_TM_ZONE) && !defined(HAVE_TZNAME)
232 char *
233 zone_name (struct tm *tp)
234 {
235 char *timezone ();
236 struct timeval tv;
237 struct timezone tz;
238
239 gettimeofday (&tv, &tz);
240 return timezone (tz.tz_minuteswest, tp->tm_isdst);
241 }
242 #endif
243
244 /* Format the time given in TM according to FORMAT, and put the
245 results in STRING.
246 Return the number of characters (not including terminating null)
247 that were put into STRING, or 0 if the length would have
248 exceeded MAX. */
249
250 size_t strftime (char *string, size_t max, CONST char *format,
251 CONST struct tm *tm);
252
253 size_t
254 strftime (char *string, size_t max, CONST char *format, CONST struct tm *tm)
255 {
256 enum padding pad; /* Type of padding to apply. */
257 size_t length = 0; /* Characters put in STRING so far. */
258
259 for (; *format && length < max; ++format)
260 {
261 if (*format != '%')
262 add_char (*format);
263 else
264 {
265 ++format;
266 /* Modifiers: */
267 if (*format == '-')
268 {
269 pad = none;
270 ++format;
271 }
272 else if (*format == '_')
273 {
274 pad = blank;
275 ++format;
276 }
277 else
278 pad = zero;
279
280 switch (*format)
281 {
282 /* Literal character fields: */
283 case 0:
284 case '%':
285 add_char ('%');
286 break;
287 case 'n':
288 add_char ('\n');
289 break;
290 case 't':
291 add_char ('\t');
292 break;
293 default:
294 add_char (*format);
295 break;
296
297 /* Time fields: */
298 case 'H':
299 case 'k':
300 length +=
301 add_num2 (&string[length], tm->tm_hour, max - length,
302 *format == 'H' ? pad : blank);
303 break;
304 case 'I':
305 case 'l':
306 {
307 int hour12;
308
309 if (tm->tm_hour == 0)
310 hour12 = 12;
311 else if (tm->tm_hour > 12)
312 hour12 = tm->tm_hour - 12;
313 else
314 hour12 = tm->tm_hour;
315 length +=
316 add_num2 (&string[length], hour12, max - length,
317 *format == 'I' ? pad : blank);
318 }
319 break;
320 case 'M':
321 length +=
322 add_num2 (&string[length], tm->tm_min, max - length, pad);
323 break;
324 case 'p':
325 if (tm->tm_hour < 12)
326 add_char ('A');
327 else
328 add_char ('P');
329 add_char ('M');
330 break;
331 case 'r':
332 length +=
333 strftime (&string[length], max - length, "%I:%M:%S %p", tm);
334 break;
335 case 'R':
336 length +=
337 strftime (&string[length], max - length, "%H:%M", tm);
338 break;
339
340 case 's':
341 {
342 struct tm writable_tm;
343 writable_tm = *tm;
344 length += add_num_time_t (&string[length], max - length,
345 mktime (&writable_tm));
346 }
347 break;
348
349 case 'S':
350 length +=
351 add_num2 (&string[length], tm->tm_sec, max - length, pad);
352 break;
353 case 'T':
354 length +=
355 strftime (&string[length], max - length, "%H:%M:%S", tm);
356 break;
357 case 'X':
358 length +=
359 strftime (&string[length], max - length, "%H:%M:%S", tm);
360 break;
361 case 'Z':
362 #ifdef HAVE_TM_ZONE
363 length += add_str (&string[length], tm->tm_zone, max - length);
364 #else
365 #ifdef HAVE_TZNAME
366 if (tm->tm_isdst && tzname[1] && *tzname[1])
367 length += add_str (&string[length], tzname[1], max - length);
368 else
369 length += add_str (&string[length], tzname[0], max - length);
370 #else
371 length += add_str (&string[length], zone_name (tm), max - length);
372 #endif
373 #endif
374 break;
375
376 /* Date fields: */
377 case 'a':
378 add_char (days[tm->tm_wday][0]);
379 add_char (days[tm->tm_wday][1]);
380 add_char (days[tm->tm_wday][2]);
381 break;
382 case 'A':
383 length +=
384 add_str (&string[length], days[tm->tm_wday], max - length);
385 break;
386 case 'b':
387 case 'h':
388 add_char (months[tm->tm_mon][0]);
389 add_char (months[tm->tm_mon][1]);
390 add_char (months[tm->tm_mon][2]);
391 break;
392 case 'B':
393 length +=
394 add_str (&string[length], months[tm->tm_mon], max - length);
395 break;
396 case 'c':
397 length +=
398 strftime (&string[length], max - length,
399 "%a %b %d %H:%M:%S %Z %Y", tm);
400 break;
401 case 'C':
402 length +=
403 add_num2 (&string[length], (tm->tm_year + 1900) / 100,
404 max - length, pad);
405 break;
406 case 'd':
407 length +=
408 add_num2 (&string[length], tm->tm_mday, max - length, pad);
409 break;
410 case 'e':
411 length +=
412 add_num2 (&string[length], tm->tm_mday, max - length, blank);
413 break;
414 case 'D':
415 length +=
416 strftime (&string[length], max - length, "%m/%d/%y", tm);
417 break;
418 case 'j':
419 length +=
420 add_num3 (&string[length], tm->tm_yday + 1, max - length, pad);
421 break;
422 case 'm':
423 length +=
424 add_num2 (&string[length], tm->tm_mon + 1, max - length, pad);
425 break;
426 case 'U':
427 length +=
428 add_num2 (&string[length], sun_week (tm), max - length, pad);
429 break;
430 case 'w':
431 add_char (tm->tm_wday + '0');
432 break;
433 case 'W':
434 length +=
435 add_num2 (&string[length], mon_week (tm), max - length, pad);
436 break;
437 case 'x':
438 length +=
439 strftime (&string[length], max - length, "%m/%d/%y", tm);
440 break;
441 case 'y':
442 length +=
443 add_num2 (&string[length], tm->tm_year % 100,
444 max - length, pad);
445 break;
446 case 'Y':
447 add_char ((tm->tm_year + 1900) / 1000 + '0');
448 length +=
449 add_num3 (&string[length],
450 (1900 + tm->tm_year) % 1000, max - length, zero);
451 break;
452 }
453 }
454 }
455 add_char (0);
456 return length - 1;
457 }