ISO C 200X Proposal:
Long Time Type


By David R. Tribble
david@tribble.com

Revision 1.3, 2004-08-01

 

Contents


Cover Sheet

 
                     Document Number:  WG14 N___/X3J11 __-___

                           C200X Revision Proposal
                           =======================

 Title:              Long Time Type
 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.3, 2004-08-01
 Supersedes:         1.2, 2004-07-18
 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
       __ Variable
       X_ Function
       X_ Header
       __ Other (please specify)  ______________________________  
 Prior Art:
      Many operating system provide functions for retrieving the
      system time with a resolution greater than one second,
      including POSIX, Microsoft DOS and Win32, Digital VMS,
      Apple Mac/OS, IBM OS/390, IBM OS/400, etc.
 Target Audience:  _____________________________________________
 Related Documents (if any): None.
 Proposal Attached: X_ Yes __ No, but what's your interest?
 Abstract:
      The addition of an extended time type and supporting
      functions to provide enhanced resolution of system time
      values.  Also the addition of a new standard library header
      that contains these types and functions.
 

 


And an astronomer said, Master, what of Time?
And he answered:
You would measure time the measureless and the immeasurable.
You would adjust your conduct and even direct the course of your spirit according to hours and seasons.
Of time you would make a stream upon whose bank you would sit and watch its flowing.

— Kahlil Gibran, The Prophet, 1923.

1. Introduction

All hosted ISO C (ISO 9899:1999) implementations support the concept of system time. The system time can be queried by the standard time() function, which returns values of type time_t. This type embodies system time values in some implementation-defined representation.

This proposal describes a new time type, longtime_t, and supporting functions to be added to the standard C library. This type is defined with a well-defined range and resolution, and with more precise semantics than the existing time_t type.


1.1 Problems Addressed

While the standard definition of the time_t type has managed to cover the minimum functionality required to support the notion of system time, it is lacking in several areas. The basic problem is that the current definition of it was made purposefully vague in order to include as many existing implementations as possible at the time (circa 1988).

Specific problems with the time_t are not discussed here, but are dealt with in a separate proposal (see Proposal [P1]). It is felt that attempting to improve the capabilities of the time_t type and its related functions would require changes to existing implementations to an extent that such an approach would be rejected on practical grounds.

This document instead proposes the invention of a new library type, similar to time_t, that is designed from the outset to possess specific semantic properties. New functions for manipulating objects of this type are proposed as well.

In setting out to invent a new time type, it is useful to list the shortcomings of the existing standard time_t type. The following is a list of common programming operations and capabilities that are not currently supported by the time_t type of ISO C.

Problems

  1. There is no defined or guaranteed minimum resolution for time_t values, such as one second.

    It is possible that such a minimum could be mandated, but in light of existing implementations, it is unlikely to be anything smaller (finer) than one second, and it is possible that even a resolution as large as one second may prove troublesome for existing implementations.

  2. time_t values have no defined date range.

    System times may span dates covering ranges as wide as thousands of years or as short as a single day. Mandating any specific range of dates would, as a practical matter, prove impossible without adversely affecting some existing implementations.

  3. There is no defined way to determine if a given time_t value is valid, i.e., that it represents a meaningful date within the implementation.

  4. There is no explict time_t "error" value.

    A value of (time_t)(-1) is used as a special return value from the mktime() function to indicate an error, but it is not explictly defined as an error value when assigned to a variable of type time_t.

  5. time_t values have no defined encoding representation. Beyond the fact that time_t is an arithmetic type, it has no other specified computational properties.

    While this gives implementations the most latitude for choosing how to implement the type (essentially allowing any encoding, any range, and any precision), it also makes it difficult to write programs that manipulate system times by any means other than the few standard functions provided.

  6. time_t values have no defined external representational properties.

    A system time value is, in effect, constrained to reside only within the confines of any given implementation, with no consideration given to the possibility that such values can be shared between other implementations.

  7. There is no requirement that a given time_t value compares less than another time_t value representing a later (or earlier) time.

  8. It is unspecified whether or not negative time_t values represent valid times.

  9. Incrementing or decrementing a time_t value is an operation that has no defined interpretation.

  10. The existing mktime() function cannot handle date conversions from any timezone other than the local timezone, such as GMT.

1.2 Design Goals

The following design goals guide the design of a new long time type for ISO C that is meant to remedy the shortcomings listed above.

  1. Use of a standard primitive datatype to represent extended-precision system times. Leaving the type as implementation-defined is to be avoided.

  2. Well-defined representational properties, such as minimum range and precision. The type should have well-defined arithmetic properties as well.

  3. Resonably compact external representational form. Most existing implementations of the time_t type use a 32-bit (4-byte) value, so choosing a type that is not too much larger than this is desirable.

  4. Range of dates that is wide enough (spans enough years) to be useful to most civil and commercial applications. The earliest dates to deal with probably involve still-extant mortgage and other related financial calculations, which implies a usable date range spanning a few hundred years before and after the present.

  5. Resolution fine enough to be useful to most civil and commercial applications. Network latency and GPS applications typically involve time calculations on the scale of a few nanoseconds, give or take a few orders of magnitude.

  6. Convertible to and from existing time_t and struct tm values.

  7. Address the problem of representing or not representing leap seconds.

  8. Provide thread-safe function definitions. In particular, none of the functions should rely on global variables.

  9. Define a standard portable external representation for system time values, so that they can be stored externally and interchanged between different implementations.

2. Definitions

The following terms are used in this proposal. Some of the terms may need to be added to the ISO C standard.

broken-down time
Same as calendar date.

