4 * Public-domain relatively quick-and-dirty implemenation of
5 * ANSI library routine for System V Unix systems.
7 * It's written in old-style C for maximal portability.
8 * However, since I'm used to prototypes, I've included them too.
10 * If you want stuff in the System V ascftime routine, add the SYSV_EXT define.
11 * For stuff needed to implement the P1003.2 date command, add POSIX2_DATE.
12 * For complete POSIX semantics, add POSIX_SEMANTICS.
14 * The code for %c, %x, and %X is my best guess as to what's "appropriate".
15 * This version ignores LOCALE information.
16 * It also doesn't worry about multi-byte characters.
20 * January, February, March, 1991
23 * Fixes from ado@elsie.nci.nih.gov
26 * Fixed compiler warnings from GCC
27 * Olly Betts 2005-01-12
29 * Fix memory leak if realloc() fails
30 * Olly Betts 2010-12-21
32 * Fix compilation error with GCC
33 * Olly Betts 2010-12-21
40 #include <sys/types.h>
45 extern void *malloc();
46 extern void *realloc();
49 extern char *strchr();
50 extern char *getenv();
51 static int weeknumber();
54 extern void tzset(void);
55 static int weeknumber(const struct tm
*timeptr
, int firstweekday
);
59 #define inline __inline__
64 #define range(low, item, hi) max(low, min(item, hi))
66 extern char *tzname
[2];
69 #define SYSV_EXT 1 /* stuff in System V ascftime routine */
70 #define POSIX2_DATE 1 /* stuff in Posix 1003.2 date command */
71 #define VMS_EXT 1 /* include %V for VMS date format */
72 #define POSIX_SEMANTICS 1 /* call tzset() if TZ changes */
74 #if defined(POSIX2_DATE) && ! defined(SYSV_EXT)
78 /* min --- return minimum of two numbers */
89 return (a
< b
? a
: b
);
92 /* max --- return maximum of two numbers */
103 return (a
> b
? a
: b
);
106 /* strftime --- produce formatted time */
110 strftime(s
, maxsize
, format
, timeptr
)
114 const struct tm
*timeptr
;
117 strftime(char *s
, size_t maxsize
, const char *format
, const struct tm
*timeptr
)
120 char *endp
= s
+ maxsize
;
124 static short first
= 1;
125 static char *savetz
= NULL
;
126 static int savetzlen
= 0;
130 /* various tables, useful in North America */
131 static const char *days_a
[] = {
132 "Sun", "Mon", "Tue", "Wed",
135 static const char *days_l
[] = {
136 "Sunday", "Monday", "Tuesday", "Wednesday",
137 "Thursday", "Friday", "Saturday",
139 static const char *months_a
[] = {
140 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
141 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
143 static const char *months_l
[] = {
144 "January", "February", "March", "April",
145 "May", "June", "July", "August", "September",
146 "October", "November", "December",
148 static const char *ampm
[] = { "AM", "PM", };
150 if (s
== NULL
|| format
== NULL
|| timeptr
== NULL
|| maxsize
== 0)
153 if (strchr(format
, '%') == NULL
&& strlen(format
) + 1 >= maxsize
)
156 #ifndef POSIX_SEMANTICS
161 #else /* POSIX_SEMANTICS */
166 savetz
= (char *) malloc(tzlen
+ 1);
167 if (savetz
!= NULL
) {
168 savetzlen
= tzlen
+ 1;
175 /* if we have a saved TZ, and it is different, recapture and reset */
176 if (tz
&& savetz
&& (tz
[0] != savetz
[0] || strcmp(tz
, savetz
) != 0)) {
179 char * newsavetz
= (char *) realloc(savetz
, i
);
182 strcpy(newsavetz
, tz
);
191 #endif /* POSIX_SEMANTICS */
193 for (; *format
&& s
< endp
- 1; format
++) {
195 if (*format
!= '%') {
209 case 'a': /* abbreviated weekday name */
210 if (timeptr
->tm_wday
< 0 || timeptr
->tm_wday
> 6)
213 strcpy(tbuf
, days_a
[timeptr
->tm_wday
]);
216 case 'A': /* full weekday name */
217 if (timeptr
->tm_wday
< 0 || timeptr
->tm_wday
> 6)
220 strcpy(tbuf
, days_l
[timeptr
->tm_wday
]);
224 case 'h': /* abbreviated month name */
226 case 'b': /* abbreviated month name */
227 if (timeptr
->tm_mon
< 0 || timeptr
->tm_mon
> 11)
230 strcpy(tbuf
, months_a
[timeptr
->tm_mon
]);
233 case 'B': /* full month name */
234 if (timeptr
->tm_mon
< 0 || timeptr
->tm_mon
> 11)
237 strcpy(tbuf
, months_l
[timeptr
->tm_mon
]);
240 case 'c': /* appropriate date and time representation */
241 sprintf(tbuf
, "%s %s %2d %02d:%02d:%02d %d",
242 days_a
[range(0, timeptr
->tm_wday
, 6)],
243 months_a
[range(0, timeptr
->tm_mon
, 11)],
244 range(1, timeptr
->tm_mday
, 31),
245 range(0, timeptr
->tm_hour
, 23),
246 range(0, timeptr
->tm_min
, 59),
247 range(0, timeptr
->tm_sec
, 61),
248 timeptr
->tm_year
+ 1900);
251 case 'd': /* day of the month, 01 - 31 */
252 i
= range(1, timeptr
->tm_mday
, 31);
253 sprintf(tbuf
, "%02d", i
);
256 case 'H': /* hour, 24-hour clock, 00 - 23 */
257 i
= range(0, timeptr
->tm_hour
, 23);
258 sprintf(tbuf
, "%02d", i
);
261 case 'I': /* hour, 12-hour clock, 01 - 12 */
262 i
= range(0, timeptr
->tm_hour
, 23);
267 sprintf(tbuf
, "%02d", i
);
270 case 'j': /* day of the year, 001 - 366 */
271 sprintf(tbuf
, "%03d", timeptr
->tm_yday
+ 1);
274 case 'm': /* month, 01 - 12 */
275 i
= range(0, timeptr
->tm_mon
, 11);
276 sprintf(tbuf
, "%02d", i
+ 1);
279 case 'M': /* minute, 00 - 59 */
280 i
= range(0, timeptr
->tm_min
, 59);
281 sprintf(tbuf
, "%02d", i
);
284 case 'p': /* am or pm based on 12-hour clock */
285 i
= range(0, timeptr
->tm_hour
, 23);
287 strcpy(tbuf
, ampm
[0]);
289 strcpy(tbuf
, ampm
[1]);
292 case 'S': /* second, 00 - 61 */
293 i
= range(0, timeptr
->tm_sec
, 61);
294 sprintf(tbuf
, "%02d", i
);
297 case 'U': /* week of year, Sunday is first day of week */
298 sprintf(tbuf
, "%d", weeknumber(timeptr
, 0));
301 case 'w': /* weekday, Sunday == 0, 0 - 6 */
302 i
= range(0, timeptr
->tm_wday
, 6);
303 sprintf(tbuf
, "%d", i
);
306 case 'W': /* week of year, Monday is first day of week */
307 sprintf(tbuf
, "%d", weeknumber(timeptr
, 1));
310 case 'x': /* appropriate date representation */
311 sprintf(tbuf
, "%s %s %2d %d",
312 days_a
[range(0, timeptr
->tm_wday
, 6)],
313 months_a
[range(0, timeptr
->tm_mon
, 11)],
314 range(1, timeptr
->tm_mday
, 31),
315 timeptr
->tm_year
+ 1900);
318 case 'X': /* appropriate time representation */
319 sprintf(tbuf
, "%02d:%02d:%02d",
320 range(0, timeptr
->tm_hour
, 23),
321 range(0, timeptr
->tm_min
, 59),
322 range(0, timeptr
->tm_sec
, 61));
325 case 'y': /* year without a century, 00 - 99 */
326 i
= timeptr
->tm_year
% 100;
327 sprintf(tbuf
, "%d", i
);
330 case 'Y': /* year with century */
331 sprintf(tbuf
, "%d", 1900 + timeptr
->tm_year
);
334 case 'Z': /* time zone name or abbrevation */
336 if (daylight
&& timeptr
->tm_isdst
)
338 strcpy(tbuf
, tzname
[i
]);
342 case 'n': /* same as \n */
347 case 't': /* same as \t */
352 case 'D': /* date as %m/%d/%y */
353 strftime(tbuf
, sizeof tbuf
, "%m/%d/%y", timeptr
);
356 case 'e': /* day of month, blank padded */
357 sprintf(tbuf
, "%2d", range(1, timeptr
->tm_mday
, 31));
360 case 'r': /* time as %I:%M:%S %p */
361 strftime(tbuf
, sizeof tbuf
, "%I:%M:%S %p", timeptr
);
364 case 'R': /* time as %H:%M */
365 strftime(tbuf
, sizeof tbuf
, "%H:%M", timeptr
);
368 case 'T': /* time as %H:%M:%S */
369 strftime(tbuf
, sizeof tbuf
, "%H:%M:%S", timeptr
);
375 case 'V': /* date as dd-bbb-YYYY */
376 sprintf(tbuf
, "%2d-%3.3s-%4d",
377 range(1, timeptr
->tm_mday
, 31),
378 months_a
[range(0, timeptr
->tm_mon
, 11)],
379 timeptr
->tm_year
+ 1900);
380 for (i
= 3; i
< 6; i
++)
381 if (islower(tbuf
[i
]))
382 tbuf
[i
] = toupper(tbuf
[i
]);
389 sprintf(tbuf
, "%02d", (timeptr
->tm_year
+ 1900) / 100);
395 /* POSIX locale extensions, ignored for now */
406 if (s
+ i
< endp
- 1) {
414 if (s
< endp
&& *format
== '\0') {
421 /* weeknumber --- figure how many weeks into the year */
423 /* With thanks and tip of the hatlo to ado@elsie.nci.nih.gov */
424 /* Modified byMichal Jaegermann <audfax!emory!vm.ucs.UAlberta.CA!NTOMCZAK> */
428 weeknumber(timeptr
, firstweekday
)
429 const struct tm
*timeptr
;
433 weeknumber(const struct tm
*timeptr
, int firstweekday
)
436 * firstweekday is 0 if starting in Sunday, non-zero if in Monday
439 return (timeptr
->tm_yday
- timeptr
->tm_wday
+
440 (firstweekday
? (timeptr
->tm_wday
? 8 : 1) : 7)) / 7;