Proposal for ISO C and C++
Extended-Range Time Type


Author: David R. Tribble, <david@tribble.com>
Version: 2.10, 1999-09-26


Contents


Extended-Range Time Type

Types

The standard header <time.h> shall contain declarations for the following types:

    etime_t
    struct etime_tm

Type etime_t

The etime_t type is a signed integer type at least 60 bits wide. It is capable of representing date and time values to the nearest microsecond across a range of about 20,000 years. The type represents the number of microsecond ticks since the zero date of the extended time epoch. Negative values represent dates prior to the zero date1. Whether or not leap seconds are represented is implementation defined2. (Note that leap seconds were not introduced until 1972, so that even if an implementation does represent leap seconds, it does so only for dates after that time.)

[Rationale: A 60-bit encoding works quite well on most existing architectures. Most architectures have 8, 16, 32, or 64 bit word sizes, so a 64-bit type is appropriate. Other architectures have different word sizes, such as 36 and 60 bit; for these implementations, types using multiples of word sizes, such as 72-bit and 60-bit integers, are appropriate.]

For purposes of conversions to and from other time encoding types (such as struct tm), the time encoded in type etime_t uses the proleptic Gregorian calendar3,4. Other representations and conversions adhere to the ISO-8601 standard for representing dates and times5.

[Rationale: The use of a single calendric interpretation avoids problems that arise due to regional calendar differences. It also uses the most widely accepted (worldwide) calendar, and standardizes the calculation of entities like leap years and leap days.]

Arithmetic operations can be performed on etime_t values, since it is a signed integer type. The difference between two etime_t values represents the difference in time between the two dates, i.e., the number of microseconds between them.

[Note that the largest possible difference between two etime_t values requires a signed binary value at least 61 bits wide, covering a difference of 19,998 years; 60 bits is sufficient to encode a signed difference of only about 18,266 years. However, the difference between the largest and smallest positive valid etime_t values, or any difference of less than 10,000 years, requires a signed binary value of only 60 bits.]

Relational operations can also be performed, providing an ordered relation between time values. The ETIME_UNKNOWN constant is guaranteed to compare less than any other time value, and the ETIME_NEVER constant is guaranteed to compare greater than any other time value (see the Constants section below).

Type struct etime_tm

[Rationale: The etime_tm structure extends the representational capabilities of the tm and tmx structures. The choice of inventing a new structure type instead of using the tm_ext member of the tmx structure was done to simplify the argument passing conventions of the new etime_t functions and the memory allocation requirements of the structures passed to them. It was felt that requiring the allocation of an extension block in a new version of the tmx structure would complicate the function interfaces and would burden the programmer with extra memory management worries. It was felt that a monolithic structure type would be easier to manage on the whole.]

The etime_tm structure type shall contain at least the following members, in an order such that they are part of a common initial subsequence of the tm structure (which is declared in the <time.h> header). The semantics of the members and their normal ranges are indicated in the comments.

    int       tm_sec;      // Seconds after the minute, [0, 60]
    int       tm_min;      // Minutes after the hour, [0, 59]
    int       tm_hour;     // Hours since midnight, [0, 23]
    int       tm_mday;     // Day of the month, [1, 31]
    int       tm_mon;      // Months since January, [0, 11]
    int       tm_year;     // Year number (1 = AD0001), [-9,998, +9,999]
    int       tm_wday;     // Days since Sunday, [0, 6]
    int       tm_yday;     // Days since January 1st, [0, 365]
    int       tm_isdst;    // Minutes offset of DST, [-1, 60]

The tm_sec member has a range which allows for an additional leap second.

The tm_isdst member indicates the positive number of minutes of offset if Daylight Savings Time (DST) is in effect, zero if DST is not in effect, and -1 if the information is not available.

Note that the tm_year member has different semantics than the member of the tm and tmx structures. Instead of the number of years since 1900, it represents the calendar year number itself, and holds values in the range [-9,998+9,999].

In addition, the structure contains the following members:

    int       tm_version;  // Version number, 2
    long int  tm_zone;     // Time zone offset from UTC in seconds,
                           // [-86,400, +86,400]
    int       tm_leapsecs; // Number of leap seconds applied

The tm_version member shall have a value of 2. The implementation or a future implementation of this structure may contain additional members. If so, the additional members shall follow the last member of the structure defined here (i.e., the tm_usec member), and the tm_version member shall have a value greater than 2.