calendar date
A data structure whose members comprise the components of a date and time value, i.e., a year, month, day of the month, hour, minute, second, etc. Such a set of values represents a date within a calendric system, which is usually taken to be the international Gregorian calendar. Also known as broken-down time.

Daylight Savings Time (DST)
An adustment made to civil time as a way of increasing the number of working daylight hours during the summer months. This generally involves a set of timekeeping rules whereby the clock is advanced ahead a specific amount of time (typically one hour) during the Spring (e.g., in April) and back again in the Fall (e.g., in October). These rules thus make it possible for times within a certain interval (typically between 02:00:00 and 03:00:00) to occur not at all on one particular night of the year (when DST takes effect) and to occur twice on another particular night of the year (when DST reverts back to normal).

epoch
The range of dates that can be represented by a given time encoding format. Sometimes used to mean the beginning date of the epoch.

external time
Same as portable external time.

leap second
An extra second periodically inserted into civil (UTC) time reckoning, which is based on International Atomic Time (TAI), in order to keep it synchronized to within 0.9 seconds of the physical rotation of the Earth. A leap second is inserted into the international calendar about every 500 days or so, generally just after the last day of June or December. The first leap second was {1972-06-30 23:59:60 Z}.

long time
An extended-precision system time value, representing a date and time within the implementation using a representation having a higher resolution than the standard time_t type.

system time
Time as reckoned by the implementation. This is a generally a linear count of the amount of time that has transpired from some starting date, but actual implementations vary widely as to the format, range, and precision with which they reckon time.

portable external time
An encoding of system time that is portable between all implementations, whereby a given external long time value represents the same system time on all implementations.

tick
The smallest discernable increment of time the can be encoded in a given representational format. For implementations that use an integer representation, this is generally a fixed unit of time. For other implementations that use, say, a floating-point representation for times, this may be an interval of variable duration.

timezone
A region of the Earth's surface having a given time difference from GMT (Zulu) time. This is necessary to reflect the fact that 12:00 noon in Bangladesh is not the same moment in time as 12:00 noon in New York or London, due to the fact that the Earth is round. Timezones are usually defined as being an integral number of hours offset from GMT time, but there are exceptions to this rule.

3. Library Header

A new standard library header is to be added, named <stdtime.h>. This header contains the constants, types, and functions described in this proposal.

[Note]
A newly invented standard header file is proposed in order to minimize the impact that these new names will have on existing code.

The choice of the name of the new header reflects its contents, namely, standard functions dealing with time.

It is recommended that the <stdtime.h> header also contain the entire contents (constant, type, variable, and function definitions) of the existing <time.h> standard library header.

[Note]
While this creates a duplicate source for the existing names in the <time.h> header, it makes it easier to transition to the new header. This implies that the <time.h> header may be deprecated in some future version of ISO C.


4. Constants

The following constants are defined in the <stdtime.h> standard header.


4.1 Constant _LONGTIME_ERROR

Synopsis

    #include <stdtime.h>

    #define _LONGTIME_ERROR  (longtime_t)integer-expression

Description

This preprocessor macro is defined as an expression of type longtime_t, and represents a special long time error value. It is guaranteed to compare unequal to all longtime_t values that represent valid times within the implementation, i.e., this constant compares less than _LONGTIME_MIN or greater than _LONGTIME_MAX.

This value is returned from some of the long time functions to indicate an error condition or failure. Variables of type longtime_t may be initialized to this value to indicate that they do not represent a valid time.

[Note]
This constant is similar to the special (time_t)(-1) error value that is returned from various time_t handling functions such as mktime(). By being defined as a standard macro name, though, it is more portable than an explicit hard-coded value.

See also

_LONGTIME_MAX, _LONGTIME_MIN, _TIME_ERROR, longtime_t, getlongtime(), mklongtime().

4.2 Constant _LONGTIME_HAS_LEAP_SECS

Synopsis

    #include <stdtime.h>

    #define _LONGTIME_HAS_LEAP_SECS  integer-expression

Description

This preprocessor macro is defined as an expression of type int which specifies whether or not the longtime_t type encodes leap seconds. This constant evaluates to a non-zero (true) value if the implementation inserts leap seconds into the longtime_t type (i.e., if certain distinct long time values represent leap seconds that have been inserted into the standard calendar), or to zero (false) if it does not.

[Note]
There has been much debate on whether a standard time representation should encode leap seconds or not. This proposal leaves that up to the implementation, but requires it to specify clearly what it chooses to do.

It is recognized that some existing implementations go to the trouble of providing system times with inserted leap seconds. For these implementations, it might require an unreasonable amount of overhead to compute the current system time with leap seconds removed. For this reason, the choice is left to the implementor. See Appendix B for further discussion.

In order to deal with disparate implementations, some providing leap seconds and others not, this proposal mandates the presence of this constant as well as the extlongtime() and intlongtime() functions.

See also

longtime_t, extlongtime(), getlongtime(), intlongtime().

4.3 Constant _LONGTIME_MAX

Synopsis

    #include <stdtime.h>

    #define _LONGTIME_MAX  (longtime_t)integer-expression

Description

This preprocessor macro is defined as an expression of type longtime_t which represents the maximum (latest) valid long time value. Long time (longtime_t) values greater than this constant do not represent meaningful times within the implementation.

This constant must not be less than +6,311,390,399,999,999,999, which is the long time value representing the date that is one tick before {AD 2401-01-01 00:00:00 Z}, without leap seconds.

See also

_LONGTIME_ERROR, _LONGTIME_MIN, longtime_t, getlongtime().

4.4 Constant _LONGTIME_MIN

Synopsis

    #include <stdtime.h>

    #define _LONGTIME_MIN  (longtime_t)integer-expression

