ISO C 201X Proposal:
Timezone Functions
Document Number: WG14 N___/X3J11 __-___ C201X Revision Proposal ======================= Title: Timezone Functions Author: David R. Tribble Author Affiliation: Self Postal Address: Plano, TX USA E-mail Address: david@tribble.com Web URL: http://david.tribble.com Telephone Number: *************** Sponsor: ________________________________________ Revision: 1.4, 2009-10-08 Supersedes: 1.3, 2009-08-19 Proposal Category: __ Editorial change/non-normative contribution __ Correction X_ New feature __ Addition to obsolescent feature list __ Addition to Future Directions __ Other (please specify) _________________________________ Area of Standard Affected: __ Environment __ Language __ Preprocessor X_ Library X_ Macro/typedef/tag name X_ Function X_ Header __ Other (please specify) ______________________________ Prior Art: The Java standard runtime library provides a TimeZone utility class which works in concert with its Date and Calendar utility classes. Target Audience: C/C++/POSIX programmers. Related Documents (if any): None Proposal Attached: X_ Yes __ No, but what's your interest? Abstract: The addition of new constants, types, and functions in a new <stdtime.h> header for calendar computations, intended to augment the existing capabilities of the 'tm' structure and its related functions. Date conversions are also brought closer in line with the rules of ISO 8601. |
This proposal presents enhancements to the standard C library affecting the time and date handling functions. The standard addressed is ISO/IEC 9899:1999 [1].
There are several key components to the proper handling of dates, times, and calendars:
There are several deficiencies and limitations with the existing (C99) standard library time and date functions, either because of missing functionality or because some operations are difficult (or impossible) to achieve.
The types and functions described in this proposal solve many of the problems mentioned in the previous section.
The following diagram illustrates the various types and their relationships within this proposal and its related proposals.
Conversion functions | |||
---|---|---|---|
C99 Library | Proposed | ||
(a) | localtime() | (c) | setcalendartime() |
gmtime() | mklongtime() | ||
mktime() | |||
(d) | calendarformat() | ||
(b) | strftime() | calendarscanf() | |
strptime() POSIX, not ISO C | |||
(e) | inittimezone() | ||
mktimezonename() | |||
(f) | longtimetotime() | ||
timetolongtime() |
The following terms are used in this proposal. Some of the terms may need to be added to the ISO C standard.
The following constant is defined in the <stdtime.h> standard header.
_TZ_ERROR
It is described in detail below.
#include <stdtime.h> #define _TZ_ERROR integer-expression
This is a constant of type long int, representing an invalid timezone offset.
The value of this constant must not compare equal to the offset of any supported combination of timezone setting and Daylight Saving Time adjustment.
[Notes]
This constant is used as a special error return value of the timezoneoffset() function. It is also used as a special value for the tz_offset member to indicate an invalid timezone setting.
The following types are defined in the <stdtime.h> standard header file:
struct calendar struct timezone
Each type is described in detail below.
#include <stdtime.h> struct calendar;
This structure contains a broken-down calendar date.
The contents of this structure are discussed in a separate proposal (see Proposal [P1] for more details.)
#include <stdtime.h> struct timezone;
This structure contains information about a timezone and its DST variants.
[Notes]
Timezone objects are allocated and deallocated by the programmer. Therefore there are no additional requirements for the standard library to call malloc() or free(). It is intended that multiple calendar date objects can share (i.e., point to) the same timezone object. It is recommended that implementations define the timezone structure such that its members are not pointers to allocated data objects that would need to be explicitly deallocated by the programmer. On the other hand, the structure members could be pointers to statically allocated objects which do not require any deallocation. Intimately related to timezones is the application of Daylight Saving Time rules. Timezones thus come in (at least) two flavors, those with DST in effect and those without. This setting is presumably reflected in their names (e.g., "CST" versus "CDT"). For POSIX implementations, the intent is to have the proposed timezone object contain the settings of the global timezone and daylight variables. This would allow a program to manage multiple timezones in addition to the local zone. Implementations that support complicated timezone naming schemes (such as POSIX) will probably provide extra members in this structure to deal with DST time change rules, etc.
This structure contains the following members, which may occur in any order within the structure:
long int tz_offset; // Milliseconds east of UTC struct tz_z[N]; // DST-adjusted zones
The tz_z array is composed of one or more DST structures. This structure type contains the following members, which may occur in any order:
int z_dst; // Seconds of DST adjustment char z_name[N+1]; // DST-adjusted zone name
Each member is described in more detail below.
[Note]
For example, U.S. Central Standard Time is 6 hours west of UTC, so its timezone offset is -21,600,000 (-6×60×60×1000) msec. Thus adding -06:00 to a UTC time of 18:00 yields a local CST time of 12:00. Millisecond resolution is used because of rather unusual offsets that exist for some historical timezones.
[Note]
This member provides functionality similar to the tm_dst member of the struct tm type. The semantics are more specific, though.
[Note]
Presumably, some widely accepted naming convention might be used for timezone names. For example, something similar to the POSIX timezone naming conventions could be used, e.g., "CST" for "US Central Standard Time", and so forth.
This structure may contain other implementation-defined members.
[Note]
Other members may be needed to encode the name of the timezone, and the DST rules, i.e., what days of the year DST starts and ends, etc. On POSIX systems, for instance, the timezone structure could contain all of the information obtained by a call to tzset().
To use a timezone object, it must be known which DST adjustment to apply to the timezone, which is determined based on a specific time value. In other words, a specific time is used to determine the appropriate DST rule to apply based on the time of year it occurs in, the locale settings currently in effect, etc.
Consider a timezone specification "CST6CDT" designating the "U.S. Central Standard Time" / "U.S. Central Daylight Time" timezone combination, having a base offset of 6 hours west of UTC. Its corresponding timezone object contains the following member values:
struct timezone tz_cst_cdt = { .tz_offset = -6 * 60*60*1000L, // -6 hours east of UTC .tz_z = { // .tz_z[0] { .z_name = "CST", .z_dst = 0, // No adjustment }, // .tz_z[1] { .z_name = "CDT", .z_dst = 1 * 60*60, // 1 hour adjustment }, }, };
The following sections describe the proposed functions to be added to the <stdtime.h> standard library header.
The following functions are defined in the <stdtime.h> standard header file:
inittimezone() mktimezonename() timezonedst() timezoneoffset()
Each function is described in detail below.
#include <stdtime.h> int inittimezone(struct timezone *zone, const char *name);
Initializes a timezone object, filling it with information corresponding to a timezone designated by a given name.
[Notes]
Timezone objects can be established by name (e.g., "UTC", "Z", "CST", "CST6CDT", "US Central Standard Time", etc.). Such names are implementation-specific, although it is hoped that some common naming convention (such as the one defined in POSIX) can be achieved. The allocation and deallocation of timezone objects is completely controlled by the programmer. There are thus no requirements for the standard library to call malloc() or free(). mktimezonename() is the functional complement of this function.
Argument name points to a null-terminated string containing the name of a timezone. The string may contain a name constructed by a prior call to mktimezonename(). If the string is empty (""), the local timezone is assumed. This argument may be null, in which case it is equivalent to an empty string (""). If the string compares equal to "Z", the UTC timezone with no DST adjustments is assumed.
Conforming implementations are required to support, at a minimum, timezone objects corresponding to the local and the UTC timezones.
Conforming implementations are also required to support, at a minimum,
timezones corresponding to names specified by the following syntax:
timezone-name: [+|−] [h]h [mm]
Positive values (optionally prefixed with a '+') specify zone times east (ahead) of UTC, and negative values (prefixed with a '−') specify zones west of (behind) UTC (as per the syntax defined in RFC 2822).
Implementations must recognize timezone names "−1400" through "+1400" (and their variant spellings), representing 29 distinct timezone offsets. (The designation "+0000" is preferred over "−0000", but both represent a zero offset from UTC.)
[Notes]
This syntax is based on RFC 2822. Note that positive offsets of this format designate zones east of UTC, which corresponds to the tz_offset member of a given timezone object. These minimum requirements provides a reasonably useful set of timezone names that are portable across implementations.
Implementations are free to recognize other timezone names in addition to these required names. Other than the required timezone names described above, the means by which timezone names correspond to timezone objects is implementation-defined.
The function returns zero if successful.
If no timezones with names corresponding to the string pointed to by name are supported by the implementation, or if some other error occurs, the tz_offset member of the timezone object is set to a value equal to _TZ_ERROR, and the function returns −1. The values of the other members of the timezone object are indeterminate.
[Notes]
Timezone names are implementation-specific, although it is hoped that some common naming convention can be achieved. A recommended scheme is the one used by POSIX implementations (e.g., with names like "CST6CDT" to represent "Central Standard Time" with a 6 hour offset west of UTC and "Central Daylight Time" with an additional DST adjustment of one hour). It is intended that the following function call will work as expected on POSIX compliant systems:
inittimezone(&zone, getenv("TZ"));Timezone naming schemes can be quite complicated. For example, POSIX uses the "TZ" environment variable to specify the local timezone for each user, and employs a fairly complicated syntax, allowing names such as "CST+6CDT,M4.1.0/2,M10.5.0/2", which specifies a DST/non-DST pair of timezone names and UTC offsets as well as the times of the year when the DST changes occur. It is presumed that the inittimezone() function on POSIX systems will be quite capable of handling such strings, just as the POSIX tzset() function currently does. Requiring a small set of well-defined timezone names allows portable conforming programs to be written that adjust for the 24 major timezones of the world. Intimately related to timezones is the application of Daylight Saving Time rules. Timezones thus come in two flavors, those with DST adjustments and those without, which presumably is reflected in their names (e.g., "CST" and "CDT").
The following code fragment initializes a timezone object for a timezone with a positive six hour offset from UTC.
struct timezone zone; // Initialize a timezone that is 6 hours west of UTC inittimezone(&zone, "-0600");
The following function initializes a timezone object for a timezone with an arbitrary offset from UTC.
#include <stdio.h> #include <stdtime.h> #include <string.h> int init_tz(struct timezone *zone, int hrs_east) { char buf[20]; // Initialize the timezone with a given UTC offset sprintf(buf, "%+04d", hrs_east*100); // "±hh00" return (inittimezone(zone, buf)); }
#include <stdtime.h> int mktimezonename(char *buf, size_t max, const struct timezone *zone);
Constructs the name of a given timezone object.
[Notes]
This is the functional complement of inittimezone().
Argument buf points to a character array to be filled with the resulting timezone name. The resulting string will contain the name corresponding to the timezone object, being composed of printable characters and a terminating null character. The format, length, and contents of the string are implementation-defined. This argument may be null only if argument max is zero.
Argument max specifies the maximum number of characters, including a terminating null character, that can be written into the character array. If the resulting name requires more that max characters, the function fails.
Argument zone points to a timezone object, which is assumed to have been previously initialized with a call to inittimezone(). If the tz_offset member of this object has a value equal to _TZ_ERROR, the function fails.
If successful, the function returns a value greater than zero indicating the number of characters, excluding the terminating null character, that were written into array buf. The resulting name is suitable for use as the name argument of a subsequent call to inittimezone().
[Notes]
The resulting name presumably encodes enough information about the timezone and its DST variants so that if it is passed to a subsequent call to inittimezone(), the resulting timezone object will be identical, for all intents and purposes, to the original timezone object passed to mktimezonename().
If the tz_offset member of the specified timezone object has a value equal to _TZ_ERROR, the function returns zero.
If the function fails or encounters an error, a negative value is returned and the contents of array buf are indeterminate. The absolute value of the returned value specifies a recommended minimum number of characters, including a terminating null character, needed to successfully construct a name for the specified timezone.
[Notes]
A negative return value allows the calling function to determine the minimum size to allocate for the name buffer for use in a second call to mktimezonename(). (See the example below.)
The following function allocates a string of the appropriate length and fills it with the name of a given timezone.
#include <stdlib.h> #include <stdtime.h> char * get_tz_name(const struct timezone *zone) { char * name; int len; // Determine a recommended name length len = -mktimezonename(NULL, 0, zone); name = malloc(len); // Construct the name of the timezone mktimezonename(name, len, zone); return (name); }
#include <stdtime.h> int timezonedst(const struct calendar *date, const struct timezone *zone);
Determines the Daylight Saving Time adjustment to apply to a specified calendar date within a given timezone.
Argument date points to a broken-down calendar date object. If the calendar date object does not represent a meaningful date, the function fails.
Argument zone points to a timezone object, which is assumed to have been previously initialized with a call to inittimezone(). This argument may be null, in which case no DST adjustment is specified, and the function returns zero.
If successful, the function returns the index of the tz_z[] element within the given timezone object which specifies the appropriate adjustment to apply to the given calendar date object.
If an error occurs, the function returns −1.
#include <stdtime.h> long int timezoneoffset(const struct calendar *date, const struct timezone *zone);
Determines the combined timezone and Daylight Saving Time offset to apply to a specified calendar date within a given timezone.
Argument date points to a broken-down calendar date object. If the calendar date object does not represent a meaningful date, the function fails.
Argument zone points to a timezone object, which is assumed to have been previously initialized with a call to inittimezone(). This argument may be null, in which case no timezone or DST adjustment is specified, and the function returns zero.
If successful, the function returns the number of milliseconds that must be added to the given calendar date in order to have it reflect the correct time for the specified timezone.
If an error occurs, the function returns a value equal to _TZ_ERROR.
tz-spec: std offset [dst [offset]] [, rule] offset: [+|−] time rule: date [/ time] , date [/ time]] date: [n][n]n J [n][n]n M m . w . d time: [h]h [: mm [: ss]This allows names such as "CST+6CDT+5,M4.1.0/2,M10.5.0/2", which specifies a DST/non-DST pair of timezone names and UTC offsets as well as the times of the year when the DST changes occur. POSIX also provides the global variable timezone, which specifies the difference in seconds between UTC and local timezone. It also provides the global variable daylight, which specifies that a DST adjustment should be applied to the local timezone in use. Presumably, the DST rules encoded in the "TZ" variable are also stored in global variables.
The following source files provide a proof-of-concept implementation of this proposal.
c0xtimezone.c c0xtimezone.h
My thanks to the people who gave helpful comments on early drafts of this document, and who posted suggestions and corrections on the comp.std.c newsgroup.
This document is in the public domain. Permission is granted to freely redistribute, copy, or reference this document.
This document: http://david.tribble.com/text/c0xtimezone.html.
Author's email: david@tribble.com.
Author's home page:
http://david.tribble.com.