ISO C 201X Proposal:
Long Time Type
By David R. Tribble
david@tribble.com
Revision 2.1, 2009-08-22
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. |
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 goals guide the design of a new long time type for ISO C that is meant to remedy the shortcomings listed above.
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() |
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.
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.
#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_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.
typedef longtime_t struct calendar struct timezone
Each type is described in detail below.
#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 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.
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:
#include <stdtime.h> struct calendar;
This structure contains a componentized calendar date.
The contents of this type 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 type are discussed in a separate proposal. (see Proposal [P2] for more details).
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.
#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> int longtimeleapsecs(longtime_t t);
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.
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.
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); }
#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 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.
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.
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); }
#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 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.
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 proposed longtime_t is very similar to the NTP representation, but provides a wider date range with a less precise subsecond resolution.
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.
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.
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;
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.