ISO C 200X Proposal: Long Time Type
By David R. Tribble |
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: david.tribble.com Telephone Number: *************** Sponsor: ________________________________________ Revision: 1.4, 2005-05-13 Supersedes: 1.3, 2004-08-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. |
And an astronomer said, Master, what of Time? 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.
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.
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.
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.
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.
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.
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.
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.
The following terms are used in this proposal. Some of the terms may need to be added to the ISO C standard.
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.
The following constants are defined in the <stdtime.h> standard header.
#include <stdtime.h> #define _LONGTIME_ERROR integer-expression
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.
#include <stdtime.h> #define _LONGTIME_HAS_LEAP_SECS integer-expression
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 or deletes leap seconds in the longtime_t type (i.e., if certain distinct long time values represent leap seconds that have been inserted or deleted from 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 or deleted 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.
#include <stdtime.h> #define _LONGTIME_MAX integer-expression
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.
#include <stdtime.h> #define _LONGTIME_MIN integer-expression
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.
#include <stdtime.h> #define _LONGTIME_RESOLUTION integer-expression
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.
#include <stdtime.h> #define _LONGTIME_TICKS_PER_SEC 536870912L
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.
#include <stdtime.h> #define _TIME_ERROR integer-expression
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.
The following types are defined in the <stdtime.h> standard header.
#include <stdtime.h> typedef integer-type longtime_t;
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 Z}, 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 Z} to {AD 2401-01-01 00:00:00.000 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 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.
It is implementation-defined whether or not the longtime_t type includes inserted or deleted 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 or deleted 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.
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 Appendix B for further discussion.
#include <stdtime.h> struct calendar;
This structure contains a broken-down calendar date.
The contents of this structure are discussed in a separate proposal (see Proposal [P3] for more details.)
#include <stdtime.h> struct timezone;
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.)
The following functions are used to manipulate long time values. They are declared in the <stdtime.h> standard header.
#include <stdtime.h> long long int extlongtime(longtime_t t);
Converts an internal long system time into a portable external long time value.
[Note]
This function performs the inverse operation of intlongtime().
Argument t is a long time value to be converted from its internal representation into its portable external equivalent.
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 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}. 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 -= 146_097; // = 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)*536_870_912; ticks += nsec*536_870_912/1_000_000_000;
(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 represented by an external long time value of exactly -11_323*24*60*60*536_870_912 = -525_224_678_680_166_400 ticks.)
External long time values bear the following relationships to the standard Gregorian calendar:
Portable external long time values may be shared among different implementations, and a given value represents equivalent system times in 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 or deleted 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 or deleted for a specific system time value. (Presumably, this information is derived from a table of leap seconds maintained in some system library.)
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.
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); printf("usecs: %lld\n", (x & (1L << 29)-1) * 1000000 >> 29)); x >>= 29; printf("secs: %lld\n", x); x /= 24 * 60 * 60; printf("days: %lld\n", x); x = x * 10000 / 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.
External long time values have well-defined arithmetic properties, making it possible to define the following conversion macros:
#include <stdtime.h> #define yyd_to_extlongtime(yr,yday) \ (((longtime_t)((yr) - 2001)*10000/3652425 + (yday))*86400 << 29) #define hms_to_extlongtime(hr,min,sec) \ ((((longtime_t)(hr)*60 + (min))*60 + (sec)) << 29) #define usec_to_extlongtime(usec) \ (((longtime_t)(usec) << 29)/1000000) #define make_extlongtime(yr,yday,hr,min,sec,usec) \ (yyd_to_extlongtime(yr, yday) \ + hms_to_extlongtime(hr, min, sec) \ + usec_to_extlongtime(usec))
The following table lists some specific dates and their corresponding external long time values:
Date | External Long Time Value | Description |
---|---|---|
1601-01-01 00:00:00.000 Z | -6_776_803_840_072_089_600 | _LONGTIME_MIN |
1858-11-17 00:00:00.000 Z | -2_405_606_028_528_844_800 | MJD 0.0 |
1900-01-01 00:00:00.000 Z | -1_711_166_510_333_952_000 | NTP and struct tm zero date |
1901-01-01 00:00:00.000 Z | -1_694_235_749_253_120_000 | ZD - 100 years |
1904-01-01 00:00:00.000 Z | -1_643_443_466_010_624_000 | MacOS epoch start |
1969-12-31 23:59:59.000 Z | -525_224_679_217_037_312 | POSIX (time_t)(-1) |
1970-01-01 00:00:00.000 Z | -525_224_678_680_166_400 | POSIX epoch start |
1972-06-30 23:59:59.000 Z | -482_967_354_985_152_512 | |
1972-07-01 00:00:00.000 Z | -482_967_354_448_281_600 | |
1980-01-01 00:00:00.000 | -355_824_296_578_252_800 | MS-DOS epoch start |
2000-01-01 00:00:00.000 Z | -16_977_146_727_628_800 | ZD - 1 year |
2000-12-31 00:00:00.000 Z | -46_385_646_796_800 | ZD - 1 day |
2000-12-31 23:59:59.999 Z | -1 | ZD - 1 tick |
2001-01-01 00:00:00.000 Z | +0 | ZD, epoch zero date |
2001-01-01 00:00:01.000 Z | +536_870_912 | ZD + 1 sec |
2001-01-02 00:00:00.000 Z | +46_385_646_796_800 | ZD + 1 day |
2002-01-01 00:00:00.000 Z | +16_930_761_080_832_000 | ZD + 1 year |
2036-02-07 06:28:16.000 Z | +594_676_498_879_741_952 | NTP 64-bit roll-over |
2038-01-19 03:14:07.000 Z | +627_696_825_389_809_664 | POSIX epoch end |
2042-09-17 23:53:47.000 Z | +706_638_743_049_601_024 | IBM OS/370 epoch end |
2100-01-01 00:00:00.000 Z | +1_677_258_602_525_491_200 | |
2101-01-01 00:00:00.000 Z | +1_694_189_363_606_323_200 | ZD + 100 years |
2107-12-31 23:59:58.000 | +1_812_797_461_391_998_976 | MS-DOS epoch end |
2400-12-31 23:59:59.999 Z | +6_776_803_840_072_089_599 | _LONGTIME_MAX |
#include <stdtime.h> longtime_t getlongtime(void);
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.
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.
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; }
#include <stdtime.h> longtime_t intlongtime(long long int t);
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.
[Note]
It is implementation-defined whether future long time values can contain inserted or deleted 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 leap seconds, or no extra leap seconds at all, or some other value altogether. See Appendix B for further discussion.
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.
#include <stdtime.h> time_t longtimetotime(longtime_t t);
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.
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.
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; }
#include <stdtime.h> longtime_t mklongtime(struct calendar *date, const struct timezone *zone);
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 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.
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.
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); }
#include <stdtime.h> int setcalendartime(struct calendar *date, const struct timezone *zone, longtime_t t);
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 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 UTC timezone.
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.
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); }
#include <stdtime.h> longtime_t timetolongtime(time_t t);
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.
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.
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; }
The following items describe various existing implementations that provide system time capabilities and the various differences and problems with them.
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-01, 2036-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 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.
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.
This structure type is similar to the timeval structure type, and suffers from the same drawbacks.
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.
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);
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.
By requiring an external long time representation that does not include inserted or deleted 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 reasonable to assume that conversions to/from external long time representation (i.e., leap second adjustments) do not ever need to be performed.
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 or deleted 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.
Proof-of-concept source code is contained in these files:
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.
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.