Mercurial > hg > xemacs-beta
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 } |