The tm_zone member indicates the number of seconds offset of the local time zone from Coordinated Universal Time (UTC). A positive value indicates a time that is ahead of UTC.

Note that the tm_zone member differs from the member of the tm and tmx structures, in that it encodes the number of seconds of timezone offset rather than minutes.

[Rationale: The tm_zone member encodes seconds rather than minutes of offset for the timezone in order to deal with certain national timezones that require an additional 30 seconds of offset.]

The tm_leapsecs member indicates the number of leap seconds applied to the time value. This member shall have a value of -1 for implementations that do not account for leap seconds2.

In addition, the structure contains the following members:

    int       tm_len;      // Length of the structure
    long int  tm_usec;     // Microseconds after the second, [0, 999,999]

The tm_len member specifies the size of the entire etime_tm structure.

Extended Time Epoch

The zero date of the extended time epoch is:

    AD1601-01-01 00:00:00.000000 Z
Positive etime_t values represent the number of microseconds after this date, and negative values represent the number of microseconds before this date.

Constraints

While it is possible that the etime_t type can represent dates across a large span of years, only dates with four-digit years are considered valid. (For example, implementing the etime_t type as a a 64-bit signed integer type provides a representation that is capable of encoding dates from the year 16,666 B.C. to the year 19,867 A.D., but only a subset of this range contains valid dates.) Thus, the etime_t type is limited to representing dates within the range:

    BC9999-01-01 00:00:00.000000 Z to
    AD9999-12-31 23:59:59.999999 Z
In other words, year numbers are limited to the range [-9,998+9,999]. Values outside of this range do not represent valid dates. (See the descriptions of the ETIME_MIN and ETIME_MAX constants in the Constants section below.)

[Rationale: Limiting the range of valid dates to four-digit years simplifies conversions to and from character representations. It also constrains year numbers not to exceed the range of a standard signed int type.]

Examples

The following tables lists some specific dates and their corresponding etime_t values:

   Date                        Value                     Notes      
   n/a                         LLONG_MIN                ETIME_UNKNOWN**
  -9998-01-01 00:00:00.000000 -366,029,020,800,000,000  ETIME_MIN
  -4712-01-01 12:00:00.000000 -199,218,916,800,000,000  Julian Day 0.0
  -0000-01-01 00:00:00.000000  -50,522,659,200,000,000  1 B.C.
   0001-01-01 00:00:00.000000  -50,491,123,200,000,000  1 A.D.
   1600-12-31 00:00:00.000000          -86,400,000,000  One day before ZD*
   1600-12-31 23:59:59.999999                       -1  1 µsec before ZD*
   1601-01-01 00:00:00.000000                       +0  Zero date of epoch
   1601-01-01 00:00:00.000001                       +1  1 µsec after ZD*
   1601-01-02 00:00:00.000000          +86,400,000,000  One day after ZD*
   1602-01-01 00:00:00.000000      +31,536,000,000,000  One year after ZD*
   1858-11-17 12:00:00.000000   +8,137,800,000,000,000  Julian day 2,400,000.5, MJD 0
   1900-01-01 00:00:00.000000   +9,435,484,800,000,000  1900 A.D.
   1970-01-01 00:00:00.000000  +11,644,473,600,000,000  POSIX epoch start
   2000-01-01 00:00:00.000000  +12,591,158,400,000,000  2000 A.D.
   2038-01-19 03:14:07.000000  +13,791,957,247,000,000  POSIX epoch end
   2100-01-01 00:00:00.000000  +15,746,918,400,000,000  2100 A.D.
  +9999-12-31 23:59:59.999999 +265,046,774,399,999,999  ETIME_MAX
   n/a                         LLONG_MAX                ETIME_NEVER**

  These values do not account for leap seconds.
*  "ZD" means "zero date".
**  This assumes that the etime_t type is implemented using the signed long long int type.

Normalized Encoded Time

Extended time values may be converted to and from normalized encodings. The normalized encoding of an extended time value is a fixed-length array of unsigned character data, representing a series of bits. Such an encoding is a portable data format.

