ISO C 201X Proposal:
Long Time Type


By David R. Tribble
david@tribble.com

Revision 2.1, 2009-08-22

 

 Contents

 

 Cover Sheet

 
                     Document Number:  WG14 N___/X3J11 __-___

                           C201X Revision Proposal
                           =======================

 Title:              Long Time Type
 Author:             David R. Tribble
 Author Affiliation: Self
 Postal Address:     ******
                     USA
 E-mail Address:     david@tribble.com
 Web URL:            david.tribble.com
 Telephone Number:   ***************
 Sponsor:            ________________________________________
 Revision:           2.1, 2009-08-22
 Supersedes:         2.0, 2005-06-01
 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.
 

 

 1. Introduction

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.

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 than one second, and it is possible that even a resolution as small 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 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. In many implementations, −1 is in fact a valid time_t value, rendering it useless as a unique error value.

  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 Solutions

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

  1. Types
    A standard primitive datatype is used to represent extended-precision system times. Leaving the type as implementation-defined is to be avoided.
  2. Semantics
    The new type should have well-defined representational properties, such as minimum range and precision. The type should have well-defined arithmetic properties as well.
  3. Space efficiency
    The representational form should be reasonably compact. 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. The chosen format should also lend itself to efficient arithmetic conversions to and from other formats.
  4. Range
    The new type should be capable of representing a 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 time.
  5. Resolution
    The resolution should be fine enough to be useful to most civil and commercial applications. I/O device latency, network I/O latency, and system event timer applications typically involve time calculations on the scale of nanoseconds, give or take a few orders of magnitude. At worst, the resolution should be no coarser than milliseconds. The chosen resolution should also lend itself to efficient arithmetic conversions to and from other formats.
  6. Compatibility
    The new type should be convertible to and from the existing time_t and struct tm types.
  7. Leap seconds
    The new type should address the problem of representing (or not representing) leap seconds.
  8. Safety
    The new functions should be thread-safe. In particular, none of the functions should rely on modifiable global data.
  9. Portability
    The new system time representation should be portable, so that such values can be stored externally and interchanged between different implementations.

The diagram below illustrates the various types and their relationships within this proposal and its related proposals.

Conversion functions
  C99 Library   Proposed
(a) localtime() (c) setcalendartime()
  gmtime()   mklongtime()
  mktime()    
    (d) calendarformat()
(b) strftime()   calendarscanf()
  strptime()   POSIX, not ISO C    
    (e) inittimezone()
      mktimezonename()
       
    (f) longtimetotime()
      timetolongtime()

 

 2. Definitions

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.

componentized date
Same as calendar date.

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 beginning time of the range of dates that can be represented by a given time encoding format. Also used to mean the entire range of dates covered by the encoding.

external time
Same as portable external time.

leap second
An extra second periodically inserted or removed from 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. Existing practice to date has been to insert a leap second 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 UTC}.

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 in 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 UTC (Z) 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 UTC 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.

 

 4. Constants

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

    _LONGTIME_ERROR
    _LONGTIME_MAX
    _LONGTIME_MIN
    _LONGTIME_RESOLUTION
    _LONGTIME_TICKS_PER_SEC
    _TIME_ERROR

Each constant is described in detail below.


4.1 Constant _LONGTIME_ERROR

Synopsis

    #include <stdtime.h>

    #define _LONGTIME_ERROR  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 which 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 valid times.

