comparison src/strftime.c @ 428:3ecd8885ac67 r21-2-22

Import from CVS: tag r21-2-22
author cvs
date Mon, 13 Aug 2007 11:28:15 +0200
parents
children abe6d1db359e
comparison
equal deleted inserted replaced
427:0a0253eac470 428:3ecd8885ac67
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(WINDOWSNT) || defined(__CYGWIN32__)
98 #include <time.h>
99 #else
100 #if defined(HAVE_TZNAME)
101 extern char *tzname[2];
102 #endif
103 #endif /* WINDOWSNT */
104
105 #ifdef emacs
106 #define strftime emacs_strftime
107 #endif
108
109 /* Types of padding for numbers in date and time. */
110 enum padding
111 {
112 none, blank, zero
113 };
114
115 static char CONST* CONST days[] =
116 {
117 "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
118 };
119
120 static char CONST * CONST months[] =
121 {
122 "January", "February", "March", "April", "May", "June",
123 "July", "August", "September", "October", "November", "December"
124 };
125
126 /* Add character C to STRING and increment LENGTH,
127 unless LENGTH would exceed MAX. */
128
129 #define add_char(c) do \
130 { \
131 if (length + 1 <= max) \
132 string[length++] = (c); \
133 } while (0)
134
135 /* Add a 2 digit number to STRING, padding if specified.
136 Return the number of characters added, up to MAX. */
137
138 static int
139 add_num2 (char *string, int num, int max, enum padding pad)
140 {
141 int top = num / 10;
142 int length = 0;
143
144 if (top == 0 && pad == blank)
145 add_char (' ');
146 else if (top != 0 || pad == zero)
147 add_char (top + '0');
148 add_char (num % 10 + '0');
149 return length;
150 }
151
152 /* Add a 3 digit number to STRING, padding if specified.
153 Return the number of characters added, up to MAX. */
154
155 static int
156 add_num3 (char *string, int num, int max, enum padding pad)
157 {
158 int top = num / 100;
159 int mid = (num - top * 100) / 10;
160 int length = 0;
161
162 if (top == 0 && pad == blank)
163 add_char (' ');
164 else if (top != 0 || pad == zero)
165 add_char (top + '0');
166 if (mid == 0 && top == 0 && pad == blank)
167 add_char (' ');
168 else if (mid != 0 || top != 0 || pad == zero)
169 add_char (mid + '0');
170 add_char (num % 10 + '0');
171 return length;
172 }
173
174 /* Like strncpy except return the number of characters copied. */
175
176 static int
177 add_str (char *to, CONST char *from, int max)
178 {
179 int i;
180
181 for (i = 0; from[i] && i <= max; ++i)
182 to[i] = from[i];
183 return i;
184 }
185
186 static int
187 add_num_time_t (char *string, int max, time_t num)
188 {
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. */
192 char buf[40];
193 int length;
194
195 if (sizeof (num) > 16)
196 abort ();
197 sprintf (buf, "%lu", (unsigned long) num);
198 length = add_str (string, buf, max);
199 return length;
200 }
201
202 /* Return the week in the year of the time in TM, with the weeks
203 starting on Sundays. */
204
205 static int
206 sun_week (CONST struct tm *tm)
207 {
208 int dl;
209
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);
217 }
218
219 /* Return the week in the year of the time in TM, with the weeks
220 starting on Mondays. */
221
222 static int
223 mon_week (CONST struct tm *tm)
224 {
225 int dl, wday;
226
227 if (tm->tm_wday == 0)
228 wday = 6;
229 else
230 wday = tm->tm_wday - 1;
231 dl = tm->tm_yday - wday;
232 return dl <= 0 ? 0 : dl / 7 + (dl % 7 != 0);
233 }
234
235 #if !defined(HAVE_TM_ZONE) && !defined(HAVE_TZNAME)
236 char *
237 zone_name (CONST struct tm *tp)
238 {
239 char *timezone ();
240 struct timeval tv;
241 struct timezone tz;
242
243 gettimeofday (&tv, &tz);
244 return timezone (tz.tz_minuteswest, tp->tm_isdst);
245 }
246 #endif
247
248 /* Format the time given in TM according to FORMAT, and put the
249 results in STRING.
250 Return the number of characters (not including terminating null)
251 that were put into STRING, or 0 if the length would have
252 exceeded MAX. */
253
254 size_t strftime (char *string, size_t max, CONST char *format,
255 CONST struct tm *tm);
256
257 size_t
258 strftime (char *string, size_t max, CONST char *format, CONST struct tm *tm)
259 {
260 enum padding pad; /* Type of padding to apply. */
261 size_t length = 0; /* Characters put in STRING so far. */
262
263 for (; *format && length < max; ++format)
264 {
265 if (*format != '%')
266 add_char (*format);
267 else
268 {
269 ++format;
270 /* Modifiers: */
271 if (*format == '-')
272 {
273 pad = none;
274 ++format;
275 }
276 else if (*format == '_')
277 {
278 pad = blank;
279 ++format;
280 }
281 else
282 pad = zero;
283
284 switch (*format)
285 {
286 /* Literal character fields: */
287 case 0:
288 case '%':
289 add_char ('%');
290 break;
291 case 'n':
292 add_char ('\n');
293 break;
294 case 't':
295 add_char ('\t');
296 break;
297 default:
298 add_char (*format);
299 break;
300
301 /* Time fields: */
302 case 'H':
303 case 'k':
304 length +=
305 add_num2 (&string[length], tm->tm_hour, max - length,
306 *format == 'H' ? pad : blank);
307 break;
308 case 'I':
309 case 'l':
310 {
311 int hour12;
312
313 if (tm->tm_hour == 0)
314 hour12 = 12;
315 else if (tm->tm_hour > 12)
316 hour12 = tm->tm_hour - 12;
317 else
318 hour12 = tm->tm_hour;
319 length +=
320 add_num2 (&string[length], hour12, max - length,
321 *format == 'I' ? pad : blank);
322 }
323 break;
324 case 'M':
325 length +=
326 add_num2 (&string[length], tm->tm_min, max - length, pad);
327 break;
328 case 'p':
329 if (tm->tm_hour < 12)
330 add_char ('A');
331 else
332 add_char ('P');
333 add_char ('M');
334 break;
335 case 'r':
336 length +=
337 strftime (&string[length], max - length, "%I:%M:%S %p", tm);
338 break;
339 case 'R':
340 length +=
341 strftime (&string[length], max - length, "%H:%M", tm);
342 break;
343
344 case 's':
345 {
346 struct tm writable_tm;
347 writable_tm = *tm;
348 length += add_num_time_t (&string[length], max - length,
349 mktime (&writable_tm));
350 }
351 break;
352
353 case 'S':
354 length +=
355 add_num2 (&string[length], tm->tm_sec, max - length, pad);
356 break;
357 case 'T':
358 length +=
359 strftime (&string[length], max - length, "%H:%M:%S", tm);
360 break;
361 case 'X':
362 length +=
363 strftime (&string[length], max - length, "%H:%M:%S", tm);
364 break;
365 case 'Z':
366 #ifdef HAVE_TM_ZONE
367 length += add_str (&string[length], tm->tm_zone, max - length);
368 #else
369 #ifdef HAVE_TZNAME
370 if (tm->tm_isdst && tzname[1] && *tzname[1])
371 length += add_str (&string[length], tzname[1], max - length);
372 else
373 length += add_str (&string[length], tzname[0], max - length);
374 #else
375 length += add_str (&string[length], zone_name (tm), max - length);
376 #endif
377 #endif
378 break;
379
380 /* Date fields: */
381 case 'a':
382 add_char (days[tm->tm_wday][0]);
383 add_char (days[tm->tm_wday][1]);
384 add_char (days[tm->tm_wday][2]);
385 break;
386 case 'A':
387 length +=
388 add_str (&string[length], days[tm->tm_wday], max - length);
389 break;
390 case 'b':
391 case 'h':
392 add_char (months[tm->tm_mon][0]);
393 add_char (months[tm->tm_mon][1]);
394 add_char (months[tm->tm_mon][2]);
395 break;
396 case 'B':
397 length +=
398 add_str (&string[length], months[tm->tm_mon], max - length);
399 break;
400 case 'c':
401 length +=
402 strftime (&string[length], max - length,
403 "%a %b %d %H:%M:%S %Z %Y", tm);
404 break;
405 case 'C':
406 length +=
407 add_num2 (&string[length], (tm->tm_year + 1900) / 100,
408 max - length, pad);
409 break;
410 case 'd':
411 length +=
412 add_num2 (&string[length], tm->tm_mday, max - length, pad);
413 break;
414 case 'e':
415 length +=
416 add_num2 (&string[length], tm->tm_mday, max - length, blank);
417 break;
418 case 'D':
419 length +=
420 strftime (&string[length], max - length, "%m/%d/%y", tm);
421 break;
422 case 'j':
423 length +=
424 add_num3 (&string[length], tm->tm_yday + 1, max - length, pad);
425 break;
426 case 'm':
427 length +=
428 add_num2 (&string[length], tm->tm_mon + 1, max - length, pad);
429 break;
430 case 'U':
431 length +=
432 add_num2 (&string[length], sun_week (tm), max - length, pad);
433 break;
434 case 'w':
435 add_char (tm->tm_wday + '0');
436 break;
437 case 'W':
438 length +=
439 add_num2 (&string[length], mon_week (tm), max - length, pad);
440 break;
441 case 'x':
442 length +=
443 strftime (&string[length], max - length, "%m/%d/%y", tm);
444 break;
445 case 'y':
446 length +=
447 add_num2 (&string[length], tm->tm_year % 100,
448 max - length, pad);
449 break;
450 case 'Y':
451 add_char ((tm->tm_year + 1900) / 1000 + '0');
452 length +=
453 add_num3 (&string[length],
454 (1900 + tm->tm_year) % 1000, max - length, zero);
455 break;
456 }
457 }
458 }
459 add_char (0);
460 return length - 1;
461 }