[Rationale: The normalized portable character encoding is simply a 60-bit two's-complement signed binary value converted into characters, with the most significant bits in the first (leftmost) character. On systems employing 8-bit characters, the encoding is an array of 8 characters, 64 bits total, with the first character containing the most significant 8 bits. Systems that do not employ the two's-complement representation of binary values will need to deal with this appropriately. The intent is that the normalized encoding is very portable across a wide variety of implementations. The only other truly portable encodings are some form of character representation of the date, such as "CCYYMMDDHHMMSSUUUUUU" or the number of microseconds as a hexadecimal number, but these other encodings invariably take up more space.]

A normalized encoding for a given time value is equivalent to the value obtained by interpreting the bytes comprising the unsigned character array as if they are a signed binary integer that is ETIME_NORMLEN bytes in length. Thus the equivalence between the normalized extended time value contained in array n and its corresponding etime_t value t is:

    t = ((((...
          n[0]  * (UCHAR_MAX+1)
        + n[1]) * (UCHAR_MAX+1)
        + n[2]) * (UCHAR_MAX+1)
        + n[3]) * (UCHAR_MAX+1)
        + ...)
        + n[ETIME_NORMLEN-1];

The normalized encoding of a time value does not take into account leap seconds2, regardless of whether or not the implementation properly accounts for leap seconds in the representation of the etime_t type.

[Rationale: The normalized portable character encoding does not encode leap seconds because not every implementation is capable of handling leap seconds. It is assumed that those implementations that properly account for leap seconds will be able to add and subtract them from normalized time values appropriately.]



Constants

The standard header <time.h> shall contain declarations for the following library constants:

    ETIME_MAX
    ETIME_MIN
    ETIME_NEVER
    ETIME_NORMLEN
    ETIME_UNKNOWN

Constant ETIME_MAX

    const etime_t  ETIME_MAX;

The ETIME_MAX constant represents the highest (i.e., latest) valid time value, which is the encoding of the date:

    AD9999-12-31 23:59:59.999999 Z
It is equal to at least the value +265,046,774,399,999,999. If the implementation accounts for leap seconds, the value will be larger and will encode the date:
    AD9999-12-31 23:59:60.999999 Z

Constant ETIME_MIN

    const etime_t  ETIME_MIN;

The ETIME_MIN constant represents the lowest (i.e., earliest) valid time value, which is the encoding of the date:

    BC9999-01-01 00:00:00.000000 Z
It is equal to the value -366,029,020,800,000,000. (Note that leap seconds do not occur in dates prior to 1972.)

Constant ETIME_NEVER

    const etime_t  ETIME_NEVER;

The ETIME_NEVER constant represents a time that will never occur, i.e., a time in the future that will never be reached. It is equal to the maximum possible etime_t value. (For example, if the etime_t type is implemented using the signed long long int type, then the maximum value is LLONG_MAX.) This value is guaranteed to compare greater than any other etime_t value.

Constant ETIME_NORMLEN

    #define ETIME_NORMLEN  integer-expression

The ETIME_NORMLEN constant is a macro that evaluates to an integer expression specifying the size of a normalized extended time value.

[Rationale: This constant is a macro so that it can be used in array declarations and other constant expressions.]
Such normalized values are manipulated by the etime_norm() and etime_denorm functions (see the Functions section below). The value of the macro is the number of characters required to encode an etime_t value, and is precisely equal to the value of the expression:
    (60 + CHAR_BIT - 1) / CHAR_BIT
(For example, this value is 8 for implementations having 8-bit characters.)

[Rationale: The intent is that this value will be the same for all systems with the same word size and using the same number of bits for the etime_t type. Thus all systems having 8-bit characters, for instance, will use the same value of 8.]

Constant ETIME_UNKNOWN

    const etime_t  ETIME_UNKNOWN;

The ETIME_UNKNOWN constant represents an unknown time. It is equal to the minimum etime_t value possible. (For example, if the etime_t type is implemented using the signed long long int type, then the minimum value is LLONG_MIN.) This value is guaranteed to compare less than any other etime_t value.



Functions

The standard header <time.h> shall contain declarations for the following library functions:

    etime()
    etime_add()
    etime_denorm()
    etime_diff()
    etime_fromtime()
    etime_fromtm()
    etime_fromtmx()
    etime_gmtime()
    etime_localtime()
    etime_mktime()
    etime_norm()
    etime_sub()

    etime_totime()
    etime_totm()
    etime_totmx()

Function etime()

    bool  etime(etime_t *tp);

