Initial import of the CDE 2.1.30 sources from the Open Group.
This commit is contained in:
319
cde/lib/csa/iso8601.c
Normal file
319
cde/lib/csa/iso8601.c
Normal file
@@ -0,0 +1,319 @@
|
||||
/* $TOG: iso8601.c /main/2 1997/12/29 10:46:50 bill $ */
|
||||
/*
|
||||
* (c) Copyright 1993, 1994 Hewlett-Packard Company
|
||||
* (c) Copyright 1993, 1994 International Business Machines Corp.
|
||||
* (c) Copyright 1993, 1994 Novell, Inc.
|
||||
* (c) Copyright 1993, 1994 Sun Microsystems, Inc.
|
||||
*/
|
||||
|
||||
#include <EUSCompat.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
#include "iso8601.h"
|
||||
|
||||
static void
|
||||
set_timezone(char *tzname)
|
||||
{
|
||||
static char tzenv[BUFSIZ];
|
||||
|
||||
if (tzname==NULL)
|
||||
system("unset TZ\n");
|
||||
else {
|
||||
sprintf(tzenv, "TZ=%s", tzname);
|
||||
(void) putenv(tzenv);
|
||||
tzset();
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
validate_iso8601(char *buf)
|
||||
{
|
||||
/* validation rules:
|
||||
* - sscanf returns # of matches, which must be 6.
|
||||
* - crude range check on each numerical value scanned.
|
||||
* - length of input is fixed: strlen("CCYYMMDDThhmmssZ")
|
||||
* - last char must be Z, indicating UTC time
|
||||
*/
|
||||
int year, month, day, hour, min, sec;
|
||||
int scan_ret=0;
|
||||
static char tmp[] = "CCYYMMDDThhmmssZ";
|
||||
|
||||
scan_ret=sscanf(buf, "%4d%2d%2dT%2d%2d%2dZ",
|
||||
&year, &month, &day, &hour, &min, &sec);
|
||||
|
||||
/* the rules: if any fail, whole test fails so return */
|
||||
if (strlen(buf) != strlen(tmp)) return (-1);
|
||||
if (buf[strlen(buf)-1] != 'Z') return (-1);
|
||||
if (scan_ret != 6) return (-1);
|
||||
if ((year<1970) || (year>2038)) return (-1);
|
||||
if ((month<1) || (month>12)) return (-1);
|
||||
if ((day<1) || (day>31)) return (-1);
|
||||
if ((hour<0) || (hour>24)) return (-1);
|
||||
if ((min<0) || (min>59)) return (-1);
|
||||
if ((sec<0) || (sec>59)) return (-1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* _csa_iso8601_to_tick - convert a standard date/time string to a tick
|
||||
*
|
||||
* Note 1:This function supports a limited subset of the iso8601 standard.
|
||||
* Only one of the variations described by the standard is
|
||||
* supported, namely:
|
||||
*
|
||||
* CCYYMMDDThhmmssZ
|
||||
*
|
||||
* ...known in the standard as "Complete Representation, Basic Format"
|
||||
* for calendar date and "Coordinated Universal Time (UTC), Basic Format"
|
||||
* for time.
|
||||
*
|
||||
* This can carry all the information required for date+time by
|
||||
* the CDE 1.0 Calendar component. More general support, if ever
|
||||
* needed (say for interoperability or finer granularity) can be
|
||||
* implemented inside this function without modifying its interface.
|
||||
*
|
||||
* Note 2:All output time information is in UTC, and all input
|
||||
* time information is assumed to be pre-converted to UTC.
|
||||
*
|
||||
* dac 19940728T224055Z :-)
|
||||
*/
|
||||
int
|
||||
_csa_iso8601_to_tick(char *buf, time_t *tick_out)
|
||||
{
|
||||
int year, month, day, hour, min, sec;
|
||||
struct tm time_str;
|
||||
char tz_orig[BUFSIZ];
|
||||
boolean_t orig_tzset = B_FALSE;
|
||||
int scan_ret=0;
|
||||
|
||||
scan_ret=sscanf(buf, "%4d%2d%2dT%2d%2d%2dZ",
|
||||
&year, &month, &day, &hour, &min, &sec);
|
||||
|
||||
if (validate_iso8601(buf) != 0)
|
||||
return(-1);
|
||||
|
||||
time_str.tm_year = year - 1900;
|
||||
time_str.tm_mon = month - 1;
|
||||
time_str.tm_mday = day;
|
||||
time_str.tm_hour = hour;
|
||||
time_str.tm_min = min;
|
||||
time_str.tm_sec = sec;
|
||||
time_str.tm_isdst = -1;
|
||||
|
||||
if (getenv("TZ")) {
|
||||
strncpy(tz_orig, getenv("TZ"), sizeof(tz_orig));
|
||||
tz_orig[sizeof(tz_orig)-1] = '\0';
|
||||
orig_tzset = B_TRUE;
|
||||
}
|
||||
|
||||
#ifdef __osf__
|
||||
set_timezone("GMT0");
|
||||
#else
|
||||
set_timezone("GMT");
|
||||
#endif
|
||||
|
||||
*tick_out = mktime(&time_str);
|
||||
|
||||
if (orig_tzset == B_TRUE)
|
||||
set_timezone(tz_orig);
|
||||
else
|
||||
set_timezone(NULL);
|
||||
|
||||
if (*tick_out != (long)-1)
|
||||
return(0);
|
||||
else
|
||||
return(-1);
|
||||
}
|
||||
|
||||
/*
|
||||
* _csa_tick_to_iso8601 - convert from tick to iso8601 time string
|
||||
*
|
||||
* Note 1: Similar comments to the above. This function complements
|
||||
* _csa_iso8601_to_tick, providing bi-directional conversion.
|
||||
*
|
||||
* Note 2: All input and output time information is UTC.
|
||||
*/
|
||||
int
|
||||
_csa_tick_to_iso8601(time_t tick, char *buf_out)
|
||||
{
|
||||
struct tm *time_str;
|
||||
time_t tk=tick;
|
||||
char tz_orig[BUFSIZ];
|
||||
boolean_t orig_tzset = B_FALSE;
|
||||
|
||||
/* tick must be +ve to be valid */
|
||||
if (tick < 0) {
|
||||
return(-1);
|
||||
}
|
||||
|
||||
if (getenv("TZ")) {
|
||||
strncpy(tz_orig, getenv("TZ"), sizeof(tz_orig));
|
||||
tz_orig[sizeof(tz_orig)-1] = '\0';
|
||||
orig_tzset = B_TRUE;
|
||||
}
|
||||
|
||||
#ifdef __osf__
|
||||
set_timezone("GMT0");
|
||||
#else
|
||||
set_timezone("GMT");
|
||||
#endif
|
||||
|
||||
time_str = localtime(&tk);
|
||||
|
||||
if (orig_tzset == B_TRUE)
|
||||
set_timezone(tz_orig);
|
||||
else
|
||||
set_timezone(NULL);
|
||||
|
||||
/* format string forces fixed width (zero-padded) fields */
|
||||
sprintf(buf_out, "%04d%02d%02dT%02d%02d%02dZ",
|
||||
time_str->tm_year + 1900,
|
||||
time_str->tm_mon + 1,
|
||||
time_str->tm_mday,
|
||||
time_str->tm_hour,
|
||||
time_str->tm_min,
|
||||
time_str->tm_sec);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert iso8601 date time range to a start tick and an end tick
|
||||
*
|
||||
* iso8601 range is:
|
||||
* <start> "/" <end>
|
||||
*
|
||||
* start and end are iso8601 strings in CCYYMMDDThhmmssZ format.
|
||||
*/
|
||||
|
||||
int
|
||||
_csa_iso8601_to_range(char *buf, time_t *start, time_t *end)
|
||||
{
|
||||
int nchars;
|
||||
char tmpstr[BUFSIZ];
|
||||
char *p;
|
||||
|
||||
if ((p = strchr(buf, '/')) == NULL) {
|
||||
return (-1);
|
||||
}
|
||||
|
||||
nchars=(p-buf);
|
||||
strncpy(tmpstr, buf, (size_t)nchars);
|
||||
tmpstr[nchars]='\0';
|
||||
|
||||
if (_csa_iso8601_to_tick(tmpstr, start) != 0) {
|
||||
return (-1);
|
||||
}
|
||||
|
||||
p++;
|
||||
if (_csa_iso8601_to_tick(p, end) != 0) {
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (end < start)
|
||||
return (-1);
|
||||
else
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert time range specified as start/end ticks to iso8601 format
|
||||
*
|
||||
* iso8601 result is:
|
||||
* <start> "/" <end>
|
||||
*
|
||||
* start and end are iso8601 strings in CCYYMMDDThhmmssZ format.
|
||||
*/
|
||||
int
|
||||
_csa_range_to_iso8601(time_t start, time_t end, char *buf)
|
||||
{
|
||||
char tmpstr1[BUFSIZ], tmpstr2[BUFSIZ];
|
||||
|
||||
/* validate: ticks must be +ve, and end can't preceed start */
|
||||
if ((start < 0) || (end < 0) || (end < start)) {
|
||||
return(-1);
|
||||
}
|
||||
|
||||
if (_csa_tick_to_iso8601(start, tmpstr1) != 0) {
|
||||
return (-1);
|
||||
}
|
||||
if (_csa_tick_to_iso8601(end, tmpstr2) != 0) {
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (sprintf(buf, "%s/%s", tmpstr1, tmpstr2) < 0) {
|
||||
return (-1);
|
||||
}
|
||||
else
|
||||
return(0);
|
||||
}
|
||||
|
||||
static int
|
||||
not_sign(char c)
|
||||
{
|
||||
if ((c=='+') || (c=='-'))
|
||||
return (0);
|
||||
else
|
||||
return (1);
|
||||
}
|
||||
|
||||
/*
|
||||
* This converts from a string representation of a quantity of time,
|
||||
* to (signed) integer number of * seconds.
|
||||
* The first character (byte) must be a '+' or * a '-', indicating
|
||||
* the sense of the period. This can be used however you like - it's
|
||||
* just a way of carrying round the sign, while keeping the main part
|
||||
* of the string in ISO 8601.
|
||||
* The string must be in the format described by ISO 8601, clause
|
||||
* 5.5.1 (b), with the added restriction that only seconds may be specified.
|
||||
* format: [+/-]PTnS
|
||||
*/
|
||||
int
|
||||
_csa_iso8601_to_duration(char *buf, time_t *sec)
|
||||
{
|
||||
/* buf must begin with '+' or '-', then 'P', end with 'S' */
|
||||
char sign, *ptr, *ptr2, *numptr;
|
||||
int num=0;
|
||||
|
||||
ptr2 = ptr = buf;
|
||||
sign = *ptr++;
|
||||
|
||||
if (not_sign(sign)) {
|
||||
if (*ptr2++ != 'P' || *ptr2++ != 'T') {
|
||||
return (-1);
|
||||
}
|
||||
} else if (not_sign(sign) || *ptr++ != 'P' || *ptr++ != 'T') {
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (not_sign(sign))
|
||||
ptr = ptr2;
|
||||
|
||||
numptr = ptr;
|
||||
while (*ptr >= '0' && *ptr <= '9') ptr++;
|
||||
|
||||
if (numptr == ptr || !(*ptr && *ptr++ == 'S' && *ptr == NULL))
|
||||
return (-1);
|
||||
else {
|
||||
num = atoi(numptr);
|
||||
*sec = (sign == '-') ? -num : num;
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This converts from a (signed) integer number of seconds to a string
|
||||
* representation. The string format is a sign character followed by
|
||||
* an IS0 8601 string as described above for _csa_iso8601_to_duration.
|
||||
*/
|
||||
int
|
||||
_csa_duration_to_iso8601(time_t sec, char *buf)
|
||||
{
|
||||
sprintf(buf, "%cPT%dS", (sec < 0) ? '-': '+', abs(sec));
|
||||
return(0);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user