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