The etime() function sets the value pointed to by tp to the current system time. The value is guaranteed to be accurate to at least the nearest second. The value represents the time to the nearest microsecond, but is accurate only to the precision that the system is capable of representing time. (For example, if the implementation is capable of discerning time only to the nearest millisecond, the resulting extended time value will only be accurate to the nearest millisecond.) The value is guaranteed to be within the range [ETIME_MINETIME_MAX] or be the value ETIME_UNKNOWN.

If successful, the function returns true. If the system time is not available, the value pointed to by tp is set to ETIME_UNKNOWN and the function returns false. If tp is a null pointer, no assignment is attempted and the function returns true or false as stated previously6.

Function etime_add()

    bool  etime_add(const struct etime_tm *a, const struct etime_tm *d,
              struct etime_tm *b);

The etime_add() function adds the broken-down time in the structure pointed to by d to the broken-down time in the structure pointed to by a, storing the result in the structure pointed to by b. The structure pointed to by d represents the delta time between the time at a and the resulting time at b. In other words, it specifies the number of years (tm_year), then months (tm_mon), then days (tm_mday), then hours (tm_hour), then minutes (tm_min), then seconds (tm_sec), and then microseconds (tm_usec) that are to be added to the time at a to make it equal to the time at b. All other members of the structure pointed to by d are ignored. The resulting time at b represents a time after (i.e., later than) the time at a. Undefined behavior results if any of the members of the structure at d mentioned above are not positive.

If, after the delta time has been added to time at a, the resulting date is invalid (i.e., represents a time later than ETIME_MAX), the members of the structure pointed to by b are set to values that represent the time ETIME_MAX. Otherwise, the resulting member values are guaranteed to represent a time within the range [ETIME_MINETIME_MAX].

If the time at a does not represent a valid time (i.e., if one or more of the structure members have improper values), the results are undefined.

If successful, the function returns true. If an error occurs, the object pointed to by b is not changed and the function returns false. If pointer a or pointer d is a null pointer, none of the members of the structure at b are modified and the function returns false. If pointer b is a null pointer, no assignment is attempted and the function returns false.

Function etime_denorm()

    bool  etime_denorm(const unsigned char n[ETIME_NORMLEN], etime_t *tp);

The etime_denorm() function decodes the normalized, portable character data in n into an extended time value, storing the resulting value in the object pointed to by tp. The resulting extended time value is guaranteed to be within the range [ETIME_MINETIME_MAX], or the value ETIME_UNKNOWN or ETIME_NEVER.

If successful, the function returns true. If the encoded value in n does not represent a valid date or either of the values ETIME_UNKNOWN or ETIME_NEVER, The object pointed to by tp is set to ETIME_UNKNOWN and the function returns false. If either pointer n or tp is a null pointer, no assignment is attempted and the function returns false.

Function etime_diff()

    bool  etime_diff(const struct etime_tm *a, const struct etime_tm *b,
              struct etime_tm *d);

The etime_diff() function determines the delta time between the broken-down times pointed to by a and b, placing the results in broken-down form into the structure pointed to by d. The delta time represents the absolute difference between time a and time b. In other words, it specifies the number of years (tm_year), then months (tm_mon), then days (tm_mday), then hours (tm_hour), then minutes (tm_min), then seconds (tm_sec), and then microseconds (tm_usec) that must be added to the the time at a to make it equal to the time at b. All other members of the structure pointed to by d are set to zero.

[Rationale: The difference between two etime_t values can be computed by simply subtracting one from the other, resulting in the difference between the two times in microseconds. However, this is not always an appropriate measure of the "difference" between two dates, hence the need for this function. For example, the difference between 1991-01-01 and 1992-01-01 is exactly one year of 365 days; in contrast, the difference between 1992-01-01 and 1993-01-01 is also exactly one year, but 366 days, because 1992 is a leap year.]

If successful, the members of the structure at d are set to positive values, and the function returns true. If the structure at a represents a time that is later than the time in the structure at b, the results are undefined. If an error occurs, the object pointed to by d is not changed and the function returns false. If d is a null pointer, no assignment is attempted and the function returns false.

Function etime_fromtime()

    bool  etime_fromtime(const time_t *xp, etime_t *tp);

The etime_fromtime() function converts the time_t value pointed to by xp into its equivalent etime_t time, storing the result in the object pointed to by tp.

