Pivotal ANSI-like time functions

OVERVIEW

The ANSI C library has several core time functions that do not give correct results past the year 2038. The functions below are replacements of two kinds, 1) drop-in replacements, and 2) replacement functions that require re-factoring your code by changing all use of time_t to time64_t. Please note that on Windows you should use the _gmtime64() and _localtime64() functions.

Also note that a certain consistency of the use of daylight savings in different countries is assumed with the _localtime64_r function. You are advised to look closely at the implementation.

The function prototypes are:

1)

2)

 

API DOCUMENTATION

struct tm *pivotal_localtime_r (const time_t * now, const time_t * t, struct tm *p);
Calculates broken-down time representation for the local timezone: year/month/day/hour/minute/second.

Arguments:

     now: Current time, or if passed as NULL does a system call to get the current time.
     t: Requested time in UTC seconds.
     p: User-suppled storage area.

Return value:

     p is populated and returned back to the caller.

struct tm *pivotal_gmtime_r (const time_t * now, const time_t * t, struct tm *p);
Calculates broken down time representation for the timezone GMT, year/month/day/hour/minute/second.

Arguments:

     now: Current time, or if passed as NULL does a system call to get the current time.
     t: Requested time in UTC seconds.
     p: User-suppled storage area.

Return value:

     p is populated and returned back to the caller.

time64_t pivot_time_t (const time_t * now, time64_t *t);
Takes a 64-bit time that may have had its top 32-bits set to zero, and adjusts it so that it is in the range explained in the notes below. You can use pivot_time_t() to convert any time that may be incorrect. pivot_time_t() returns its argument unchanged if either now is NULL or sizeof(time_t) is not 4.

Arguments:

     now: Current time, or if passed as NULL does a system call to get the current time.
     t: Requested time in UTC seconds.

Return value:

     Corrected 64-bit time.

time64_t mktime64 (struct tm *t);
Converts broken down time representation to 64-bit time in UTC, seconds since 1970-01-01 00:00:00.

Arguments:

     t: Broken-down time.

Return value:

     64-bit UTC time in seconds.

struct tm *localtime64_r (const time64_t *t, struct tm *p);
64-bit version; calculates broken down time representation for the local timezone: year/month/day/hour/minute/second.

Arguments:

     now: Current time, or if passed as NULL does a system call to get the current time.
     t: Requested time in UTC seconds.
     p: User-suppled storage area.

Return value:

     p is populated and returned back to the caller.

struct tm *gmtime64_r (const time64_t *t, struct tm *p);
64-bit version; calculates broken down time representation for the timezone GMT, year/month/day/hour/minute/second.

Arguments:

     now: Current time, or if passed as NULL does a system call to get the current time.
     t: Requested time in UTC seconds.
     p: User-suppled storage area.

Return value:

     p is populated and returned back to the caller.

 

HOW THIS WORKS

Although 31-bits (32 less the sign bit) cannot represent the full range of dates from 1970 past 2038, it can easily represent the full range of days between the present time and 231 seconds in the past or future. If we assume that a program is only interested in dates 68 years in the past or future, then it easy to calculate that a date that could be either 1917 or 2053 (due to the missing high bits) should be the latter value and not the former.

Range of correct results for different classes of function
 
32-bit ANSI functions32-bit pivot functions64-bit functions
1800    
1900    
1901    
1940    
1941  Present Time - 68 
1969    
1970    
2009  Present Time 
2037    
2038    
2077  Present Time + 68 
2078    
2200    

In order to support this, the only change to the standard ANSI C time functions is the inclusion of the current time as an additional parameter: "now" in the above prototypes. This is called the pivot point - hence the name of this library.

This is most useful where one is unable to re-factor all one's code to use time64_t instead of a 32-bit time value.

It is intended that you pass 'now' as the current time (as previously retrieved with a call such as time(&now)). In this case, pivotal_gmtime_r() returns the correct broken down time in the range of
    now - 2147483648 seconds
through to
    now + 2147483647 seconds

For example, on 10-Jan-2008, pivotal_gmtime_r() will return the correct broken down time format for any time in the range 23-Dec-1939 through 29-Jan-2076.

For all values of 'now' before Jan-10-2009 and after Jan-19-2038, pivotal_gmtime_r() will return the correct broken down time format for any time 01-Jan-1970 through to 07-Feb-2106.

In other words, if, for example, pivotal_gmtime_r() is used in a program that needs to convert time values of 25 years into the future and 68 years in the past, the program will operate as expected until the year 2106-25=2081. This will be true even on 32-bit systems.

Note that "Jan-10-2009" is the date of the authoring of this code.

Programmers who have available to them 64-bit integer values (long long type) can use gmtime64_r() instead, which correctly converts the time even on 32-bit systems. Whether you have 64-bit integer values will depend on the operating system.

Both functions are 64-bit clean and should work as expected on 64-bit systems.

The localtime() equivalent functions do both a POSIX localtime_r() and gmtime_r() and work out the time zone offset from their difference. This is inefficient but gives the correct timezone offset and daylight savings time adjustments.

Note that none of these functions handle leap seconds.

Changes:
    10-Jan-2009  v4.0:  Make localtime work in 28-year cycle.
    06-Feb-2005  v3.0:  Some optimizations.
			mktime() no-longer zeros tm struct.