v17i030: strftime - Implementation of ANSI strftime routine, Part01/01

Arnold Robbins arnold at audiofax.com
Sat Mar 2 11:28:27 AEST 1991


Submitted-by: Arnold Robbins <arnold at 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 at 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 at 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 at 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 at 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 at root.co.uk> for helping debug earlier
versions of this routine.
Additional thanks to Arthur David Olsen <ado at 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 at 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 at 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 at uunet.uu.net.



More information about the Comp.sources.misc mailing list