If successful, the resulting value is guaranteed to be within the range [ETIME_MINETIME_MAX], and the function returns true. If unsuccessful or if an error occurs, the function returns false. If either xp or tp is a null pointer, no conversion occurs and the function returns false.

Function etime_fromtm()

    bool  etime_fromtm(const struct tm *xp, struct etime_tm *sp);

The etime_fromtm() function converts the broken-down time structure pointed to by xp into its corresponding broken-down time structure pointed to by sp. The members of the resulting structure object at sp shall have normalized values (in particular, the tm_year member will have a value in the range [-9,998+9,999]), provided that the date represented by the object at xp is valid (i.e., within the range [ETIME_MINETIME_MAX]), otherwise the members of the structure at sp are set to zero.

If successful, the function returns true. If unsuccessful or if an error occurs, the function returns false. If either pointer xp or sp is a null pointer, no conversion occurs and the function returns false.

Function etime_fromtmx()

    bool  etime_fromtmx(const struct tmx *xp, struct etime_tm *sp);

The etime_fromtmx() function converts the broken-down time structure pointed to by xp into its corresponding broken-down time structure pointed to by sp. The members of the resulting structure object at sp shall have normalized values (in particular, the tm_year member will have a value in the range [-9,998+9,999]), provided that the date represented by the object at xp is valid (i.e., within the range [ETIME_MINETIME_MAX]), otherwise the members of the structure at sp are set to zero.

If successful, the function returns true. If unsuccessful or if an error occurs, the function returns false. If either pointer xp or sp is a null pointer, no conversion occurs and the function returns false.

Function etime_gmtime()

    bool  etime_gmtime(etime_t t, struct etime_tm *sp);

The etime_gmtime() function converts the time value t into its corresponding broken-down time value pointed to by sp, relative to UTC time. The members of the etime_tm structure object pointed to by sp are set to normalized values. The tm_year member of the structure is set to a value in the range [-9,998+9,999].

If successful, the function returns true. If an error occurs, the object pointed to by sp is not changed and the function returns false. If time t is ETIME_UNKNOWN or ETIME_NEVER, all of the members of the structure at sp are set to zero and the function returns false. If sp is a null pointer, no assignment is attempted and the function returns false.

Function etime_localtime()

    bool  etime_localtime(etime_t t, struct etime_tm *sp);

The etime_localtime() function converts the time value t into its corresponding broken-down time value pointed to by sp, taking into account the local timezone offset from UTC. The members of the etime_tm structure object pointed to by sp are set to normalized values. The tm_year member of the structure is set to a value in the range [-9,998+9,999].

If successful, the function returns true. If an error occurs, the value pointed to by s is not changed and the function returns false. If time t is ETIME_UNKNOWN or ETIME_NEVER, all of the members in the structure at sp are set to zero and the function returns false. If sp is a null pointer, no assignment is attempted and the function returns false.

Function etime_mktime()

    bool  etime_mktime(const struct etime_tm *sp, etime_t *tp);

The etime_mktime() function converts the broken-down time value pointed to by sp into its equivalent etime_t value, storing the result into the object pointed to by tp. The resulting value at tp is guaranteed to be within the range [ETIME_MINETIME_MAX], provided that the date represented by the structure object at sp is valid, otherwise t is set to ETIME_UNKNOWN.

If successful, the function returns true. If unsuccessful or if an error occurs, the function returns false. If either sp or tp is a null pointer, no conversion occurs and the function returns false.

Function etime_norm()

    bool  etime_norm(etime_t t, unsigned char n[ETIME_NORMLEN]);

The etime_norm() function encodes the extended time value t into normalized, portable character data, storing the resulting data in array n.

The resulting encoded time does not take into account leap seconds, regardless of whether or not the implementation's representation of the etime_t type does so2.

If successful, the function returns true. If value t is not within the range [ETIME_MINETIME_MAX] and is not the value ETIME_UNKNOWN or ETIME_NEVER, the resulting data in n is set to an encoding for the value ETIME_UNKNOWN and the function returns false. If n is a null pointer, no assignment is attempted and the function returns false.

Function etime_sub()

    bool  etime_sub(const struct etime_tm *a, const struct etime_tm *d,
              struct etime_tm *b);