Description

This preprocessor macro is defined as an expression of type longtime_t which represents the minimum (earliest) valid long time value. Long time (longtime_t) values less than this constant do not represent meaningful times within the implementation.

This constant must not be greater than -6,311,390,400,000,000,000, which is the long time value representing the date {AD 1601-01-01 00:00:00 Z}, without leap seconds, in the proleptic Gregorian calendar.

See also

_LONGTIME_ERROR, _LONGTIME_MAX, longtime_t, getlongtime().

4.5 Constant _LONGTIME_RESOLUTION

Synopsis

    #include <stdtime.h>

    #define _LONGTIME_RESOLUTION  integer-expression

Description

This preprocessor macro is defined as an expression of type long int which specifies the least number of ticks per second that the implementation is capable of discerning when it determines system time.

This value thus specifies the largest difference to be expected between the long time values returned by two separate calls to function getlongtime() that are as close together in time as possible within the implementation.

[Note]
This constant specifies the largest granularity of time that the implementation can determine from the system clock. It is similar to the CLOCKS_PER_SEC constant, which specifies the resolution of the values returned by the clock() function.

Note that this constant specifies the physical resolution of the system clock, which is completely independent of the resolution of the longtime_t representation.

For example, if an implementation can discern system time to the nearest hundredth of a second (0.01 sec or 100 Hz), this constant would be defined as 100.

This constant specifies the least number of ticks per second in order to provide a meaningful value on implementations whose system clock does not have a fixed resolution. For example, if the precision that an implementation can detect time varies in resolution from 60 to 100 ticks per second, this constant would be defined as the least accurate end of the range, 60, indicating that the implementation can detect time with at least this resolution or better.

This constant must compare greater than zero, unless the implementation cannot discern system time values at all, in which case this constant is equal to zero.

[Note]
Mandating that this constant have a value of at least 1 obligates implementations to provide system times with a resolution of at least one second.

A value of zero indicates an implementation that cannot discern system time, and thus is not capable of supporting the standard long time functions, nor, presumably, the existing time() function.

This value must not be greater than 500,000,000, which is the smallest possible resolution representable by the longtime_t type (i.e., a tick duration of two nanoseconds).

See also

CLOCKS_PER_SEC, longtime_t, getlongtime().

4.6 Constant _TIME_ERROR

Synopsis

    #include <stdtime.h>

    #define _TIME_ERROR  (time_t)integer-expression

Description

This preprocessor macro is defined as an expression of type time_t, and represents a special system time error value. It is guaranteed to compare unequal to all time_t values representing valid times according to the implementation.

This value is returned from some of the long time functions to indicate an error condition or failure. Variables of type time_t may be initialized to this value to indicate that they do not represent a valid time.

[Note]
This constant meant to replace the special (time_t)(-1) error value that is returned from various time_t handling functions such as mktime(). It is specifically used by the longtimetotime() function.

See also

_LONGTIME_ERROR, time_t, longtimetotime(), mktime().

5. Types

The following types are defined in the <stdtime.h> standard header.


5.1 Typedef longtime_t

Synopsis

    #include <stdtime.h>

    typedef integer-type  longtime_t;

Description

This is a signed integer type capable of holding long time values. It is guaranteed to be at least 64 bits in width (i.e., 63 bits plus a sign bit).

[Note]
The obvious implementation for longtime_t is type long long int, which is guaranteed to be at least 64 bits wide.

The longtime_t type represents a system time as an integral number of ticks elaped since the beginning of the long time epoch. Each tick is two nanoseconds in length. The epoch begins at {AD 2001-01-01 00:00:00.000 Z}.

Long time values represent dates across the range of {AD 1601-01-01 00:00:00 Z} to {AD 2401-01-01 00:00:00 Z} within the proleptic Gregorian calendar.

[Note]
The resolution of the longtime_t representation is fixed and is independent of the physical resolution of the system clock.

Implementations are obliged to provide system time values with a discernable physical resolution of at least one second.

