Will `gmtime()` report seconds as 60 when in a leap second? - c

I have a server running in TZ=UTC and I have code like this:
time_t t = time(NULL);
struct tm tm;
gmtime_r(&t, &tm);
The question is will tm.tm_sec == 60 when the server is within a leap second?
For example, if I were in the following time span:
1998-12-31T23:59:60.00 - 915 148 800.00
1998-12-31T23:59:60.25 - 915 148 800.25
1998-12-31T23:59:60.50 - 915 148 800.50
1998-12-31T23:59:60.75 - 915 148 800.75
1999-01-01T00:00:00.00 - 915 148 800.00
would gmtime() return tm == 1998-12-31T23:59:60 for time_t = 915148800 and, once out of the leap second, return tm == 1999-01-01T00:00:00 for the same time_t?

The short answer is, no, practically speaking gmtime_r will never fill in tm_sec with 60. This is unfortunate, but unavoidable.
The fundamental problem is that time_t is, per the Posix standard, a count of seconds since 1970-01-01 UTC assuming no leap seconds.
During the most recent leap second, the progression was like this:
1483228799 2016-12-31 23:59:59
1483228800 2017-01-01 00:00:00
Yes, there should have been a leap second, 23:59:60, in there. But there's no possible time_t value in between 1483228799 and 1483228800.
I know of two ways for a gmtime variant to return a time ending in :60:
You can run your OS clock on something other than UTC, typically TAI or TAI-10, and use the so-called "right" timezones to convert to UTC (or local time) for display. See this web page for some discussion on this.
You can use clock_gettime() and define a new clkid value, perhaps CLOCK_UTC, which gets around the time_t problem by using deliberately nonnormalized struct timespec values when necessary. For example, the way to get a time value in between 1483228799 and 1483228800 is to set tv_sec to 1483228799 and tv_nsec to 1000000000. See this web page for more details.
Way #1 works pretty well, but nobody uses it because nobody wants to run their kernel clock on anything other than the UTC it's supposed to be. (You end up having problems with things like filesystem timestamps, and programs like tar that embed those timestamps.)
Way #2 is a beautiful idea, IMO, but to my knowledge it has never been implemented in a released OS. (As it happens, I have a working implementation for Linux, but I haven't released my work yet.) For way #2 to work, you need a new gmtime variant, perhaps gmtime_ts_r, which accepts a struct timespec instead of a time_t.
Addendum: I just reread your question title. You asked, "Will gmtime() report 60 for seconds when the server is on a Leap Second?" We could answer that by saying "yes, but", with the disclaimer that since most servers can't represent time during a leap second properly, they're never "on" a leap second.
Addendum 2: I forgot to mention that scheme #1 seems to work better for local times -- that is, when you're calling one of the localtime variants -- than for UTC times and gmtime. Clearly the conversions performed by localtime are affected by the setting of the TZ environment variable, but it's not so clear that TZ has any effect on gmtime. I've observed that some gmtime implementations are influenced by TZ and can therefore do leap seconds in accordance with the "right" zones, and some cannot. In particular, the gmtime in GNU glibc seems to pay attention to the leap second information in a "right" zone if TZ specifies one, whereas the gmtime in the IANA tzcode distribution does not.

The question is will tm.tm_sec == 60 when the server is within a leap second?
No. On a typical UNIX system, time_t counts the number of non-leap seconds since the epoch (1970-01-01 00:00:00 GMT). As such, converting a time_t to a struct tm will always yield a time structure with a tm_sec value between 0 and 59.
Ignoring leap seconds in time_t reckoning makes it possible to convert a time_t to a human-readable date/time without full knowledge of all leap seconds before that time. It also makes it possible to unambiguously convert time_t values in the future; including leap seconds would make that impossible, as the presence of a leap second isn't known beyond 6 months in the future.
There are a few ways that UNIX and UNIX-like systems tend to handle leap seconds. Most typically, either:
One time_t value is repeated for the leap second. (This is the result of a strict interpretation of standards, but will cause many applications to malfunction, as it appears that time has gone backwards.)
System time is run slightly slower for some time surrounding the leap second to "smear" the leap second across a wider period. (This solution has been adopted by many large cloud platforms, including Google and Amazon. It avoids any local clock inconsistencies, at the expense of leaving the affected systems up to half a second out of sync with UTC for the duration.)
The system time is set to TAI. Since this doesn't include leap seconds, no leap second handling is necessary. (This is rare, as it will leave the system several seconds out of sync with UTC systems, which make up most of the world. But it may be a viable option for systems which have little to no contact with the outside world, and hence have no way of learning of upcoming leap seconds.)
The system is completely unaware of leap seconds, but its NTP client will correct the clock after the leap second leaves the system's clock one second off from the correct time. (This is what Windows does.)