The etime_sub() function subtracts the broken-down time in the structure pointed to by d from the broken-down time in the structure pointed to by a, storing the result in the structure pointed to by b. The structure pointed to by d represents the delta time between the time at a and the resulting time at b. In other words, it specifies the number of years (tm_year), then months (tm_mon), then days (tm_mday), then hours (tm_hour), then minutes (tm_min), then seconds (tm_sec), and then microseconds (tm_usec) that are to be subtracted from the time at a to make it equal to the time at b. All other members of the structure pointed to by d are ignored. The resulting time at b represents a time before (i.e., earlier than) the time at a. Undefined behavior results if any of the members of the structure at d mentioned above are not positive.

If, after the delta time has been subtracted from the time at a, the resulting date is invalid (i.e., represents a time earlier than ETIME_MIN), the members of the structure pointed to by b are set to values that represent the time ETIME_MIN. Otherwise, the resulting member values are guaranteed to represent a time within the range [ETIME_MINETIME_MAX].

If the time at a does not represent a valid time (i.e., if one or more of the structure members have improper values), the results are undefined.

If successful, the function returns true. If an error occurs, the object pointed to by b is not changed and the function returns false. If pointer a or pointer d is a null pointer, none of the members of the structure at b are modified and the function returns false. If pointer b is a null pointer, no assignment is attempted and the function returns false.

Function etime_totime()

    bool  etime_totime(etime_t t, time_t *xp);

The etime_totime() function converts the etime_t value t into its equivalent time_t time, storing the result in the object pointed to by xp.

If successful, the function returns true. If unsuccessful or if an error occurs, the function returns false. If the time represented by the value t is ETIME_UNKNOWN or ETIME_NEVER, or if it cannot be represented by a time_t value, the value pointed to by xp is set to the minimum or maximum meaningful time_t value, as appropriate, and the function returns false. If xp is a null pointer, no conversion occurs and the function returns false.

Function etime_totm()

    bool  etime_totm(const struct etime_tm *sp, struct tm *xp);

The etime_totm() function converts the broken-down time structure pointed to by sp into its corresponding broken-down time structure, storing the results in the object pointed to by xp. The members of the resulting structure at xp shall have normalized values (in particular, the tm_year member shall have a value in the range [-11,898+8,099]), provided that the date represented by the object at sp is valid (i.e., within the range [ETIME_MINETIME_MAX]), otherwise the members of the structure at xp are set to zero.

If successful, the function returns true. If unsuccessful or if an error occurs, the function returns false. If either sp or xp is a null pointer, no conversion occurs and the function returns false.

Function etime_totmx()

    bool  etime_totmx(const struct etime_tm *sp, struct tmx *xp);

The etime_totmx() function converts the broken-down time structure pointed to by sp into its corresponding broken-down time structure, storing the results in the object pointed to by xp. The members of the resulting structure at xp shall have normalized values (in particular, the tm_year member shall have a value in the range [-11,898+8,099]), provided that the date represented by the object at sp is valid (i.e., within the range [ETIME_MINETIME_MAX]), otherwise the members of the structure at xp are set to zero.

It is implementation-defined whether or not the tm_ext member of the structure at xp is null. If it is not null, then the object to which it points shall be allocated as if by a call to malloc() (which is declared in the <stdlib.h> header).

If successful, the function returns true. If unsuccessful or if an error occurs, the function returns false. If either sp or xp is a null pointer, no conversion occurs and the function returns false.



Other Issues

Alternate Epochs

The following is a list of other dates to be considered for use as the zero date of the extended time epoch:
    BC4713-01-01 12:00:00 Z  Julian day 0.0 (LY)
    BC0001-01-01 00:00:00 Z  1 B.C. (LY)
    AD0001-01-01 00:00:00 Z  1 A.D.
    AD1600-01-01 00:00:00 Z  (LY)
    AD1601-01-01 00:00:00 Z  COBOL-90 and Win32 epoch start
    AD1858-11-17 12:00:00 Z  MJD 0.0, VMS epoch start
    AD1900-01-01 00:00:00 Z  ISO struct tm min year, IBM ESA epoch start
    AD1970-01-01 00:00:00 Z  POSIX epoch start
    AD2000-01-01 00:00:00 Z  (LY)
    AD2001-01-01 00:00:00 Z  2001 A.D., a Monday
Dates marked with (LY) are leap years.



