Revised C Code for draft-newman-datetime-00.txt
Markus G. Kuhn
kuhn at cs.purdue.edu
Thu Jan 16 03:23:40 UTC 1997
Here comes the revised version of my example ISO 8601 output routine.
It includes almost all suggestions that I have received plus some more
and I have added test calls that should print on all POSIX systems the
following:
Show time in various forms:
Fri Feb 13 18:59:59 2009
2009-02-13 18:59:59-05
2009-02-13 18:59:59.012-05
2009-02-13 23:59:59Z
2009-02-13 23:59:59.012Z
2009-02-13 23:59:60.012Z
2009-02-13 18:59:60-05
2009-02-13 23:59:59+00
2009-02-14 05:14:59+05:15
2009-02-14 05:14:59.012345678+05:15
Show a DST switch (Central Europe, October 1996):
1996-10-26 23:30:00Z = 1996-10-27 01:30:00+02
1996-10-27 00:00:00Z = 1996-10-27 02:00:00+02
1996-10-27 00:30:00Z = 1996-10-27 02:30:00+02
1996-10-27 01:00:00Z = 1996-10-27 02:00:00+01
1996-10-27 01:30:00Z = 1996-10-27 02:30:00+01
1996-10-27 02:00:00Z = 1996-10-27 03:00:00+01
1996-10-27 02:30:00Z = 1996-10-27 03:30:00+01
Thanks for the many valuable suggestions. Since I know now finally, why localtime() uses call-by-reference, I feel very comfortable with breaking
the consistency with this legacy interface.
Do you see any other portability or elegance problems?
Markus
--
Markus G. Kuhn, Computer Science grad student, Purdue
University, Indiana, USA -- email: kuhn at cs.purdue.edu
-------------- next part --------------
/*
* Example implementation of the RFC xxxx profile of ISO 8601
* ASCII standard date and time representation in ANSI/ISO C.
*
* This implementation is only an informal appendix of RFC xxxx and not
* part of the standard. This is an early draft version, do not use
* this in products yet.
*
* Please send comments for improvement to
*
* Markus Kuhn <kuhn at cs.purdue.edu>
*
* Thanks for suggestions to:
*
* Chris Newman <Chris.Newman at innosoft.com>
* Ken Pizzini <ken at spry.com>
* Antoine Leca <Antoine.Leca at renault.fr>
*
* $Id: demo8601.c,v 1.1 1997-01-15 22:02:37-05 kuhn Exp $
*/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
/*
* Create an ISO 8601 time string conforming to the RFC xxxx profile
*
* Input:
*
* t the time that will be written as returned by time()
* utc if true, then UTC instead of local time will be shown
* nsec nanoseconds since start of current second or 0 if unknown
* prec number of digits (0..9) for decimal fractions of the
* second (set to 0 if nsec is unknown)
*
* Output:
*
* s time string with strlen(buf) < 36
*
* When time_t does not allow to encode leap seconds (like in POSIX),
* you can use the value of second 59 in t and add 1000000000 to nsec
* in order to represent a leap second. If input or libary errors
* are detected, NULL will be returned, otherwise a pointer to the
* output string. If buf is NULL, the time string will be written to
* a local buffer. This function is not multithreading safe.
*
* Markus Kuhn <kuhn at cs.purdue.edu>, 1997
*/
char *
create_timestring(char *s, time_t t, int utc, long nsec, unsigned prec)
{
char *buf, buffer[36];
struct tm ltm, utm, *tmp;
long offset;
/* safety checks */
s = buf = (s == NULL) ? buffer : s;
*buf = '\0';
if (nsec < 0 || nsec > 1999999999L)
return NULL;
if (prec > 9)
prec = 9;
/* convert time */
if ((tmp = gmtime(&t)) == NULL)
return NULL;
utm = ltm = *tmp;
if (!utc)
ltm = *localtime(&t);
/* leap second handling */
if (nsec > 1000000000L) {
ltm.tm_sec++;
utm.tm_sec++;
nsec -= 1000000000L;
}
/* basic format */
buf += strftime(buf, 20, "%Y-%m-%d %H:%M:%S", utc ? &utm : <m);
if (prec > 0) {
sprintf(buf, ".%09ld", nsec);
buf += prec + 1;
}
/* time zone offset */
if (utc) {
*(buf++) = 'Z';
*buf = '\0';
} else {
/* we assume without check that the offset is less than 24 hours */
offset = ltm.tm_yday - utm.tm_yday;
if (offset > 1)
offset = -24;
else if (offset < -1)
offset = 24;
else
offset *= 24;
offset += ltm.tm_hour - utm.tm_hour;
offset *= 60;
offset += ltm.tm_min - utm.tm_min;
sprintf(buf, "%+03ld", offset / 60); /* hour offset */
buf += 3;
if (offset < 0)
offset = -offset;
offset %= 60;
if (offset != 0)
sprintf(buf, ":%02ld", offset); /* minute offset */
}
return s;
}
/* Some test code */
int main()
{
char buf[50];
time_t now;
time_t last_dst_hour;
int i;
puts("Show time in various forms:\n");
now = 1234569599L; /* on POSIX this is 2009-02-13 23:59:59Z */
putenv("TZ=EST5");
puts(ctime(&now));
create_timestring(buf, now, 0, 0, 0); puts(buf);
create_timestring(buf, now, 0, 12345678, 3); puts(buf);
create_timestring(buf, now, 1, 0, 0); puts(buf);
create_timestring(buf, now, 1, 12345678, 3); puts(buf);
create_timestring(buf, now, 1, 1012345678, 3); puts(buf); /* leap */
create_timestring(buf, now, 0, 1012345678, 0); puts(buf); /* leap */
putenv("TZ=UTC0");
create_timestring(buf, now, 0, 0, 0); puts(buf);
putenv("TZ=TST-5:15");
create_timestring(buf, now, 0, 0, 0); puts(buf);
create_timestring(buf, now, 0, 12345678, 9); puts(buf);
puts("\nShow a DST switch (Central Europe, October 1996):\n");
last_dst_hour = (((26L * 365) + 6 + 300) * 24) * 3600;
putenv("TZ=CET-1CEST,M3.5.0/2,M10.5.0/3");
for (i = -1; i <= 5; i++) {
printf("%s = ",
create_timestring(NULL, last_dst_hour + i * 1800, 1, 0, 0));
puts(create_timestring(NULL, last_dst_hour + i * 1800, 0, 0, 0));
}
return 0;
}
More information about the tz
mailing list