C Code for draft-newman-datetime-00.txt

Markus G. Kuhn kuhn at cs.purdue.edu
Wed Jan 15 06:01:05 UTC 1997


I have done a sample implementation of the draft-newman-datetime-00.txt
ISO 8601 profile with the modifications suggested by Paul and myself
already included. So far, this is only the write routine, the parser will 
follow later.

Please scrutinize the code below such that it becomes highly portable and
reliable ANSI/ISO C.

I have some doubts whether this code will handle the local time form
correctly during both DST switch hours.  Does anyone know a nicer
solution for determining the UTC offset of local time with portable
library functions reliably? (May be, something like "utm.tm_isdst =
ltm.tm_isdst" before mktime() is still necessary?)

Well-engineered ISO C example code that can easily be included in new
applications will certainly make the ISO 8601 profile RFC more popular
and should definitely be added.

I would also like to see new format specifiers in GNU date (and POSIX.2
date) that would allow me to easily generate the UTC offset fields.

Markus

-- 
Markus G. Kuhn, Computer Science grad student, Purdue
University, Indiana, USA -- email: kuhn at cs.purdue.edu

-------------- next part --------------
#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 clock()
 *   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:
 *
 *   buf     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, buf will be set to be an empty string. This function
 * is not multithreading safe.
 *
 * Markus Kuhn <kuhn at cs.purdue.edu>, 1997-01-15 01:00:18-05, early draft
 */

void create_timestring(char *buf, time_t t, int utc, long nsec, int prec)
{
  struct tm ltm, utm, *tmp;
  double off;
  long offset;

  /* safety checks */
  if (buf == NULL) return;
  *buf = 0;
  if (t < 0 || nsec < 0 || nsec > 1999999999L) return;
  if (prec > 9) prec = 9;
  /* convert time */
  if ((tmp = localtime(&t)) == NULL)
    utc = 1; /* fallback when library does not know local time */
  else
    ltm = *tmp;
  if ((tmp = gmtime(&t)) == NULL) return;
  utm = *tmp;
  /* leap second */
  if (nsec > 1000000000L) {
    ltm.tm_sec++;
    utm.tm_sec++;
    nsec -= 1000000000L;
  }
  /* basic format */
  strftime(buf, 20, "%Y-%m-%d %H:%M:%S", utc ? &utm : &ltm);
  buf += 19;
  /* fractional seconds */
  if (prec > 0) {
    sprintf(buf, ".%09ld", nsec);
    buf += prec + 1;
  }
  /* time zone offset */
  if (utc) {
    *(buf++) = 'Z';
    *buf = 0;
  } else {
    off = difftime(t, mktime(&utm));
    if (off < -0.01) {
      *(buf++) = '-';
      offset = -off + 0.5;
    } else {
      *(buf++) = '+';
      offset = off + 0.5;
    }
    offset = (offset + 30) / 60;
    sprintf(buf, "%02ld", offset / 60); /* hour offset */
    buf += 2;
    offset %= 60;
    if (offset)
      sprintf(buf, ":%02ld", offset);   /* minute offset */
  }
  
  return;
}


int main()
{
  char buf[50];
  time_t now;

  now = time(NULL);
  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);
  create_timestring(buf, now, 0, 12345678, 9);   puts(buf);

  return 0;
}


More information about the tz mailing list