Footnotes

  1. Thus an etime_t value of -1 represents the time at one microsecond before the zero date of the epoch, or the date AD1600-12-31 23:59:59.999999 Z.

  2. Leap seconds are the difference in the counting of seconds between UTC (Coordinated Universal Time) as measured by atomic clocks (International Atomic Time, or IAT) and the actual physical rotation of the Earth (terrestrial time). The first leap second was introduced on 1972-06-30. Some implementations are capable of properly accounting for leap seconds, while others are not.

  3. The proleptic Gregorian calendar is used regardless of the dates being represented, and regardless of the locale and regional calendars in use for the implementation. This provides a consistent and portable calendar encoding scheme.

  4. Dates prior to AD0001-01-01 (i.e., dates in the B.C. or B.C.E. era) are represented with negative year numbers, so that BC0001 is year number 0, BC0002 is year number -1, and so forth. (There is no year AD0000 or BC0000 in the Gregorian calendar.) This agrees with current practices of historical reckoning.

  5. The ISO-8601 standard specifies an international character presentation of dates and times. Some variations are specifically allowed, but the basic format is "CCYY-MM-DD HH:MM:SS". In this proposal, the format is extended by appending ".UUUUUU", specifying microseconds.

  6. This is useful for determining whether or not the implementation is capable of discerning time.



Prior Art

  1. POSIX defines the time_t time type as a count of the number of seconds since AD1970-01-01 00:00:00 Z. It is usually implemented as a 32-bit signed integer. This epoch ends at AD2038-01-19 03:14:07 Z. Negative values are not valid times.

  2. POSIX defines a timeval structure, composed of a seconds count (a time_t value) member and a microseconds count member.

  3. The ANSI COBOL-90 standard specifies date and time functions for manipulating values representing dates since AD1601-01-01 00:00:00 Z (which was a Monday in the proleptic Gregorian calendar). Dates prior to this are not handled.

  4. The Digital (DEC) VMS operating system implements a 64-bit time value representing the number of 10-nanosecond ticks since AD1858-11-17 12:00:00 Z, which is the standard astronomical Modified Julian Day (MJD) zero day and is Julian day number 2,400,000.5.

  5. The Microsoft Win32 API encodes file timestamps as 64-bit values representing the number of 100-nanosecond ticks since AD1601-01-01 00:00:00 Z.

  6. The IBM S/360 and ESA architectures implement a system clock using an unsigned 64-bit binary tick counter representing time elapsed since AD1900-01-01 00:00:00 Z. Each tick is 2-12 microseconds (approx. 244.14 picoseconds). The most significant 32 bits of this counter represent the number of "long seconds" elapsed, where a "long second" is 220 microseconds. This epoch will end in AD2047.

  7. IBM CICS implements a 15-digit packed decimal ABSTIME value, representing the number of milliseconds since AD1900-01-01 00:00:00 Z. It is capable of representing dates 31,688 years beyond this date. However, it relies on the underlying system clock for the actual time value, so as a practical matter it cannot handle dates beyond the system epoch (which ends in AD2047 on S/360 systems).

  8. The Apple Rhapsody operating system implements system dates as an class type named NSDate. It is implemented as a time interval (of class type NSTimeInterval), which is a double floating-point type, encoded as seconds and providing sub-millisecond precision across a range of over 10,000 years. Dates are time intervals in the epoch having a zero date of AD2001-01-01 00:00:00 Z. Negative values represent dates prior to this date. Assuming that a double type is implemented as an IEEE 64-bit value having 53 fraction bits, an NSDate type has a precision of about 28,500 parts per second.



References

  1. Working Draft of the ISO C9X Programming Language Standard, 1997-11-21, Document WG14/N2620 J11/97-158, section 7.16, "Date and time <time.h>".
    Found at <http://www.dkuug.dk/JTC1/SC22/open/n2620/>.

  2. ISO C++ Proposal: Extended-Range Time Classes
    David R. Tribble, 1998-03.
    Found at <http://david.tribble.com/text/cxxtime.htm>.

  3. ISO-8601 Standard Representation of Dates and Times.
    The actual document is copyrighted by ISO and is not available online. However, the following documents describe the standard to various degrees:

  4. Proposed new <time.h> for ISO C9X
    By Markus Kuhn.
    Another proposal to enhance the capabilities of the ISO C time and date library functions.
    Found at <http://www.cl.cam.ac.uk/~mgk25/c-time/>.

  5. Modernized <time.h> API for ISO C
    By Paul Eggert.
    Another proposal to enhance the capabilities of the ISO C time and date library functions.
    Found at <http://www.twinsun.com/tz/timeapi.html>.


