arnold@audiofax.com (Arnold Robbins) (01/26/91)
The other day I wanted to use the ANSI C routine strftime. It's not in my C library, unfortunately. So I cobbled one up. Here it is. Please try it out, and let me know if you find any bugs. If there are none reported in the next week or so, I'll ship it out over one of the moderated source groups. As this is alt.sources, there is no man page or makefile. This compiles fine with both gcc and cc on my ESIX V.3.2 rev D system. It does assume System V for the timezone information. ---------------- cut here --------------------- /* * strftime.c * * Public-domain relatively quick-and-dirty implemenation of * ANSI library routine for System V Unix systems. * * It's written in old-style C for maximal portability. * * The code for %c, %x, and %X is my best guess as to what's "appropriate". * This version ignores LOCALE information. * It also doesn't worry about multi-byte characters. * So there. * * Arnold Robbins * January, 1991 */ #include <stdio.h> #include <string.h> #include <time.h> #include <sys/types.h> #ifndef NULL #define NULL 0 /* the real programmer's definition */ #endif #ifndef __STDC__ #define const /**/ #endif #ifndef __STDC__ extern void tzset(); extern int abs(); static int weeknumber(); #else extern void tzset(void); extern int abs(int val); static int weeknumber(const struct tm *timeptr, int firstweekday); #endif extern char *tzname[2]; extern int daylight; #define L_DAYA 3 /* length of an abbreviated day name */ #define L_MONA 3 /* length of an abbreviated month name */ /* strftime --- produce formatted time */ #ifndef __STDC__ size_t strftime(s, maxsize, format, timeptr) char *s; size_t maxsize; const char *format; const struct tm *timeptr; #else size_t strftime(char *s, size_t maxsize, const char *format, const struct tm *timeptr) #endif { char *endp = s + maxsize; char *start = s; char tbuf[100]; int i; static short first = 1; /* various tables, useful in North America */ static char *days_a[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", }; static char *days_l[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", }; static char *months_a[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", }; static char *months_l[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", }; if (s == NULL || format == NULL || timeptr == NULL || maxsize <= 0) return 0; if (first) { first = 0; tzset(); } for (; *format && s < endp - 1; format++) { tbuf[0] = '\0'; if (*format != '%') { *s++ = *format; continue; } switch (*++format) { case '\0': break; case '%': *s++ = '%'; continue; case 'a': /* abbreviated weekday name */ if (s + L_DAYA < endp - 1) { *s++ = days_a[timeptr->tm_wday][0]; *s++ = days_a[timeptr->tm_wday][1]; *s++ = days_a[timeptr->tm_wday][2]; } else goto out; break; case 'A': /* full weekday name */ i = strlen(days_l[timeptr->tm_wday]); if (s + i < endp - 1) { strcpy(s, days_l[timeptr->tm_wday]); s += i; } else goto out; break; case 'b': /* abbreviated month name */ if (s + L_MONA < endp - 1) { *s++ = months_a[timeptr->tm_mon][0]; *s++ = months_a[timeptr->tm_mon][1]; *s++ = months_a[timeptr->tm_mon][2]; } else goto out; break; case 'B': /* full month name */ i = strlen(months_l[timeptr->tm_mon]); if (s + i < endp - 1) { strcpy(s, months_l[timeptr->tm_mon]); s += i; } else goto out; break; case 'c': /* appropriate date and time representation */ sprintf(tbuf, "%s %s %2d %02d:%02d:%02d %d", days_a[timeptr->tm_wday], months_a[timeptr->tm_mon], timeptr->tm_mday, timeptr->tm_hour, timeptr->tm_min, timeptr->tm_sec, timeptr->tm_year + 1900); break; case 'd': /* day of the month, 01 - 31 */ sprintf(tbuf, "%02d", timeptr->tm_mday); break; case 'H': /* hour, 24-hour clock, 00 - 23 */ sprintf(tbuf, "%02d", timeptr->tm_hour); break; case 'I': /* hour, 12-hour clock, 01 - 12 */ i = timeptr->tm_hour; if (i == 0) i = 12; else if (i > 12) i -= 12; sprintf(tbuf, "%02d", i); break; case 'j': /* day of the year, 001 - 366 */ sprintf(tbuf, "%03d", timeptr->tm_yday + 1); break; case 'm': /* month, 01 - 12 */ sprintf(tbuf, "%02d", timeptr->tm_mon + 1); break; case 'M': /* minute, 00 - 59 */ sprintf(tbuf, "%02d", timeptr->tm_min); break; case 'p': /* am or pm based on 12-hour clock */ if (timeptr->tm_hour < 12) strcpy(tbuf, "a.m."); else strcpy(tbuf, "p.m."); break; case 'S': /* second, 00 - 61 */ sprintf(tbuf, "%02d", timeptr->tm_sec); break; case 'U': /* week of year, Sunday is first day of week */ sprintf(tbuf, "%d", weeknumber(timeptr, 0)); break; case 'w': /* weekday, Sunday == 0, 0 - 6 */ sprintf(tbuf, "%d", timeptr->tm_wday); break; case 'W': /* week of year, Monday is first day of week */ sprintf(tbuf, "%d", weeknumber(timeptr, 1)); break; case 'x': /* appropriate date representation */ sprintf(tbuf, "%s %s %2d %d", days_a[timeptr->tm_wday], months_a[timeptr->tm_mon], timeptr->tm_mday, timeptr->tm_year + 1900); break; case 'X': /* appropriate time representation */ sprintf(tbuf, "%02d:%02d:%02d", timeptr->tm_hour, timeptr->tm_min, timeptr->tm_sec); break; case 'y': /* year without a century, 00 - 99 */ i = timeptr->tm_year % 100; sprintf(tbuf, "%d", i); break; case 'Y': /* year with century */ sprintf(tbuf, "%d", 1900 + timeptr->tm_year); break; case 'Z': /* time zone name or abbrevation */ i = 0; if (daylight && timeptr->tm_isdst) i = 1; strcpy(tbuf, tzname[i]); break; default: /* do something reasonable */ if (s + 2 < endp - 1) { *s++ = '%'; *s++ = *format; } else goto out; continue; } i = strlen(tbuf); if (i) if (s + i < endp - 1) { strcpy(s, tbuf); s += i; } else break; /* the for loop */ } out: if (s < endp) { *s = '\0'; return (s - start - 1); } else return 0; } /* weeknumber --- figure how many weeks into the year */ #ifndef __STDC__ static int weeknumber(timeptr, firstweekday) const struct tm *timeptr; int firstweekday; #else static int weeknumber(const struct tm *timeptr, int firstweekday) #endif { int m, drift, r; /* days into the current 7-day period */ m = timeptr->tm_yday % 7; /* special case the first week */ if (m == timeptr->tm_yday) if (timeptr->tm_wday >= firstweekday) return 1; else return 0; /* how many days away from Sunday or Monday the year started */ drift = timeptr->tm_wday - m + firstweekday; drift = abs(drift); r = (timeptr->tm_yday - drift) / 7; return r; } -- Arnold Robbins AudioFAX, Inc. | Laundry increases 2000 Powers Ferry Road, #200 / Marietta, GA. 30067 | exponentially in the INTERNET: arnold@audiofax.com Phone: +1 404 933 7612 | number of children. UUCP: emory!audfax!arnold Fax-box: +1 404 618 4581 | -- Miriam Robbins