arnold@audiofax.com (Arnold Robbins) (03/02/91)
Submitted-by: Arnold Robbins <arnold@audiofax.com> Posting-number: Volume 17, Issue 30 Archive-name: strftime/part01 Supersedes: strftime: Volume 16, Issue 94 Here is a new and improved version of the strftime routine and man page posted a few weeks ago. It's not worth posting diffs, the whole package is pretty small. Enjoy, 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 ------------------------------ cut here ------------------------------- #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create: # strftime.c # strftime.3 # This archive created: Fri Mar 1 11:49:00 1991 export PATH; PATH=/bin:/usr/bin:$PATH echo shar: "extracting 'strftime.c'" '(6976 characters)' if test -f 'strftime.c' then echo shar: "will not over-write existing file 'strftime.c'" else cat << \SHAR_EOF > 'strftime.c' /* * 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. * However, since I'm used to prototypes, I've included them too. * * If you want stuff in the System V ascftime routine, add the SYSV_EXT define. * * 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, February, 1991 * * Fixes from ado@elsie.nci.nih.gov * February 1991 */ #include <stdio.h> #include <string.h> #include <time.h> #include <sys/types.h> #ifndef __STDC__ #define const /**/ #endif #ifndef __STDC__ extern void tzset(); extern char *strchr(); static int weeknumber(); #else extern void tzset(void); extern char *strchr(const char *str, int ch); static int weeknumber(const struct tm *timeptr, int firstweekday); #endif extern char *tzname[2]; extern int daylight; #define SYSV_EXT 1 /* stuff in System V ascftime routine */ /* 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", }; static char *ampm[] = { "AM", "PM", }; if (s == NULL || format == NULL || timeptr == NULL || maxsize == 0) return 0; if (strchr(format, '%') == NULL && strlen(format) + 1 >= maxsize) return 0; if (first) { tzset(); first = 0; } for (; *format && s < endp - 1; format++) { tbuf[0] = '\0'; if (*format != '%') { *s++ = *format; continue; } switch (*++format) { case '\0': *s++ = '%'; goto out; case '%': *s++ = '%'; continue; case 'a': /* abbreviated weekday name */ if (timeptr->tm_wday < 0 || timeptr->tm_wday > 6) strcpy(tbuf, "?"); else strcpy(tbuf, days_a[timeptr->tm_wday]); break; case 'A': /* full weekday name */ if (timeptr->tm_wday < 0 || timeptr->tm_wday > 6) strcpy(tbuf, "?"); else strcpy(tbuf, days_l[timeptr->tm_wday]); break; #ifdef SYSV_EXT case 'h': /* abbreviated month name */ #endif case 'b': /* abbreviated month name */ if (timeptr->tm_mon < 0 || timeptr->tm_mon > 11) strcpy(tbuf, "?"); else strcpy(tbuf, months_a[timeptr->tm_mon]); break; case 'B': /* full month name */ if (timeptr->tm_mon < 0 || timeptr->tm_mon > 11) strcpy(tbuf, "?"); else strcpy(tbuf, months_l[timeptr->tm_mon]); 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, ampm[0]); else strcpy(tbuf, ampm[1]); 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; #ifdef SYSV_EXT case 'n': /* same as \n */ tbuf[0] = '\n'; tbuf[1] = '\0'; break; case 't': /* same as \t */ tbuf[0] = '\t'; tbuf[1] = '\0'; break; case 'D': /* date as %m/%d/%y */ strftime(tbuf, sizeof tbuf, "%m/%d/%y", timeptr); break; case 'e': /* day of month, blank padded */ sprintf(tbuf, "%2d", timeptr->tm_mday); break; case 'r': /* time as %I:%M:%S %p */ strftime(tbuf, sizeof tbuf, "%I:%M:%S %p", timeptr); break; case 'R': /* time as %H:%M */ strftime(tbuf, sizeof tbuf, "%H:%M", timeptr); break; case 'T': /* time as %H:%M:%S */ strftime(tbuf, sizeof tbuf, "%H:%M:%S", timeptr); break; #endif default: tbuf[0] = '%'; tbuf[1] = *format; tbuf[2] = '\0'; break; } i = strlen(tbuf); if (i) if (s + i < endp - 1) { strcpy(s, tbuf); s += i; } else return 0; } out: if (s < endp && *format == '\0') { *s = '\0'; return (s - start); } else return 0; } /* weeknumber --- figure how many weeks into the year */ /* With thanks and tip of the hatlo to ado@elsie.nci.nih.gov */ #ifndef __STDC__ static int weeknumber(timeptr, firstweekday) const struct tm *timeptr; int firstweekday; #else static int weeknumber(const struct tm *timeptr, int firstweekday) #endif { if (firstweekday == 0) return (timeptr->tm_yday + 7 - timeptr->tm_wday) / 7; else return (timeptr->tm_yday + 7 - (timeptr->tm_wday ? (timeptr->tm_wday - 1) : 6)) / 7; } SHAR_EOF fi echo shar: "extracting 'strftime.3'" '(5331 characters)' if test -f 'strftime.3' then echo shar: "will not over-write existing file 'strftime.3'" else cat << \SHAR_EOF > 'strftime.3' .TH STRFTIME 3 .SH NAME strftime \- generate formatted time information .SH SYNOPSIS .ft B .nf #include <sys/types.h> #include <time.h> .sp size_t strftime(char *s, size_t maxsize, const char *format, const struct tm *timeptr); .SH DESCRIPTION The following description is transcribed verbatim from the December 7, 1988 draft standard for ANSI C. This draft is essentially identical in technical content to the final version of the standard. .LP The .B strftime function places characters into the array pointed to by .B s as controlled by the string pointed to by .BR format . The format shall be a multibyte character sequence, beginning and ending in its initial shift state. The .B format string consists of zero or more conversion specifiers and ordinary multibyte characters. A conversion specifier consists of a .B % character followed by a character that determines the behavior of the conversion specifier. All ordinary multibyte characters (including the terminating null character) are copied unchanged into the array. If copying takes place between objects that overlap the behavior is undefined. No more than .B maxsize characters are placed into the array. Each conversion specifier is replaced by appropriate characters as described in the following list. The appropriate characters are determined by the .B LC_TIME category of the current locale and by the values contained in the structure pointed to by .BR timeptr . .TP .B %a is replaced by the locale's abbreviated weekday name. .TP .B %A is replaced by the locale's full weekday name. .TP .B %b is replaced by the locale's abbreviated month name. .TP .B %B is replaced by the locale's full month name. .TP .B %c is replaced by the locale's appropriate date and time representation. .TP .B %d is replaced by the day of the month as a decimal number .RB ( 01 - 31 ). .TP .B %H is replaced by the hour (24-hour clock) as a decimal number .RB ( 00 - 23 ). .TP .B %I is replaced by the hour (12-hour clock) as a decimal number .RB ( 01 - 12 ). .TP .B %j is replaced by the day of the year as a decimal number .RB ( 001 - 366 ). .TP .B %m is replaced by the month as a decimal number .RB ( 01 - 12 ). .TP .B %M is replaced by the minute as a decimal number .RB ( 00 - 59 ). .TP .B %p is replaced by the locale's equivalent of the AM/PM designations associated with a 12-hour clock. .TP .B %S is replaced by the second as a decimal number .RB ( 00 - 61 ). .TP .B %U is replaced by the week number of the year (the first Sunday as the first day of week 1) as a decimal number .RB ( 00 - 53 ). .TP .B %w is replaced by the weekday as a decimal number .RB [ "0 " (Sunday)- 6 ]. .TP .B %W is replaced by the week number of the year (the first Monday as the first day of week 1) as a decimal number .RB ( 00 - 53 ). .TP .B %x is replaced by the locale's appropriate date representation. .TP .B %X is replaced by the locale's appropriate time representation. .TP .B %y is replaced by the year without century as a decimal number .RB ( 00 - 99 ). .TP .B %Y is replaced by the year with century as a decimal number. .TP .B %Z is replaced by the time zone name or abbreviation, or by no characters if no time zone is determinable. .TP .B %% is replaced by .BR % . .LP If a conversion specifier is not one of the above, the behavior is undefined. .SH RETURNS If the total number of resulting characters including the terminating null character is not more than .BR maxsize , the .B strftime function returns the number of characters placed into the array pointed to by .B s not including the terminating null character. Otherwise, zero is returned and the contents of the array are indeterminate. .SH NON-ANSI EXTENSIONS If .B SYSV_EXT is defined when the routine is compiled, then the following additional conversions will be available. These are borrowed from the System V .IR cftime (3) and .IR ascftime (3) routines. .TP .B %D is equivalent to specifying .BR %m/%d/%y . .TP .B %e is replaced by the day of the month, padded with a blank if it is only one digit. .TP .B %h is equivalent to .BR %b , above. .TP .B %n is replaced with a newline character (\s-1ASCII LF\s+1). .TP .B %r is equivalent to specifying .BR "%I:%M:%S %p" . .TP .B %R is equivalent to specifying .BR %H:%M: . .TP .B %T is equivalent to specifying .BR %H:%M:%S . .TP .B %t is replaced with a \s-1TAB\s+1 character. .SH SEE ALSO time(2), ctime(3), localtime(3) .SH BUGS This version does not handle multibyte characters or pay attention to the setting of the .B LC_TIME environment variable. .LP It is not clear what is ``appropriate'' for the C locale; the values returned are a best guess on the author's part. .SH CAVEATS This implementation calls .IR tzset (3) exactly once. If the .B TZ environment variable is changed after .B strftime has been called, then .IR tzset (3) must be called again, explicitly, in order for the correct timezone information to be available. .SH AUTHOR .nf Arnold Robbins AudioFAX, Inc. Suite 200 2000 Powers Ferry Road Marietta, GA. 30067 U.S.A. INTERNET: arnold@audiofax.com UUCP: emory!audfax!arnold Phone: +1 404 933 7600 Fax-box: +1 404 618 4581 .fi .SH ACKNOWLEDGEMENTS Thanks to Geoff Clare <gwc@root.co.uk> for helping debug earlier versions of this routine. Additional thanks to Arthur David Olsen <ado@elsie.nci.nih.gov> for some code improvements. SHAR_EOF fi exit 0 # End of shell archive -- 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 exit 0 # Just in case... -- Kent Landfield INTERNET: kent@sparky.IMD.Sterling.COM Sterling Software, IMD UUCP: uunet!sparky!kent Phone: (402) 291-8300 FAX: (402) 291-4362 Please send comp.sources.misc-related mail to kent@uunet.uu.net.