The representation provides certain well-defined properties for long system time values:

  • Guaranteed subsecond resolution.
  • Guaranteed date range.
  • Predictable arithmetic behavior.
  • Reasonable and predictable storage requirements.

    The fixed resolution (2 nsec) was chosen so as to be as small as possible while at the same time providing a reasonably wide range of dates (about ±584 years, which covers at least two 400-year cycles of the Gregorian calendar). Other resolution/range combinations may be considered, but the choice must provide a reasonable balance between the two ends of the scale.

    The epoch start date (a.k.a. the "zero" date) was chosen because it was felt to be the most useful for the widest number of applications. Modeled after existing implementations, this epoch provides positive time values for all dates that are likely to occur in existing civil applications, specifically, the years AD 1601 through AD 2400.

    Other possible epoch start dates to consider include:

  • {1858-11-17 12:00:00.000 Z} - Modified Julian Day (MJD) zero.
  • {1900-01-01 00:00:00.000 Z} - zero year of the struct tm calendar type.
  • {1970-01-01 00:00:00.000 Z} - start of the Unix epoch.
  • It is implementation-defined whether or not the longtime_t type includes inserted leap seconds.

    [Note]
    There has been much debate on whether a standard time representation should encode leap seconds or not. This proposal leaves that up to the implementation, but requires it to specify clearly what it chooses to do.

    The simplest approach is to omit leap seconds, ignoring them entirely. However, it is recognized that some existing implementations go to the trouble of providing system times with inserted leap seconds. For these implementations, it might require a noticeable amount of overhead to compute the current system time with leap seconds removed. For this reason, the choice is left to the implementor. See Appendix B for further discussion.

    The _LONGTIME_HAS_LEAP_SECS macro indicates whether or not the implementation handles leap seconds.

    The extlongtime() and intlongtime() functions are provided in order to make it possible to construct portable and well-defined arithmetic time values across all implementations.

    See also

    _LONGTIME_HAS_LEAP_SECS, _LONGTIME_MAX, _LONGTIME_MIN, extlongtime(), getlongtime(), intlongtime().

    5.2 Structure calendar

    Synopsis

        #include <stdtime.h>
    
        struct calendar;
    

    Description

    This structure contains a broken-down calendar date.

    The contents of this structure are discussed in a separate proposal (see Proposal [P3] for more details.)


    5.3 Structure timezone

    Synopsis

        #include <stdtime.h>
    
        struct timezone;
    

    Description

    This structure contains timezone and Daylight Savings Time (DST) adjustment information.

    The contents of this structure are discussed in a separate proposal (see Proposal [P2] for more details.)


    6. Functions

    The following functions are used to manipulate long time values. They are declared in the <stdtime.h> standard header.


    6.1 Function extlongtime()

    Synopsis

        #include <stdtime.h>
    
        long long int  extlongtime(longtime_t t);
    

    Description

    Converts an internal long system time into a portable external long time value.

    [Note]
    This function performs the inverse operation of intlongtime().

    A portable external long time value is a signed integer at least 64 bits wide (63 bits plus a sign bit) representing the number of ticks since the start of the epoch without any inserted leap seconds. Each tick is two nanoseconds in duration, and the epoch begins at {2001-01-01 00:00:00.000 Z}. Such a value has a precise mathematical relationship to the number of years (yr), days (yday), seconds (sec), and nanoseconds (nsec) since the epoch start date (according to the proleptic Gregorian calendar):

        nd = 0;
        while (yr < 0)
        {
            nd -= 400*365 + 400/4 - 400/100 + 400/400;
            yr += 400;
        }
        nd +=   yr*365 + yr/4 - yr/100 + yr/400 + yday;
        ticks = (nd*24*60*60 + sec)*500_000_000 + nsec/2;
    

    (For example, the date {1970-01-01 00:00:00.000 Z} is 31 years before the start of the epoch, which is exactly 11,323 days. It is thus is represented by an external long time value of exactly -11,323 * 24 * 60 * 60 * 500,000,000 = -489,153,600,000,000,000 ticks.)

    Portable external long time values may be shared among different implementations, and a given value represents equivalent system times on all implementations.

    [Note]
    This requirement obliges implementations to recognize long time values produced by other systems in a portable manner.

    This requirement implicitly assumes that the most practical portable representation of system times is one that does not include inserted leap seconds, i.e., a representation that is a simple abstract count of fixed-duration ticks from the standard epoch date, without regard to UTC leap seconds. The example above of 1970-01-01, for instance, does not add any leap seconds inserted between 1972-06 and 2001-01.

    It is reasonable to assume that if an implementation supports leap seconds, then it is capable of determining how many leap seconds are inserted in a given system time value. (Presumably, this information is derived from a table of leap seconds maintained in some system library.)

    Argument t is a long time value to be converted from its internal representation into its portable external equivalent. If it specifies a future time (i.e., a long time value greater than the current system time), the results are implementation-defined.

    [Note]
    It is implementation-defined whether future long time values can contain inserted leap seconds. This is a long-standing problem with no easy solution. Leaving it up to the implementor allows the function to fail, or to provide a long time value with an estimated number of inserted leap seconds, or no extra inserted leap seconds at all, or some other value altogether. See Appendix B for further discussion.

    Returns

    If successful, a converted external long time value is returned.

    If the internal long time representation of the implementation is identical to the portable external representation, then the function simply returns the value of t unchanged.

    If an error occurs, a value equal to _LONGTIME_ERROR is returned.

    Examples

    The function below retrieves the current system time as a portable external long time value.

        #include <stdtime.h>
    
        long long ext_time()
        {
            // Get the current system time in portable external form
            return extlongtime(getlongtime());
        }
    

    The function below displays the current system time as the number of microseconds, seconds, days, and years since the beginning of the long time epoch.

        #include <stdio.h>
        #include <stdtime.h>
    
        void print_now()
        {
            longtime_t      t;
            long long int   x;
    
            // Get the current system time sans leap seconds
            t = getlongtime();
            x = extlongtime(t);
    
            // Display the current time in various resolutions
            printf("ticks:  %lld\n", x);
    
            x /= 1000000000/2 * 1000000;
            printf("usecs:  %lld\n", x);
    
            x /= 1000000;
            printf("secs:   %lld\n", x);
    
            x /= 24 * 60 * 60;
            printf("days:   %lld\n", x);
    
            x = x * 1000 / 3652425;
            printf("years:  %lld\n", x);
        }
    
    [Note]
    Portable code like that shown in the example above is possible because of the specific arithmetic properties defined for the portable external form of the longtime_t type.

    See also

    _LONGTIME_HAS_LEAP_SECS, intlongtime().

    6.2 Function getlongtime()

    Synopsis

        #include <stdtime.h>
    
        longtime_t  getlongtime(void);
    

    Description

    Determines the current system time.

    [Note]
    The value returned reflects the best precision obtainable by the implementation. Implementations are obliged to provide times with a resolution of at least one second.

    Returns

    If successful, the current system time is returned, having a value in the range [_LONGTIME_MIN_LONGTIME_MAX]. If the system time is not obtainable or if an error occurs, a value equal to _LONGTIME_ERROR is returned.

    Example

    The function below retrieves the current system time twice and returns the difference between the two times in microseconds.

        #include <stdtime.h>
    
        long int two_times()
        {
            longtime_t  t1;
            longtime_t  t2;
            long int    dif;
    
            // Get the system time twice
            t1 = getlongtime();
            process();
            t2 = getlongtime();
    
            // Return the time difference in usec
            dif = (long)(t2 - t1) * 2 / 1000;
            return dif;
        }
    

    See also

    _LONGTIME_ERROR, _LONGTIME_RESOLUTION, longtime_t, extlongtime(), intlongtime(), mklongtime(), time().

    6.3 Function intlongtime()

    Synopsis

        #include <stdtime.h>
    
        longtime_t  intlongtime(long long int t);
    

    Description

    Converts a portable external long system time into an internal long time value. (See function extlongtime() for a definition of portable external long time.)

    An internal long time value is the internal representation of the longtime_t type provided by the implementation.

    [Note]
    This function performs the inverse operation of extlongtime().

    Argument t is a long time value to be converted from its portable external representation into its internal equivalent. If it specifies a future time (i.e., a long time value greater than the current system time), the results are implementation-defined.

    Returns

    If successful, a converted internal long time value is returned.

    If the internal long time representation of the implementation is identical to the portable external representation, then the function simply returns the value of t unchanged.

    If an error occurs, a value equal to _LONGTIME_ERROR is returned.

    See also

    _LONGTIME_HAS_LEAP_SECS, extlongtime().

    6.4 Function longtimetotime()

    Synopsis

        #include <stdtime.h>
    
        time_t  longtimetotime(longtime_t t);
    

    Description

    Converts a longtime_t time value into its corresponding time_t value.

    Argument t is a long time value to be converted into its equivalent system time value. If the long time value cannot be converted, or the resulting converted value exceeds the representational limits of the time_t type, the function fails.

    [Note]
    Some implementations may provide a longtime_t representation with a range that exceeds the capabilities of the time_t type. Thus some valid long time values may not be able to be represented as regular system time values.

    Returns

    If successful, the function returns a time value, otherwise it returns a value equal to _TIME_ERROR.

    [Note]
    It is implementation-defined whether or not the value (time_t)(-1), which is used to indicate error when returned from the mktime() function, can result from the conversion of a valid longtime_t value. If it can, then presumably the implementation will choose a value for _TIME_ERROR that is not equal to -1.

    Example

    The function below retrieves the system time as a longtime_t value and converts it into a time_t value.

        #include <stdtime.h>
        #include <time.h>
    
        time_t time_get(void)
        {
            longtime_t  lt;
            time_t      t;
    
            // Get the current system time and convert it
            lt = getlongtime();
            t = longtimetotime(lt);
            return t;
        }
    

    See also

    _LONGTIME_ERROR, timetolongtime().

    6.5 Function mklongtime()

    Synopsis

        #include <stdtime.h>
    
        longtime_t  mklongtime(struct calendar *date, const struct timezone *zone);
    

    Description

    Normalizes a calendar date and converts it into its equivalent long system time value.

    [Note]
    This function provides the same functionality for longtime_t and calendar structure values as function mktime() does for time_t and tm structure values. It has a different interface, however.

    This function makes use of the calendar structure type, which is defined in another related proposal (see Proposal [P3]). If this proposed structure type is not adopted, this function can be replaced with a nearly equivalent definition which uses the existing tm structure type (see Appendix A for details).

    Argument date points to a broken-down calendar date object. The members of the calendar object are normalized by modifying their values to fall within their normal ranges, while maintaining the same date value represented by the calendar object (if possible). The values of the members of the calendar object are not constrained to fall within their normal ranges upon entry to the function, but will be so constrained upon returning from the function.

    The values of the following members of the structure are used to perform the conversion:

        cal_type
        cal_era
        cal_year
        cal_mon
        cal_mday
        cal_hour
        cal_min
        cal_sec
        cal_nsec
        cal_dsti
    

    The remaining members of the structure are ignored for the purposes of conversion.

    If the cal_type member of the calendar object specifies a calendar type that is not supported by the implementation, the function fails.

    If the cal_year member of the calendar object has a value equal to _CAL_YR_ERROR (indicating that the calendar date is erroneous), the function fails.

    Argument zone points to a timezone object containing timezone and DST adjustments that have been made to the date represented by the broken-down calendar date object. (This is typically equal to the cal_zone member of the calendar date object, but is not required to be.) The timezone and DST adjustments are applied in reverse (i.e., undone) on the calendar date before it is converted into a long time value. This argument can be null, in which case no timezone or DST adjustments are applied to the calendar date, i.e., the conversion is done with respect to GMT.

    If the normalized calendar date does not represent a meaningful date, or cannot be converted into a valid long time value after adjustments have been applied, the function fails.

    Returns

    If successful, the function returns the resulting long time value.

    On failure, the function returns a value equal to _LONGTIME_ERROR. In addition, if the calendar object cannot be normalized into a meaningful date, the cal_year member of the calendar object is set to a value equal to _CAL_YR_ERROR, and the remaining members of the calendar object have indeterminate values.

    Because it is possible for the function to successfully normalize the members of the calendar date object, but otherwise be unable to return a valid long time value (i.e., because the resulting date is not representable as a long time value), both the value returned by the function and the value of the cal_year member should be examined to determine the exact nature of the failure.

    [Notes]
    It is entirely possible that some broken-down calendar date values are not representable as valid longtime_t values because they fall outside the range of system time values supported by the implementation. In such cases, an error is returned.

    Examples

    The function below converts a calendar date into its corresponding long time value.

        #include <stdio.h>
        #include <stdtime.h>
    
        longtime_t date_to_longtime(const struct calendar *date)
        {
            struct calendar     d2;
            longtime_t          t;
    
            // Print the calendar date
            printf("%04d-%02d-%02d %02d:%02d:%02d.%03ld",
                date->cal_year, date->cal_mon, date->cal_mday,
                date->cal_hour, date->cal_min, date->cal_sec,
                date->cal_nsec/1000000);
    
            // Convert the calendar date into a long time
            d2 = *date;
            t = mklongtime(&d2, d2.cal_zone);
            printf("-> %lld\n", (long long)t);
            return t;
        }
    

    The following code fragment determines if a particular {year,mon,mday} combination constitutes a valid calendar date.

        #include <stdbool.h>
        #include <stdtime.h>
    
        bool check_date(int yr, int mon, int day)
        {
            struct calendar    date;
    
            // Set the date of a new calendar object
            initcalendar(&date, "");
            date.cal_year = yr;
            date.cal_mon =  mon;
            date.cal_mday = day;
    
            // Verify that the date is already normalized
            if (mklongtime(&date, NULL) == _LONGTIME_ERROR)
                return false;
            if (date.cal_year != yr  ||
                date.cal_mon  != mon  ||
                date.cal_mday != day)
                return false;
            return true;
        }
    

    The following code fragment converts the current system into a broken-down calendar date and then back into a system time, and then determines the difference between the before and after values.

        #include <stdio.h>
        #include <stdtime.h>
    
        void convert_and_compare(void)
        {
            longtime_t          now;
            longtime_t          then;
            struct calendar     date;
    
            // Get the current system time
            now = getlongtime();
    
            // Convert the time into a calendar date
            initcalendar(&date, "");
            setcalendartime(&date, NULL, now);
    
            // Convert the date back into a system time
            then = mklongtime(&date, date.cal_zone);
    
            // Display the difference between the two system times
            printf("Difference: %lld sec\n", (long long int)(then - now));
        }
    

    The function below prints the long time value corresponding to the date {1776-07-04 12:00:00 Z}, and also determines the day of the week that date falls on (within the proleptic Gregorian calendar).

        #include <stdio.h>
        #include <stdtime.h>
        #include <string.h>
    
        void time_1776()
        {
            struct calendar     date;
            longtime_t          t;
    
            // Set the calendar date
            initcalendar(&date, "");
    
            date.cal_era =  _CAL_ERA_COMMON;
            date.cal_year = 1776;
            date.cal_mon =  7;
            date.cal_mday = 4;
            date.cal_hour = 12;
    
            // Convert the calendar date into a long time
            t = mklongtime(&date, date.cal_zone);
            printf("04 Jul 1776: %lld\n", (long long)t);
    
            // Print the day of the week for the date
            calendarformat(buf, sizeof(buf), "%a", &date);
            printf("04 Jul 1776: %s\n", buf);
        }
    

    See also

    _CAL_YR_ERROR, _LONGTIME_ERROR, longtime_t, setcalendartime(), mktime(), Proposal [P2], Proposal [P3].

    6.6 Function setcalendartime()

    Synopsis

        #include <stdtime.h>
    
        int  setcalendartime(struct calendar *date, const struct timezone *zone,
                 longtime_t t);
    

    Description

    Converts a long system time value into a calendar date relative to a given timezone, after undoing the effects of the previously applied timezone.

    [Note]
    This function provides the same functionality for longtime_t and calendar structure values as functions gmtime() and localtime() do for time_t and tm structure values. It has a different interface, however.

    This function makes use of the calendar structure type, which is defined in another related proposal (see Proposal [P3]). If this proposed structure type is not adopted, this function can be replaced with a nearly equivalent definition which uses the existing tm structure type (see Appendix A for details).

    Argument t is a long time value to be converted. If t is equal to _LONGTIME_ERROR or cannot be converted into a meaningful broken-down calendar date, the function fails.

    Argument date points to a broken-down calendar date object, If member cal_zone is not null, the timezone offset and DST adjustment previously applied to the calendar object are undone (reversed), and the members of the calendar object are modified accordingly. Then the members of the calendar object are modified with appropriate values resulting from the conversion of long time value t into a broken-down calendar date, with adjustments made for the specified timezone zone.

    Argument zone points to a timezone object, which specifies the appropriate timezone and DST adjustments to apply to the converted date. This argument can be null, in which case no timezone offset or DST adjustments are made, i.e., the date conversion is made with respect to the GMT timezone.

    Returns

    If successful, the function returns zero. On failure, the function returns -1 after setting member cal_year of the calendar date object to a value equal to _CAL_YR_ERROR.

    Example

    The function below formats and prints a long time value for the local timezone.

        #include <stdio.h>
        #include <stdtime.h>
    
        void print_date(longtime_t t)
        {
            struct timezone     tz;
            struct calendar     date;
            char                buf[40];
    
            // Get the local timezone
            inittimezone(&tz, "");
    
            // Convert the long time into a calendar date
            if (setcalendartime(&date, &tz, t) != 0)
            {
                printf("Cannot convert time: %lld\n", (long long)t);
                return;
            }
    
            // Format the calendar date and print it
            calendarformat(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &date);
            printf("%lld -> %s\n", (long long)t, buf);
        }
    

    See also

    _LONGTIME_ERROR, longtime_t, gmtime(), localtime(), mklongtime(), Proposal [P2], Proposal [P3].

    6.7 Function timetolongtime()

    Synopsis

        #include <stdtime.h>
    
        longtime_t  timetolongtime(time_t t);
    

    Description

    Converts a time_t time value into its corresponding longtime_t value.

    Argument t is a system time value to be converted into its equivalent long time value. If the time value cannot be converted or is equal to _TIME_ERROR, or if the resulting long time value exceeds the representational limits of the longtime_t type (i.e., if the value is less than _LONGTIME_MIN or greater than _LONGTIME_MAX), the function fails.

    [Note]
    Some implementations may provide a time_t representation with a range that exceeds the capabilities of the longtime_t type. Thus some valid system time values may not be able to be represented as long time values.

    Returns

    If successful, the function returns a long time value, otherwise it returns a value equal to _LONGTIME_ERROR.

    [Note]
    It is implementation-defined whether or not the value (time_t)(-1), which is used to indicate error when returned from the mktime() function, is convertible into a valid longtime_t value. If it is, then presumably the implementation will choose a value for _TIME_ERROR that is not equal to -1.

    Example

    The function below retrieves the system time as a time_t value and converts it into a long time value.

        #include <stdtime.h>
        #include <time.h>
    
        longtime_t get_time(void)
        {
            time_t      t;
            longtime_t  lt;
    
            // Get the current system time and convert it
            time(&t);
            lt = timetolongtime(t);
            return lt;
        }
    

    See also

    _LONGTIME_ERROR, longtimetotime().

    7. Prior Art

    The following items describe various existing implementations that provide system time capabilities and the various differences and problems with them.

    1. POSIX defines the standard time_t type to be a signed integer representing the number of seconds since {1970-01-01 00:00:00 Z}, with no leap seconds. It is usually implemented as a 32-bit signed integer. Negative values are not valid times.

      The shortcomings of this format have been discussed at length, most notably:

      Some POSIX implementations have begun using a 64-bit integer type to represent extended system times (the datatype typically having a name like time64_t). While this extends the range of representable dates considerably (to over 292,000 years), it still suffers from a one-second resolution limitation.

    2. POSIX defines a struct timeval type, which represents system times as seconds from the start of the epoch, as well as microseconds. Values of this type are returned by the gettimeofday() system call.

      The obvious drawback to this implementation is the inability to perform direct arithmetic operations on structure objects. Such operations are generally simulated with extra functions provided by the user.

      While this structure type extends the resolution of system time values, it does not extend the range of such values, so the POSIX end-of-epoch problem still remains.

    3. POSIX defines a struct timespec type, which represents system times as seconds from the start of the epoch, as well as nanoseconds.

      This structure type is similar to the timeval structure type, and suffers from the same drawbacks as it does.

    4. Microsoft Win32 provides filesystem times (file timestamps) as 64-bit values representing the number of 100-nanosecond ticks since {1601-01-01 00:00:00 Z} (as defined in the proleptic Gregorian calendar), sans leap seconds.

    5. Digital VMS implements system times as 64-bit values representing the number of 10-nanosecond ticks since {1858-11-17 12:00:00 Z}, which is Modified Julian Day (MJD) day zero and Julian day number 2,400,000.5.

    6. The ANSI COBOL-90 standard specifies date and time functions for manipulating values representing dates since {1601-01-01 00:00:00 Z}, (which is a Monday in the proleptic Gregorian calendar), with a resolution of one second. Dates prior to this are not handled. Arithmetic computations involving such date/time values require at most 12 decimal digits, or 40 bits.

    7. The IBM S/360 and ESA architectures implement a system clock using an unsigned 64-bit binary tick counter representing time elapsed since {1900-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 AD 2047.

    8. IBM CICS implements a 15-digit packed decimal ABSTIME value, representing the number of milliseconds since {1900-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 AD 2047 on S/360 systems).

    9. 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 {2001-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.

    10. The Java standard runtime library provides a Date class that is capable of storing system times to the nearest millisecond. Internally, system times are stored as 64-bit signed integers, conceivably capable of representing dates across a range of millions of years.

      Java also provides TimeZone and Calendar utility classes that are integrated with the Date class, providing the ability to convert between system times and calendar dates.


    Appendix A - Other Proposed Types

    This proposal is written in concert with two other proposals which define the timezone structure (Proposal [P2]) and the calendar structure (Proposal [P3]).

    Some of the functions defined in this proposal make use of these structure types. If these other proposed types are not adopted by ISO C, the functions defined in this proposal can be replaced with functions taking equivalent arguments of types that are already defined in the standard <time.h> header.

    Specifically, these functions:

        int         setcalendartime(struct calendar *date,
                        const struct timezone *zone, longtime_t t);
    
        longtime_t  mklongtime(struct calendar *date, const struct timezone *zone);
    
    can be replaced with the functionally equivalent definitions:
        int         setcalendartime(struct tm *date, const struct timezone *zone,
                        longtime_t t);
    
        longtime_t  mklongtime(struct tm *date, const struct timezone *zone);
    
    or with the even less equivalent definitions:
        int         gmlongtime(longtime_t t, struct tm *date);
    
        int         setcalendartime(struct tm *date, longtime_t t);
    
        longtime_t  mklongtime(struct tm *date);
    

    Appendix B - Leap Seconds

    The handling of leap seconds is a long-standing problem.

    By far the easiest approach is to ignore leap seconds altogether. This makes conversions of long time values into calendar dates as simple as possible. It also completely avoids the problem of dealing with leap seconds for future time values.


    External Representation

    By requiring an external long time representation that does not include inserted leap seconds, it makes it possible for all implementations to share long time values in a consistent and predictable manner. (The usual problems of word size, endianness, and so forth notwithstanding, of course.) Thus the need for the extlongtime() and intlongtime() functions.

    The flip side of this line of reasoning is that these functions are probably not necessary for most programming applications, at least those that do not need to share long time values with other implementations having different system time representations. Provided that long time values stay within the confines of the implementation, it is probably reasonable to assume that conversions to/from external long time representation (i.e., leap second adjustments) do not need ever to be performed.


    Alternative getlongtime()

    It is possible to modify this proposal so that the getlongtime() function returns the current system time with no leap seconds, i.e., any inserted leap seconds that the implementation may be capable of accounting for are removed from the returned long time value.

    This alteration makes it simpler to write code that is completely unaware of leap seconds; however, it adds an extra burden for implementations that are able to handle leap seconds. Code similar to the following fragment becomes necessary:

            longtime_t  t;
    
            t = getlongtime();          // Get time without leap secs
        #if _LONGTIME_HAS_LEAP_SECS
            t = intlongtime(t);         // Add missing leap secs
        #endif
            ...
    

    The drawback to this approach is that it breaks the reasonable assumption that getlongtime() retrieves time values based on the implementation's notion of native internal system time, which are related to the times used by the implementation's external storage system (filesystem), e.g., modification times for I/O streams (files), etc. Having getlongtime() return a time representation that differs from the native internal time encoding would place an unreasonable burden on programmers to rectify values between the two representations, at least on all systems that recognize leap seconds.

    It is for this reason that getlongtime() is specified as returning the implementation's native notion of system time, and not some altered version of it.


    Source Code

    Proof-of-concept source code is contained in these files:


    Acknowledgements

    The author wishes to express his gratitude to those who provided comments, suggestions, and criticism on this proposal.

    Further discussion can be found on the comp.std.c newsgroup.


    References

    [1]  Programming Language C - ISO/IEC 9899:1999 - International Standard
    1999, ISO/EIC.
    International Standards Organization, http://www.iso.ch.
    Available from the American National Standards Institute (ANSI) site at http://www.ansi.org.
    The ISO C (C99) standard.

    [2]  Portable Operating System Interface for Computer Environments - IEEE Standard
    IEEE Std 1003.1-1998, ISBN 1-55937-003-3.
    Sep 1988, The Institute of Electrical and Electronics Engineers (IEEE), Inc.
    The standard POSIX definition.

    [3]  Open Group Single Unix Specification
    The Open Group.
    Available online at http://www.opengroup.org
    The standard Unix definition.

    [4]  Microsoft Windows API Reference
    1995, Microsoft, http://www.microsoft.com.
    The API reference is available online at http://www.msdn.com
    The Win32 definition.

    [5]  COBOL Standards
    http://www.cobolstandards.com
    Information about the current ISO COBOL standard, and links to the ISO/IEC JTC1/SC22/WG4 COBOL standards group.

    [6]  Java API Specification
    2004, Sun Microsystems, http://www.sun.com.
    http://java.sun.com/docs,
    http://java.sun.com/j2se/1.5/docs/api/java/util/Date.html
    The Java standard library reference documentation. In particular, the java.util.Date class.

    Related Proposals

    [P1]  Additional Constraints on time_t
    2002, David R. Tribble.
    http://david.tribble.com/text/c0xtimet.htm
    A related proposal describing improvements to be made to the existing standard time_t library type.

    [P2]  ISO C 200X Proposal: Timezone Functions
    2004, David R. Tribble.
    http://david.tribble.com/text/c0xtimezone.html
    A related proposal specifying types and functions for managing timezone and Daylight Savings Time information.

    [P3]  ISO C 200X Proposal: Calendar Library Functions
    2002, David R. Tribble.
    http://david.tribble.com/text/c0xcalendar.html
    A related proposal specifying types and functions for manipulating calendar date objects.

    [P4]  Proposal for an ISO C and C++ Extended-Range Time Type
    Sep 1999, David R. Tribble.
    http://david.tribble.com/text/c0xtime.htm
    An earlier proposal (since abandoned) for an extended-range long time type. Some of the concepts in the current proposal were drawn from this earlier proposal.

    [P5]  libtai
    D. J. Bernstein.
    http://cr.yp.to/libtai.html
    A library for storing and manipulating dates and times defined in terms of TAI, the international atomic real time standard.

    [P6]  Proposed new <time.h> for ISO C 200X
    2002, Markus Kuhn.
    http://www.cl.cam.ac.uk/~mgk25/time/c
    A proposal for a properly engineered replacement for the <time.h> API.

    [P7]  Modernized <time.h> API for ISO C
    2000, Paul Eggert
    http://www.twinsun.com/tz/timeapi.html
    A proposed extension to the standard C application programming interface (API) for time.

    [P8]  Proposal for Thread-Safe Time Zone Information
    Jonathan Lennox.
    http://www.cl.cam.ac.uk/~mgk25/time/c/proposal-lennox.txt
    A proposal for thread-safe time zone information, providing a set of extensions to ISO C99 and IEEE POSIX (200x).

    Revision History

    1.3, 2004-08-01
    Renamed function getcalendar() to initcalendar().
    Renamed function gettimezone() to inittimezone().
    Split function extlongtime() into two functions, adding a new intlongtime() function.

    1.2, 2004-07-18
    Renamed constant _LONGTIME_TICKS_PER_SEC to _LONGTIME_RESOLUTION.
    Renamed function adjleapsecs() to extlongtime(). Also introduced the concept of portable external time.
    Changed the date argument of function mklongtime() to be non-const, and to be normalized prior to conversion to a long time value.
    Renamed function locallongtime() to setcalendartime(), to agree with the calendar structure proposal.

    1.1, 2004-06-29
    Made some minor corrections and clarifications.
    Defined the precise mathematical relationship between adjusted long times without inserted leap seconds and the number of ticks from the beginning of the epoch. Added "Appendix B".

    1.0, 2004-06-25
    Initial revision.

    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/c0xlongtime.html.

    Author's email: david@tribble.com.
    Author's home page: http://david.tribble.com.