POSIX specifies the relationship between time_t "Seconds Since the Epoch" values and broken-down (struct tm) time exactly in a way that does not admit leap seconds or TAI, so essentially (up to some ambiguity about what should happen near leap seconds), POSIX time_t values are UT1, not UTC, and the results of gmtime reflect that. There is really no way to adapt or change this that's compatible with existing specifications and existing software based on them.
The right way forward is almost certainly a mix of what Google has done with leap second smearing and a standardized formula for converting back and forth between "smeared UTC" and "actual UTC" times (and thus also TAI) in the 24-hour window around a leap second and APIs to perform these conversions.

There is absolutely no easy answer to this. For there to be a 60 second when there is a leap second, you require 1) something in the OS to know there is a leap second due, and 2) for the C library that your using to also know about the leap second, and do something with it.
An awful lot of OSes and libraries don't.
The best I've found is modern versions of Linux kernel teamed up with gpsd and ntpd, using a GPS receiver as the time reference. GPS advertises leap seconds in its system datastream, and gpsd, ntpd and the Linux kernel can maintain CLOCK_TAI whilst the leap second is happening, and the system clock is correct too. I don't know if glibc does a sensible thing with the leap second.
On other UNIXes your mileage will vary. Considerably.
Windows is a ******* disaster area. For example the DateTime class in C# doesn't know about historical leap seconds. The system clock will jump 1 second next time a network time update is received.

I read this at www.cplusplus.com about gmtime: "Uses the value pointed by timer to fill a tm structure with the values that represent the corresponding time, expressed as a UTC time (i.e., the time at the GMT timezone)".
So there's a contradiction. UTC has seconds of absolutely constant length and therefore needs leap seconds, while GMT has days of exactly 86,400 seconds of very slightly varying lengths. gmtime() cannot at the same time work in UTC and GMT.
When we are told that gmtime () returns "UTC assuming no leap seconds" I would assume this means GMT. Which would mean there are no leap seconds recorded, and it would mean that the time slowly diverges from UTC, until the difference is about 0.9 seconds and a leap second is added in UTC, but not in GMT. That's easy to handle for developers but not quite accurate.
One alternative is to have constant seconds, until you are close to a leap second, and then adjust maybe 1000 seconds around that leap second in length. It's also easy to handle, 100% accurate most of the time, and 0.1% error in the length of a second sometimes for 1000 second.
And the second alternative is to have constant seconds, have leap seconds, and then forget them. So gmtime() will return the same second twice in a row, going from x seconds 0 nanoseconds to x seconds 999999999 nanoseconds, then again from x seconds 0 nanoseconds to x seconds 999999999 nanoseconds, then to x+1 seconds. Which will cause trouble.
Of course having another clock that will return exact UTC including leap seconds, with exactly accurate seconds, would be useful. To translate "seconds since epoch" to year, month, day, hours, minutes, seconds requires knowledge of all leap seconds since epoch (or before epoch if you handle times before that). And a clock that will return guaranteed exact GMT with no leap seconds and seconds that are almost but not quite constant time.

Another angle to their problem is having a library that 'know so' about leap seconds. Most libraries don't and so the answers you get from functions like gmtime are, strictly speaking, inaccurate during a leap second. Also time difference calculations often produce inaccurate results straddling a leap second. For example the value for time_t given to you at the same UTC time yesterday is exactly 86400 seconds smaller than today's value, even if there was actually a leap second.
The astronomy community has solved this. Here is the SOFA Library that has proper time routines within. See their manual (PDF), the section on timescales. If made part of your software and kept up to date (a new version is needed for each new leap second) you have accurate time calculations, conversions and display.