Acknowledgments

I wish to thank Jay Crisman for his comments on an early draft, which led to the idea of constrainting year numbers to four-digit numbers.

I wish to thank Darron Shaffer for his comments, and for the idea of the normalized portable character encoding functions.


Revision History

2.10, 1999-09-26, David R. Tribble.
Minor additions. Added a PGP authentication signature. Added references to other proposals by Markus Kuhn and Paul Eggert. Removed the "Source Code" section.

2.09, 1998-07-14, David R. Tribble.
Replaced <code> tags with <tt> tags. Corrected improper uses of "UTC" by replacing with "Z" (Zulu timezone).

2.08, 1998-06-27, David R. Tribble.
Minor corrections, clearer prose. Renamed functions etime_addtime() and etime_subtime() to etime_add() and etime_sub(), and changed their prototypes. Added the "Source Code" section, containing links to example source files. Added list of URLs for the ISO-8601 standard.

2.07, 1998-06-20, David R. Tribble.
Minor corrections, clearer prose. Renamed function etime_difftime() to etime_diff() and changed its prototype. Added function etime_fromtm() and etime_totm().

2.06, 1998-06-19, David R. Tribble.
Allow ETIME_MAX to encode a leap second. More exact definition of normalized/unnormalized time value equivalence. Added "Contents" section. Corrected etime_t values for example dates table.

2.05, 1998-04-23, David R. Tribble.
Minor corrections. Changed the value of the tm_version member of struct etime_tm to 2. Added the reference to the Apple Rhapsody O/S.

2.04, 1998-04-17, David R. Tribble.
Minor corrections.

2.03, 1998-04-03, David R. Tribble.
Added the "Normalized Encoded Time" section. Changed ETIME_UNKNOWN and ETIME_NEVER to be the minimum and maximum values of the underlying integer type (e.g., LLONG_MIN and LLONG_MAX). Exchanged the meanings of functions etime_fromtime() and etime_totime().

2.02, 1998-03-30, David R. Tribble.
Added struct etime_tm type. Replaced all uses of struct tmx with struct etime_tm. Added functions for converting between struct etime_tm and struct tmx types, and between etime_t and time_t types. Added the etime_subtime() function. Added the "Revision History" section.

2.01, 1998-03-25, David R. Tribble.
Indented the "Rationale" paragraphs. Removed the long hexadecimal constants for the ETIME_XXX values. Boldfaced the constant and function declarations.

2.00, 1998-03-22, David R. Tribble.
Limited the range of years (tm_year) to [BC9999, AD9999]. Changed tick resolution from 100 nsec to 1 usec. These changes resulted in changing the etime_t type from a 64-bit integer to a 60-bit integer. Changed the values of the ETIME_MIN and ETIME_MAX constants for 60 bits. Rewrote the "Examples" table of dates and etime_t values.

1.06, 1998-03-11, David R. Tribble.
(Incorporated comments by Darron Shaffer.) Added the ETIME_NEVER constant. Added the normalized portable character encoding/decoding functions, and the ETIME_NORMLEN constant. Moved the list of alternative epoch zero dates to the end of the document. Added a reference to the proposed C9X draft standard.

1.05, 1998-03-11, David R. Tribble.
(Incorporated comments by Jay Crisman.) Renamed "start of the epoch" to "zero date (ZD) of the epoch". Fixed the range of allowable year numbers (tm_year), allowing for a zero date other than zero (BC0001). Fixed some function prototypes.

1.04, 1998-02-09, David R. Tribble.

1.03, David R. Tribble.

1.02, David R. Tribble.

1.01, David R. Tribble.

1.00, David R. Tribble.
First cut.


Email comments to David Tribble at <david@tribble.com>.
David Tribble's home page is at <http://david.tribble.com/>.

Copyright ©1998-1999, by David R. Tribble, all rights reserved.
All trademarks are the property of their respective owners.


THIS DOCUMENT IS OBSOLETE AND HAS BEEN SUPERSEDED BY: http://david.tribble.com/text/c0xlongtime.html.