[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.

This constant also has the virtue of being defined with a value outside the range of meaningful long time values. In contrast, it is not clear whether or not −1 is a meaningful time_t value in ISO C.

See also

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


4.2 Constant _LONGTIME_MAX

Synopsis

    #include <stdtime.h>

    #define _LONGTIME_MAX  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 values greater than this constant do not represent meaningful times within the implementation.

This constant must not be less than +6_776_803_840_072_089_599 (229*60*60*24*146_097 - 1), which is the long time value representing the date that is one tick before {AD 2401-01-01 00:00:00 Z} sans leap seconds.

[Note]
The longtime_t representation is designed to span dates to at least 400 years after the start of the epoch, i.e., up to AD 2401.

See also

_LONGTIME_ERROR, _LONGTIME_MIN, longtime_t, getlongtime().


4.3 Constant _LONGTIME_MIN

Synopsis

    #include <stdtime.h>

    #define _LONGTIME_MIN  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 values less than this constant do not represent meaningful times within the implementation.

This constant must not be greater than −6_776_803_840_072_089_600 (−229*60*60*24*146_097), which is the long time value representing the date {AD 1601-01-01 00:00:00 Z} sans leap seconds in the proleptic Gregorian calendar.

[Note]
The longtime_t representation is designed to span dates back to at least 400 years before the start of the epoch, i.e., back to AD 1601.

See also

_LONGTIME_ERROR, _LONGTIME_MAX, longtime_t, getlongtime().


4.4 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 the _LONGTIME_TICKS_PER_SEC macro, which is the smallest possible resolution representable by the longtime_t type.

See also

CLOCKS_PER_SEC, _LONGTIME_TICKS_PER_SEC, longtime_t, getlongtime().


4.5 Constant _LONGTIME_TICKS_PER_SEC

Synopsis

    #include <stdtime.h>

    #define _LONGTIME_TICKS_PER_SEC  536870912L

Description

This preprocessor macro is defined as an expression of type long int which specifies the number of ticks per second encoded by the longtime_t type, which is exactly 229 (equal to 1L << 29).

[Note]
This constant is not strictly necessary, but is provided as a convenience.

See also

_LONGTIME_RESOLUTION, longtime_t.


4.6 Constant _TIME_ERROR

Synopsis

    #include <stdtime.h>

    #define _TIME_ERROR  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 is 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.

    typedef longtime_t
    struct calendar
    struct timezone

Each type is described in detail below.


5.1 Typedef longtime_t

Synopsis

    #include <stdtime.h>

    typedef integer-type  longtime_t;

Description

This is a signed integer type at least 64 bits wide, capable of holding long time values.

[Note]
The obvious implementation type for longtime_t is long long int.

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 2−29 seconds (approximately 1.863 nanoseconds) in duration.

The long time epoch is centered at the date {AD 2001-01-01 00:00:00.000 UTC} (MJD 51,910), which is represented by a long time value of zero. Long time values are signed, and span dates from {AD 1601-01-01 00:00:00.000 UTC} to {AD 2400-12-31 23:59:59.999 UTC} 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 representational form of long times is essentially a fixed-point binary tick counter composed of a sign bit, a 34-bit whole seconds count, and a 29-bit subsecond fraction count. Each tick is 2−29 seconds in length (approximately 1.863 nanoseconds), which means that there are 536,870,912 (229) long time ticks per second.

    The longtime_t representation is loosely modeled after the NTP time representation. See the Prior Art section for more details.

    The resolution was chosen so as to provide the most efficient encoding for times and which is easily convertible to other time representations with minimal computational overhead. Conversion to whole seconds, for example, requires a simple binary shift. Conversions to decimal subsecond formats, such as milliseconds or microseconds, require a single division or a multiplication and a shift.

    The fixed resolution was chosen to be as small as possible (on the scale of a nanosecond) while at the same time providing a reasonably wide range of dates (about ±544 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. For programming convenience, the encoding should also not be wider than 64 bits.

    The chosen tick duration length provides sufficiently resolution for applications dealing with things like system event timers, I/O device latencies, network I/O delays, and the like. At the same time, the chosen range is sufficiently wide enough for civil applications dealing with things like file system timestamps, future product expiration dates, existing long-term mortgages, insurance policies, etc.

    The epoch center 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 from AD 1601 through AD 2400.

    Other possible epoch start dates that might be considered include:

    •  {1858-11-17 00: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, and the NTP epoch start date.
    •  {1970-01-01 00:00:00.000 Z}
    – Start of the POSIX epoch.

    Whatever start date is chosen, it must be reasonably close to the current date, so that it results in the most useful range of dates for the widest number of applications.

    The longtime_t type includes accumulated leap second insertions and deletions. In other words, a long time value directly corresponds to a UTC time (which is essentially a TAI time with a certain number of leap second adjustments).

    [Note]
    There has been much debate on whether a standard time representation should encode leap seconds or not. This proposal mandates that long times include the inserted or deleted leap seconds for any given valid date within the epoch. This provides a common time representation with a direct correlation to the predominant standard worldwide civil time encoding, Coordinated Universal Time (UTC).

    See Appendix B for further discussion.

    Portability and Semantics

    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 plus any inserted or deleted leap seconds. Each tick is 2−29 seconds (approximately 1.863 nanoseconds) in duration, and the epoch begins at {2001-01-01 00:00:00.000 Z} (MJD 51,910). 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).

    Long time values bear the following relationships to the standard Gregorian calendar:

    See also

    _LONGTIME_MAX, _LONGTIME_MIN, getlongtime(),


    5.2 Structure calendar

    Synopsis

        #include <stdtime.h>
    
        struct calendar;
    

    Description

    This structure contains a componentized calendar date.

    The contents of this type 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 type are discussed in a separate proposal. (see Proposal [P2] for more details).

     

     6. Functions

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

        getlongtime()
        longtimeleapsecs()
        longtimetotime()
        mklongtime()
        setcalendartime()
        timetolongtime()
    

    Each function is described in detail below.


    6.1 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 system 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) * 1000000 >> 29);
            return dif;
        }
    

    See also

    _LONGTIME_ERROR, _LONGTIME_RESOLUTION, _LONGTIME_TICKS_PER_SEC, longtime_t, mklongtime(), time().


    6.2 Function longtimeleapsecs()

    Synopsis

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

    Description

    Determines the total accumulated number of inserted and deleted leap seconds for a specified long time value t.

    Argument t is a long time value within the epoch. If it does not designate a valid long time, the function fails.

    Returns

    If t is a valid long time value, the function returns the total number of leap seconds accumulated between the first UTC leap second occurrence and the specified long time, i.e., the count of inserted leap seconds minus the count of deleted leap seconds since {1972-06-30 23:59:60 UTC}, up to and including the specified long time value.

    If t designates a date prior to the occurrence of the first UTC leap second, the function returns zero.

    If t designates a future time, the returned value is implementation-defined, but is not less than the total number of leap seconds accumulated up to the current system time.

    [Note]
    In other words, implementations are not obliged to handle future leap seconds, but can simply return the value equal to the value returned for the current time, which is longtimeleapsecs(getlongtime()).

    If t is not a valid long time value within the implementation (i.e., if it is less than _LONGTIME_MIN or greater than _LONGTIME_MAX, or equal to _LONGTIME_ERROR), the function fails and returns INT_MIN.

    [Note]
    INT_MIN must be used as an error indication instead of −1 because the accumulated number of leap seconds could, in theory, be negative.

    Examples

    The following function prints the total number of leaps seconds accumulated up to the present time.

        #include <stdio.h>
        #include <stdtime.h>
    
        void print_leapsecs()
        {
            int     secs;
    
            // Determine the accumulated leap seconds for the current time
            secs = longtimeleapsecs(getlongtime());
            printf("Total leap seconds to date: %d\n", secs);
        }
    

    The following function prints the number of accumulated leap seconds between two dates.

        #include <stdio.h>
        #include <stdtime.h>
    
        void print_secsdiff(longtime_t t1, longtime_t t2)
        {
            long long   secs;
            int         leaps;
    
            // Determine the difference between the two times
            secs = (t1 - t2) >> 29;
            leaps = longtimeleapsecs(t1) - longtimeleapsecs(t2);
            printf("Difference: %+lld sec, includes %+d leap sec\n", secs, leaps);
        }
    

    See also

    INT_MIN, _LONGTIME_ERROR, _LONGTIME_MAX, _LONGTIME_MIN


    6.3 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 an 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 provide a value for _TIME_ERROR that is not equal to −1.

    Similarly, it is implementation-defined whether or not negative time_t values can result from the conversion of valid longtime_t values.

    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.4 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 componentized 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
        cal_leapsec
    

    If the cal_type member of the calendar object designates a calendric system different from the standard Gregorian calendar, other members may be used to perform the conversion. Otherwise, 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 invalid), the function fails.

    The cal_leapsec member specifies the number of leap seconds to be included in the resulting converted long time value. If this member is INT_MIN, the number of accumulated leap seconds is determined from the long time value after the conversion takes place, based on historical leap second insertions and deletions. If the member is zero, no leap seconds are included in the resulting value. Any other value specifies an exact number of leap seconds to add to the resulting long time value.

    Argument zone points to a timezone object containing timezone and DST adjustments that have been made to the date represented by the componentized 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 the UTC timezone.

    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 componentized 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 componentized 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.5 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.

    [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 componentized calendar date, the function fails.

    Argument date points to a componentized calendar date object. If its cal_zone member 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 componentized 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 UTC timezone.

    The cal_leapsec member of date is set to the total number of accumulated leap seconds within long time value t. If the number of leap seconds cannot be determined, the member is set to INT_MIN.

    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.6 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 an error when returned from the mktime() function, is convertible into a valid longtime_t value. If it is, then presumably the implementation will provide a value for _TIME_ERROR that is not equal to −1.

    Similarly, it is implementation-defined whether or not negative time_t values are convertible into meaningful longtime_t values.

    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. The Network Time Protocol (NTP), defines a time representation for synchronizing systems across the internet. It is defined as a 128-bit tick counter composed of a 64-bit whole seconds count and a 64-bit subsecond fractions count. Each tick is therefore 2−64 seconds (approx. 542.1 x 10−18 sec). The NTP epoch begins at {1900-01-01 00:00:00 Z}. The seconds count is based on UTC time, and includes inserted and deleted leap second adjustments.

      In actual use, the format physically transmitted over network lines is a 64-bit value composed of the lower unsigned 32 bits of the seconds count and the upper 32 bits of the subsecond fractions count, i.e., the middle 64 bits of the 128-bit counter. This "32.32" representation thus provides a time encoding with a resolution of 2−32 seconds (about 232.8 picoseconds) over a range of 136 years. This format encodes an epoch spanning the date range [1900-01-012036-02-06], and will run out of bits (i.e., overflow its 64th high-order bit) at {2036-02-07 06:28:16 Z}.

      The proposed longtime_t is very similar to the NTP representation, but provides a wider date range with a less precise subsecond resolution.

    2. 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.

    3. 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.

    4. 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.

    5. 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. The encoding is conceivably capable of encoding dates spanning a range of ±29,227 years.
    6. Microsoft MS-DOS provides filesystem times (file timestamps) as a 32-bit value composed of a 16-bit date and 16-bit time, each of which are composed of separate bitfields. The encoding is only accurate to the nearest two-second interval. The encoding does not account for timezones, effectively encoding a time only for the local timezone. The epoch spans 128 years, starting at {1980-01-01 00:00:00} and ending at {2107-12-31 23:59:58}.
    7. Digital VMS implements system times as 64-bit values representing the number of 10-nanosecond ticks since {1858-11-17 00:00:00 Z}, which is Modified Julian Day (MJD) day zero (and Julian Day 2,400,000.5). The encoding is conceivably capable of encoding dates spanning a range of ±2,922 years.
    8. 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.
    9. The IBM S/390 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.
    10. 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 conceivably 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/390 systems).
    11. 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 spanning a range of ±292,277,024 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 not-quite-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.

    However, there are systems that have a real need to properly account for leap seconds. Therefore the more prudent approach is to require the new long time type to include inserted and removed leap seconds. Long time values can then be exchanged between different implementations in a consistent and predictable manner (the usual problems of word size, endianness, and so forth notwithstanding, of course).

    Requiring the long time representation to include leap seconds complicates arithmetic calculations involving such value, however. For this reason, the longtimeleapsecs() function is provided to remove the accumulated leap seconds from a given long time value, thus allowing a program to perform simpler arithmetic operations directly on the resulting "nonleap" time value.

    Alternate Implementations

    It must also be mentioned that an implementation may choose to ignore the requirements of including leap seconds within the longtime_t type. Such implementations presumably do not interact with other implementations, or do not interchange long time values with other implementations. Even if they do interchange such values, they probably do not care whether leap seconds are properly accounted for, and thus are capable of ignoring leap second errors entirely.

    Such non-leap-second long time implementations have the benefit of being better suited for direct arithmetic calculations, having an exact mathematical relation to proleptic Gregorian calendar dates:

        days = 0;
        while (year < 0)
        {
            days -= 146_097;    // = 400*365 + 400/4 - 400/100 + 400/400
            year += 400;
        }
        days +=  year*365 + year/4 - year/100 + year/400 + yday;
        ticks =  (days * 24*60*60 + sec) * 536_870_912;
        ticks += nsec * 536_870_912/1_000_000_000;
    

     

     References
    [1]  Programming Language C - ISO/IEC 9899:1999 - International Standard
    1999, ISO/EIC.
    International Standards Organization, www.iso.ch.
    Available from the American National Standards Institute (ANSI) site at 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 API specification.

    [3]  Open Group Single Unix Specification
    The Open Group.
    Available online at www.opengroup.org
    The standard Unix API specification.

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

    [5]  COBOL Standards
    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, www.sun.com.
    java.sun.com/docs,
    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.

    [7]  Critical and Significant Dates
    Updated 2005, J. R. Stockton,
    www.merlyn.demon.co.uk/critdate.htm
    A comprehesive list of significant dates for computer systems, including epoch limits for various date encodings.

    [8]  Leap Seconds
    Updated May 1999, Time Service Dept., U.S. Naval Observatory.
    tycho.usno.navy.mil/leapsec.html
    A description of leap seconds and how they relate to the UTC and TAI time systems.

    [9]  Wikipedia: Time
    http://en.wikipedia.org/wiki/Time.

    [10]  Wikipedia: System time
    http://en.wikipedia.org/wiki/System_time.

    [11]  Wikipedia: Calendar
    http://en.wikipedia.org/wiki/Calendar.

    [12]  Wikipedia: Calendar Date
    http://en.wikipedia.org/wiki/Calendar_date.

    [13]  Wikipedia: Leap Seconds
    http://en.wikipedia.org/wiki/Leap_second.

    [14]  Wikipedia: Time Zone
    http://en.wikipedia.org/wiki/Time_zone.

    [15]  Wikipedia: Daylight Saving Time
    http://en.wikipedia.org/wiki/Daylight_saving_time.

     

     Related Proposals
    [P1]  Additional Constraints on time_t
    2002, David R. Tribble.
    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 201X Proposal: Timezone Functions
    2004, David R. Tribble.
    david.tribble.com/text/c0xtimezone.html
    A related proposal specifying types and functions for managing timezone and Daylight Savings Time information.

    [P3]  ISO C 201X Proposal: Calendar Library Functions
    2002, David R. Tribble.
    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.
    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.
    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.
    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
    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.
    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).

     

     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.

     

     Revision History
    2.1, 2009-08-22
    Updated the title from an ISO 200X to an ISO 201X proposal.
    Added the "Solutions" section, which includes a "big picture" diagram.
    Added more intra-document links.
    Improved section headers and paging.
    Added references to Wikipedia articles.

    2.0, 2006-03-11
    This draft version is incomplete.
    Removed the separate external representation, replacing it with a single representational form which includes accumulated leap seconds and tracks UTC time (a la NTP).
    Removed functions extlongtime() and intlongtime().
    Removed macro _LONGTIME_HAS_LEAP_SECS.
    Added function longtimeleapsecs().
    Added member cal_leapsec to the calendar structure.

    1.4, 2005-05-13
    This revision is available at: david.tribble.com/text/c0xlongtime_1_4.html.
    Redefined the representation of the longtime_t type to have a binary-based tick length of 2−29 seconds instead of a decimal-based 2-nanosecond length.
    Added macro _LONGTIME_TICKS_PER_SEC.
    Added a table of example dates and long time values.
    Added a "Prior Art" section about the Network Time Protocol (NTP).

    1.3, 2004-08-01
    This revision is available at: david.tribble.com/text/c0xlongtime_1_3.html.
    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.