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: http://david.tribble.com Telephone Number: *************** Sponsor: ________________________________________ Revision: 1.3, 2004-08-01 Supersedes: 1.2, 2004-07-18 Proposal Category: __ Editorial change/non-normative contribution __ Correction X_ New feature __ Addition to obsolescent feature list __ Addition to Future Directions __ Other (please specify) _________________________________ Area of Standard Affected: __ Environment __ Language __ Preprocessor X_ Library X_ Macro/typedef/tag name __ Variable X_ Function X_ Header __ Other (please specify) ______________________________ Prior Art: Many operating system provide functions for retrieving the system time with a resolution greater than one second, including POSIX, Microsoft DOS and Win32, Digital VMS, Apple Mac/OS, IBM OS/390, IBM OS/400, etc. Target Audience: _____________________________________________ Related Documents (if any): None. Proposal Attached: X_ Yes __ No, but what's your interest? Abstract: The addition of an extended time type and supporting functions to provide enhanced resolution of system time values. Also the addition of a new standard library header that contains these types and functions. |
And an astronomer said, Master, what of Time? 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 (finer) than one second, and it is possible that even a resolution as large 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.
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 (longtime_t)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 that represent valid times within the implementation, i.e., this constant compares less than _LONGTIME_MIN or greater than _LONGTIME_MAX.
This value is returned from some of the long time functions to indicate an error condition or failure. Variables of type longtime_t may be initialized to this value to indicate that they do not represent a valid time.
[Note]
This constant is similar to the special (time_t)(-1) error value that is returned from various time_t handling functions such as mktime(). By being defined as a standard macro name, though, it is more portable than an explicit hard-coded value.
#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 leap seconds into the longtime_t type (i.e., if certain distinct long time values represent leap seconds that have been inserted into the standard calendar), or to zero (false) if it does not.
[Note]
There has been much debate on whether a standard time representation should encode leap seconds or not. This proposal leaves that up to the implementation, but requires it to specify clearly what it chooses to do.It is recognized that some existing implementations go to the trouble of providing system times with inserted leap seconds. For these implementations, it might require an unreasonable amount of overhead to compute the current system time with leap seconds removed. For this reason, the choice is left to the implementor. See Appendix B for further discussion.
In order to deal with disparate implementations, some providing leap seconds and others not, this proposal mandates the presence of this constant as well as the extlongtime() and intlongtime() functions.
#include <stdtime.h> #define _LONGTIME_MAX (longtime_t)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 (longtime_t) values greater than this constant do not represent meaningful times within the implementation.
This constant must not be less than +6,311,390,399,999,999,999, which is the long time value representing the date that is one tick before {AD 2401-01-01 00:00:00 Z}, without leap seconds.
#include <stdtime.h> #define _LONGTIME_MIN (longtime_t)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 (longtime_t) values less than this constant do not represent meaningful times within the implementation.
This constant must not be greater than -6,311,390,400,000,000,000, which is the long time value representing the date {AD 1601-01-01 00:00:00 Z}, without leap seconds, in the proleptic Gregorian calendar.
#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 500,000,000, which is the smallest possible resolution representable by the longtime_t type (i.e., a tick duration of two nanoseconds).
#include <stdtime.h> #define _TIME_ERROR (time_t)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 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 capable of holding long time values. It is guaranteed to be at least 64 bits in width (i.e., 63 bits plus a sign bit).
[Note]
The obvious implementation for longtime_t is type long long int, which is guaranteed to be at least 64 bits wide.
The longtime_t type represents a system time as an integral number of ticks elaped since the beginning of the long time epoch. Each tick is two nanoseconds in length. The epoch begins at {AD 2001-01-01 00:00:00.000 Z}.
Long time values represent dates across the range of {AD 1601-01-01 00:00:00 Z} to {AD 2401-01-01 00:00:00 Z} within the proleptic Gregorian calendar.
[Note]
The resolution of the longtime_t representation is fixed and is independent of the physical resolution of the system clock.Implementations are obliged to provide system time values with a discernable physical resolution of at least one second.
The representation provides certain well-defined properties for long system time values:
Guaranteed subsecond resolution. Guaranteed date range. Predictable arithmetic behavior. Reasonable and predictable storage requirements. The fixed resolution (2 nsec) was chosen so as to be as small as possible while at the same time providing a reasonably wide range of dates (about ±584 years, which covers at least two 400-year cycles of the Gregorian calendar). Other resolution/range combinations may be considered, but the choice must provide a reasonable balance between the two ends of the scale.
The epoch start date (a.k.a. the "zero" date) was chosen because it was felt to be the most useful for the widest number of applications. Modeled after existing implementations, this epoch provides positive time values for all dates that are likely to occur in existing civil applications, specifically, the years AD 1601 through AD 2400.
Other possible epoch start dates to consider include:
{1858-11-17 12:00:00.000 Z} - Modified Julian Day (MJD) zero. {1900-01-01 00:00:00.000 Z} - zero year of the struct tm calendar type. {1970-01-01 00:00:00.000 Z} - start of the Unix epoch.
It is implementation-defined whether or not the longtime_t type includes inserted leap seconds.
[Note]
There has been much debate on whether a standard time representation should encode leap seconds or not. This proposal leaves that up to the implementation, but requires it to specify clearly what it chooses to do.The simplest approach is to omit leap seconds, ignoring them entirely. However, it is recognized that some existing implementations go to the trouble of providing system times with inserted leap seconds. For these implementations, it might require a noticeable amount of overhead to compute the current system time with leap seconds removed. For this reason, the choice is left to the implementor. See Appendix B for further discussion.
The _LONGTIME_HAS_LEAP_SECS macro indicates whether or not the implementation handles leap seconds.
The extlongtime() and intlongtime() functions are provided in order to make it possible to construct portable and well-defined arithmetic time values across all implementations.
#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().
A portable external long time value is a signed integer at least 64 bits wide (63 bits plus a sign bit) representing the number of ticks since the start of the epoch without any inserted leap seconds. Each tick is two nanoseconds in duration, and the epoch begins at {2001-01-01 00:00:00.000 Z}. Such a value has a precise mathematical relationship to the number of years (yr), days (yday), seconds (sec), and nanoseconds (nsec) since the epoch start date (according to the proleptic Gregorian calendar):
nd = 0; while (yr < 0) { nd -= 400*365 + 400/4 - 400/100 + 400/400; yr += 400; } nd += yr*365 + yr/4 - yr/100 + yr/400 + yday; ticks = (nd*24*60*60 + sec)*500_000_000 + nsec/2;
(For example, the date {1970-01-01 00:00:00.000 Z} is 31 years before the start of the epoch, which is exactly 11,323 days. It is thus is represented by an external long time value of exactly -11,323 * 24 * 60 * 60 * 500,000,000 = -489,153,600,000,000,000 ticks.)
Portable external long time values may be shared among different implementations, and a given value represents equivalent system times on all implementations.
[Note]
This requirement obliges implementations to recognize long time values produced by other systems in a portable manner.This requirement implicitly assumes that the most practical portable representation of system times is one that does not include inserted leap seconds, i.e., a representation that is a simple abstract count of fixed-duration ticks from the standard epoch date, without regard to UTC leap seconds. The example above of 1970-01-01, for instance, does not add any leap seconds inserted between 1972-06 and 2001-01.
It is reasonable to assume that if an implementation supports leap seconds, then it is capable of determining how many leap seconds are inserted in a given system time value. (Presumably, this information is derived from a table of leap seconds maintained in some system library.)
Argument t is a long time value to be converted from its internal representation into its portable external equivalent. If it specifies a future time (i.e., a long time value greater than the current system time), the results are implementation-defined.
[Note]
It is implementation-defined whether future long time values can contain inserted leap seconds. This is a long-standing problem with no easy solution. Leaving it up to the implementor allows the function to fail, or to provide a long time value with an estimated number of inserted leap seconds, or no extra inserted leap seconds at all, or some other value altogether. See Appendix B for further discussion.
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); x /= 1000000000/2 * 1000000; printf("usecs: %lld\n", x); x /= 1000000; printf("secs: %lld\n", x); x /= 24 * 60 * 60; printf("days: %lld\n", x); x = x * 1000 / 3652425; printf("years: %lld\n", x); }
[Note]
Portable code like that shown in the example above is possible because of the specific arithmetic properties defined for the portable external form of the longtime_t type.
#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 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) * 2 / 1000; 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.
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 error when returned from the mktime() function, can result from the conversion of a valid longtime_t value. If it can, then presumably the implementation will choose a value for _TIME_ERROR that is not equal to -1.
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 GMT.
If the normalized calendar date does not represent a meaningful date, or cannot be converted into a valid long time value after adjustments have been applied, the function fails.
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, after undoing the effects of the previously applied timezone.
[Note]
This function provides the same functionality for longtime_t and calendar structure values as functions gmtime() and localtime() do for time_t and tm structure values. It has a different interface, however.This function makes use of the calendar structure type, which is defined in another related proposal (see Proposal [P3]). If this proposed structure type is not adopted, this function can be replaced with a nearly equivalent definition which uses the existing tm structure type (see Appendix A for details).
Argument t is a long time value to be converted. If t is equal to _LONGTIME_ERROR or cannot be converted into a meaningful broken-down calendar date, the function fails.
Argument date points to a broken-down calendar date object, If member cal_zone is not null, the timezone offset and DST adjustment previously applied to the calendar object are undone (reversed), and the members of the calendar object are modified accordingly. Then the members of the calendar object are modified with appropriate values resulting from the conversion of long time value t into a broken-down calendar date, with adjustments made for the specified timezone zone.
Argument zone points to a timezone object, which specifies the appropriate timezone and DST adjustments to apply to the converted date. This argument can be null, in which case no timezone offset or DST adjustments are made, i.e., the date conversion is made with respect to the GMT timezone.
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 error when returned from the mktime() function, is convertible into a valid longtime_t value. If it is, then presumably the implementation will choose a value for _TIME_ERROR that is not equal to -1.
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.
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 as it does.
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 even less equivalent definitions:
int gmlongtime(longtime_t t, struct tm *date); int setcalendartime(struct tm *date, longtime_t t); longtime_t mklongtime(struct tm *date);
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 leap seconds, it makes it possible for all implementations to share long time values in a consistent and predictable manner. (The usual problems of word size, endianness, and so forth notwithstanding, of course.) Thus the need for the extlongtime() and intlongtime() functions.
The flip side of this line of reasoning is that these functions are probably not necessary for most programming applications, at least those that do not need to share long time values with other implementations having different system time representations. Provided that long time values stay within the confines of the implementation, it is probably reasonable to assume that conversions to/from external long time representation (i.e., leap second adjustments) do not need ever to be performed.
It is possible to modify this proposal so that the getlongtime() function returns the current system time with no leap seconds, i.e., any inserted leap seconds that the implementation may be capable of accounting for are removed from the returned long time value.
This alteration makes it simpler to write code that is completely unaware of leap seconds; however, it adds an extra burden for implementations that are able to handle leap seconds. Code similar to the following fragment becomes necessary:
longtime_t t; t = getlongtime(); // Get time without leap secs #if _LONGTIME_HAS_LEAP_SECS t = intlongtime(t); // Add missing leap secs #endif ...
The drawback to this approach is that it breaks the reasonable assumption that getlongtime() retrieves time values based on the implementation's notion of native internal system time, which are related to the times used by the implementation's external storage system (filesystem), e.g., modification times for I/O streams (files), etc. Having getlongtime() return a time representation that differs from the native internal time encoding would place an unreasonable burden on programmers to rectify values between the two representations, at least on all systems that recognize leap seconds.
It is for this reason that getlongtime() is specified as returning the implementation's native notion of system time, and not some altered version of it.
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.