Related

C: Using time() function to estimate seconds elapsed since specific date

I am having a bit of trouble here. I am tasked with finding how many seconds have elapsed since January 1, 1970 using the time function. It must include now = time (NULL)
My issue is I was never given a solid explanation as to how the time function works, so I have no idea how to get it to tell answer the problem I am being asked.
A call to time(NULL) returns the number of seconds elapsed since the UNIX epoch, or January 1, 1970 UTC. This is the singular purpose of this function.
If you want to know how it works, look up the documentation on it.

Should the POSIX 'CLOCK_REALTIME' clock be referenced to the UTC epoch?

I'm using the POSIX clock_gettime(CLOCK_REALTIME, &curr_time) to get the current time of the CLOCK_REALTIME clock. I understand the difference between CLOCK_REALTIME and CLOCK_MONOTONIC. However, what I don't know is the epoch associated with CLOCK_REALTIME.
My first guess was that it would be the elapsed seconds/nanoseconds since the UTC epoch. However, the value that I'm getting is ~180000 seconds which is ~50 hours. Obviously not the time elapsed since the UTC epoch.
So my question: what is CLOCK_REALTIME referenced against? Reboot? Platform specfic? UTC and I'm just doing something wrong?
Thanks,
CLOCK_REALTIME is tied to the Unix epoch ("UTC epoch" is the same thing but is not the correct way to name it. It's just the Unix epoch in the UTC timezone).
Try this code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
int main(void)
{
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
printf("%ld\n", ts.tv_sec);
exit(0);
}
The POSIX specification for clock_gettime defines the zero point for CLOCK_REALTIME as "the Epoch":
All implementations shall support a clock_id of CLOCK_REALTIME as defined in <time.h>. This clock represents the clock measuring real time for the system. For this clock, the values returned by clock_gettime() and specified by clock_settime() represent the amount of time (in seconds and nanoseconds) since the Epoch.
It defines the term "the Epoch" in its "General Concepts" chapter, section 4.16:
A Coordinated Universal Time name (specified in terms of seconds (tm_sec), minutes (tm_min), hours (tm_hour), days since January 1 of the year (tm_yday), and calendar year minus 1900 (tm_year)) is related to a time represented as seconds since the Epoch, according to the expression below.
If the year is <1970 or the value is negative, the relationship is undefined. If the year is >=1970 and the value is non-negative, the value is related to a Coordinated Universal Time name according to the C-language expression, where tm_sec, tm_min, tm_hour, tm_yday, and tm_year are all integer types:
tm_sec + tm_min*60 + tm_hour*3600 + tm_yday*86400 +
(tm_year-70)*31536000 + ((tm_year-69)/4)*86400 -
((tm_year-1)/100)*86400 + ((tm_year+299)/400)*86400
If you put that expression on one side of an equation, and zero on the other, and add in the usual constraints on the range of day-of-year, hour, minute, second, you find that "the Epoch" corresponds to ISO calendar date 1970-01-01T00:00:00Z. Why they don't just say that, I don't know.
The phenomenon you observed, where clock_gettime(CLOCK_REALTIME) produced a value of roughly 50 hours since 1970-01-01T00:00:00Z, is accounted for in the very next sentence of POSIX chapter 4:
The relationship between the actual time of day and the current value for seconds since the Epoch is unspecified.
Which is to say, POSIX does not require your computer's clock to be accurate.

How to set (or correct for) timezone & DST in c (time.h functions) and DOS

I have a computer running DOS (freeDos 1.1) and plain C (compiled with Borland 5.0) with time.h. When I set the computer time using BIOS or the DATE and TIME DOS commands, there is no information about time zone. I set the time to my current time 10:25 AM.
My C program does this...
char timeString[80];
time_t timeT = time(NULL);
strftime(timeString, sizeof(timeString), "%a %Y-%m-%d %H:%M:%S %Z", localtime(&timeT));
printf("%s\n", timeString);
when I run the code I get the correct current time but with a "EST" time zone at the end as called by the %Z formatter.
Mon 2017-03-13 10:25:36 EST
The returned time_t value from time(NULL) is 1489418736.
This mathematically breaks down to 47 years, 83 days, 15 hours, 25 minutes, 36 seconds
Clearly time.h is implementing some time zone information here, adding 5 hours to my current time.
Moving over to javascript which receives the time_t value
new Date(1489418736 * 1000)
Mon Mar 13 2017 08:25:36 GMT-0700 (Pacific Daylight Time)
It seems there is some combination of time zone (EST vs PST vs GMT) and daylight savings time (which might account for the extra hour?) at play here. How can I affect the time zone and DST settings of the machine, operating system, or C library representation, to get the most useful time representation whether that be local or GMT?
It's been many years, but I think there's a DOS environment variable that C looks for to determine the timezone. C may default to EST if it doesn't find the environment variable. I can't speak to the javascript aspects of the question.
Concerning daylight issues, code is going to have troubles. In US, "my current time 10:25 AM should have been "Mon 2017-03-13 10:25:36 EDT" (EDT vs EST). The quaint TurboC code is certainly going to have troubles with the evolved DST rules that have differed over the decades.
IMO, any use of TurboC localtime(), mktime(), struct tm.isdst field and _daylight will all yield unsatisfactory results concerning daylight savings time and likely be an endless source of timezone issues. Either use a modern compiler/library or import/roll your own (ugh) timezone/daylight routines.
Alternative, stick to time_t and universal time - no local time
DOS (including FreeDOS) has no notion of time zones. These OSes were designed with a simple end user in mind, not database design, mission critical apps, timestamp logging, the PC being in other timezones etc. In the end, the designers of these OSes took the path of least resistance. Everything was in the user's local time.
When C compilers started being written to conform to the Standard C API, there was a problem with implementing gmtime on DOS. DOS users set their PC to local time, not UTC. Unfortunately, you can't reliably convert a local time to UTC because of DST issues (you can reliably convert UTC to any local time), but the developers wanted a solution. They chose to ignore the issues that may arise with converting local time to UTC and allowed you to set the TZ environment variable to specify the conversion from local time to UTC.
In the absence of the TZ being set libraries defaulted to a timezone which often happened to be EST, which is the case with the Borland compilers. That is why you see EST being returned as the timezone by localtime.
Before running your application (preferably you set it in autoexec.bat at boot) you can use the set TZ= command to specify the conversion from local time to UTC. A good article on syntax of the TZ variable can be found here:
set TZ=tzn[+|-]hh[:mm[:ss] ][dzn]
TZN is the 3 letter time zone identifier for standard local time, and DZN is the 3 letter identifier for daylight savings time. TZN and DZN are used for display purposes. If DZN is omitted then this flags that daylight savings time doesn't apply to your time zone. The most important part is the offset (+/-) which specifies the amount of time to be added or subtracted from standard local time to convert it to UTC.
As an example, I'm in Mountain Standard Time (MST) and daylight savings time applies (MDT). MST is 7 hours behind UTC so 7 has to be added to my standard local time to get UTC. I'd set my timezone this way:
set TZ=MST7MDT
If I was in Newfoundland where standard time (NST) is 3.5 hours behind UTC and daylight savings time applies then I could use:
set TZ=NST3:30NDT
If I was in Saskatchewan, Canada that uses Central Standard Time (CST is 6 hours behind UTC) where daylight saving time doesn't apply then I'd use:
set TZ=CST6
My Recommendation - Use UTC in DOS
I have personally found in embedded systems where the OS isn't time zone aware, that it is easier to set the system clock to UTC instead of local time. You then write software to convert UTC to local time based on some software configuration if need be. This has the advantage that you can write the rules for DST. The rules for DST built into the Borland libraries no longer apply a generation later. Using UTC is also useful for timestamping event logs since you can express unambiguous times. In this situation I use:
set TZ=UTC0
which effectively makes local time the same as UTC.
DST Differences Between Products
At the time most of the Borland products were developed the rule employed for DST was for the US market. From 1987 through 2006 DST ran from the first Sunday of April to the last Sunday of October. After 2006 it was changed in the US to run from the second Sunday in March to the first Sunday in November.
The reason Borland C++ 5 (releases issued between 1996-1997) showed EST (and not EDT) was because from its perspective DST in 2017 didn't start until Sunday April 2nd, 2017 (first Sunday of April), so it still believed Monday March 13th, 2017 was still on Standard Time. The environment in which your Javascript ran is likely aware (based on modern underlying Time Zone databases and rules) that in 2017 DST came into effect on March 12th, 2017 (second Sunday in March) so it showed Monday March 13th, 2017 in Daylight Time (PDT). I will assume that Pacific Time was used because the system running the Javascript was told you are in a Pacific Time Zone that observes DST.

mktime having problems with certain years?

I am having a problem with years in mktime().
Every time I pass a year lower than 1970 into my struct tm and then run the mktime() function to convert my structure, it fails (returns -1).
Does anyone have any idea why and if I can make this work somehow?
That is never going to work, since it's by definition outside the epoch, which is the start for Unix time. The manual page states:
The ctime(), gmtime() and localtime() functions all take an argument of data type time_t which represents calendar time. When interpreted as an absolute time value, it represents the number of seconds elapsed since the Epoch, 1970-01-01 00:00:00 +0000 (UTC).
If it fails to convert the input into Unix time, it's documented to return -1 so that's why you're seeing that result.
This is expected behavior. Per the man page:
If the specified broken-down time cannot be represented as calendar
time (seconds since the Epoch), mktime() returns (time_t) -1 and does
not alter the members of the broken-down time structure.

clock_gettime() still not monotonic - alternatives?

As has been known for a while (see, e.g., this old question, and bug reports that pop when you google this), clock_gettime() doesn't appear to report back time monotonically. To rule out any silly error I might have overseen, here is the relevant code (excerpt from larger program):
<include time.h>
long nano_1, nano_2;
double delta;
struct timespec tspec, *tspec_ptr;
clock_gettime(CLOCK_MONOTONIC_RAW, tspec_ptr);
nano_1 = tspec.tv_nsec;
sort_selection(sorted_ptr, n);
clock_gettime(CLOCK_MONOTONIC_RAW, tspec_ptr);
nano_2 = tspec.tv_nsec;
delta = (nano_2 - nano_1)/1000000.0;
printf("\nSelection sort took %g micro seconds.\n", (double) delta);
Sorting small arrays (about 1,000 elements) reports plausible times. When I sort larger ones (10,000+) using 3 sort algorithms, 1-2 of the 3 report back negative sort time. I tried all clock types mentioned in the man page, not only CLOCK_MONOTONIC_RAW - no change.
(1) Anything I overlooked in my code?
(2) Is there an alternative to clock_gettime() that measures time in increments more accurate than seconds? I don't need nanonseconds, but seconds is too coarse to really help.
System:
- Ubuntu 12.04.
- kernel 3.2.0-30
- gcc 4.6.3.
- libc version 2.15
- compiled with -lrt
This has nothing to do with the mythology of clock_gettime's monotonic clock not actually being monotonic (which probably has a basis in reality, but which was never well documented and probably fixed a long time ago). It's just a bug in your program. tv_nsec is the nanoseconds portion of a time value that's stored as two fields:
tv_sec - whole seconds
tv_nsec - nanoseconds in the range 0 to 999999999
Of course tv_nsec is going to jump backwards from 999999999 to 0 when tv_sec increments. To compute differences of timespec structs, you need to take 1000000000 times the difference in seconds and add that to the difference in nanoseconds. Of course this could quickly overflow if you don't convert to a 64-bit type first.
Based on a bit of reading around (including the link I provided above, and How to measure the ACTUAL execution time of a C program under Linux?) it seems that getrusage() or clock() should both provide you with a "working" timer that measures the time spent by your calculation only. It does puzzle me that your other function doesn't always give a >= 0 interval, I must say.
For use on getrusage, see http://linux.die.net/man/2/getrusage

Resources