My code receives a time_t from an external source. However, that time_t isn't acutally based on UTC Epoch time, along with it I get a timezone string (eg, EDT, PST, etc), and its based on this offset'ed epoch. I need to convert this to a true UTC Epoch based time_t.
Additionally, I need to be able to go in the opposite direction, taking a UTC Epoch based time_t and a timezone, and create the offsetted time_t, but in this situation instead of having a timezone like EDT/PST), etc, I have a Unix style timezone description like "America/New York" and need to convert to the correct timezone given daylight savings, so I'd need to get back from the algorithm, both an offsetted time_t, and the correct descriptor (EDT,EST).
I'm pretty sure I can pull this off by temporarily changing tzset() and some combination of conversions between broken-down time, time_t and timeval, but doing this always makes my brain hurt and makes me feel like I'm missing something obvious. Can anyone recommend some code or sudo-code to do this or at least a proper approach?
time_t is in seconds, so just offset your time_t values by 3600 times the number of hours the timezone is offset by. As long as you have the offset specifically identified (i.e. EST or EDT instead of US/Eastern or EST5EDT or whatnot) then this is really simple and not prone to errors.
would any of these functions help you? localtime(), gmtime(), etc.
Related
I am given a time_t variable and I need to populate a struct tm with its value but I do not want GMT or UTC time, I do not want local time. I want the exact time value given from my time_t variable, I just want it to be formatted in the struct tm. After doing some digging it looks like this may not be possible but I wanted to ask here if anyone has a suggestion.
Thanks.
Problem Description
I want to get a precise local timezone timestamp on Windows and store the following in a struct:
Day
Month
Year
Hour
Minute
Second
Nanosecond
I am looking for a C solution to this problem.
My understanding is that Windows can only provide microsecond granularity, but I want to store that information as nanoseconds anyway.
My use case
Here are some details on my use case, in case they are relevant. This section can be skipped if you are not concerned with my use case.
I am writing a program that processes events. I am unable to subscribe to events as they occur, and instead I need to poll the event source periodically. The event source does not contain only new events, but it contains the full history of events.
I need to create a timestamp so I can only process events newer than this timestamp from the source.
More than one event can occur within the same millisecond, and therefore millisecond granularity is too coarse for my use case.
I also save my timestamp to persistent storage so that the time of the last event processed is not lost of the program is stopped or the program crashes.
What I tried
Below is my first stab at doing this. This could be totally wrong, in which case please jump to What I am seeking help with.
void get_timestamp_now_local_timezone(timestamp *tstamp) {
SYSTEMTIME st;
SYSTEMTIME stLocal;
FILETIME ft;
GetSystemTimePreciseAsFileTime(&ft);
FileTimeToSystemTime(&ft, &st);
SystemTimeToTzSpecificLocalTime(NULL, &st, &stLocal);
ULARGE_INTEGER large_int;
large_int.LowPart = ft.dwLowDateTime;
large_int.HighPart = ft.dwHighDateTime;
ULONGLONG timeStamp = large_int.QuadPart;
/* FILETIME has 100-nanosecond interval granularity */
ULONGLONG nanoseconds = (timeStamp % 10000000) * 100;
tstamp->year = stLocal.wYear;
tstamp->month = stLocal.wMonth;
tstamp->day = stLocal.wDay;
tstamp->hour = stLocal.wHour;
tstamp->minute = stLocal.wMinute;
tstamp->second = stLocal.wSecond;
tstamp->nanoseconds = nanoseconds;
}
I am concerned about a couple possible errors with this code:
Possible error 1
GetSystemTimePreciseAsFileTime() returns time in UTC format. I am concerned that operating on the UTC format FILETIME to get the nanoseconds in the local timezone might be incorrect due to leap milliseconds1 seconds and such that might differ between timezones.
Possible error 2
I'm not sure if I am copying into the ULARGE_INTEGER correctly. I was trying to follow what was said on the FILETIME docs page:
You should copy the low- and high-order parts of the file time to a ULARGE_INTEGER structure, perform 64-bit arithmetic on the QuadPart member, and copy the LowPart and HighPart members into the FILETIME structure
There was no corresponding example, so I could have done this wrong.
What I am seeking help with
My attempt above could be totally wrong and I could be barking up the wrong tree. I include it more to show that I attempted to solve this problem on my own before coming to Stack Overflow.
I would like help with how to solve the problem I described in Problem Description above, either by A) explaining how to solve the problem; or B) if my attempt at a solution is close (which I doubt), by pointing out what I should do to fix my attempt at a solution.
1 I've been informed that leap milliseconds don't exist (I'm not sure why I thought they did)
It looks like the answer to my question is that using local time is the wrong way to go, as pointed out by #chux - Reinstate Monica and #RbMm in the comments.
At first I thought I needed my timestamps to be in local time because my events had local time timestamps, but after further reading of the Microsoft docs, I found that my events have UTC timestamps. (If anyone is interested, more details about the events I am trying to process can be found in this question.)
GetSystemTimePreciseAsFileTime() returns a timestamp in UTC, and so I don't need to perform any conversion.
So mktime() returns a time_t value which is defined as "an integral value representing the number of seconds elapsed since 00:00 hours, Jan 1, 1970 UTC" (source). I can convert a date to a time_t value by using mktime(). For example, to convert the date 10-Sep-2017 08:34:56 to a time_t I'd do the following:
struct tm tm;
time_t tv;
tm.tm_sec = 56;
tm.tm_min = 34;
tm.tm_hour = 8;
tm.tm_mday = 10;
tm.tm_mon = 8;
tm.tm_year = 117;
tm.tm_isdst = ????;
tv = mktime(&tm);
Now what I don't understand is the idea behind the tm_isdst parameter: It is described as "a flag that indicates whether daylight saving time is in effect at the time described" (source).
This description is somewhat confusing me because I'm of the opinion that the time I'm describing in struct tm is in fact already a UTC time and the time_t value I want to have from mktime() is UTC as well. But UTC time doesn't change with a change of seasons, so why does mktime() need to bother with daylight saving time at all? Isn't the advantage of using UTC instead of local time that I won't have to bother with daylight saving time? So why do I have to set tm_isdst then?
I'm sure the answer is really simple but at the moment I'm failing to see it. Could somebody please provide a simple example that illustrates why mktime() needs the tm_isdst parameter to convert a certain date and time to a time_t value?
Whether DST is in effect changes what the epoch time will be because mktime uses the current time zone to determine the time.
For example, if I populate tm with 1/1/70 00:00:00 as follows:
tm.tm_sec = 0;
tm.tm_min = 0;
tm.tm_hour = 0;
tm.tm_mday = 1;
tm.tm_mon = 0;
tm.tm_year = 70;
tm.tm_isdst = 0;
I get a value of 18000 for tv because my timezone is GMT-5 (18000 = 3600 * 5). If I change the value of tm_isdst to 1, then tv will be set to 14400 (3600 * 4).
Setting tm_isdst to -1 will look at the local timezone database to see if DST is in effect for the given date/time.
Now what I don't understand is the idea behind the tm_isdst parameter:
It is described as "a flag that indicates whether daylight saving time
is in effect at the time described" (source).
This description is somewhat confusing me because I'm of the opinion
that the time I'm describing in struct tm is in fact already a UTC
time
Well, that's your main problem. Per its documentation, which you already linked, mktime() converts a time structure expressed as local time to a time_t.
and the time_t value I want to have from mktime() is UTC as well.
Again according to the docs, when a time_t is interpreted as an absolute time, it represents the number of seconds elapsed since the Epoch, in UTC, so that's indeed what you will get from mktime(). But that means that there is, in general, a time zone and possibly daylight-saving difference between the two representations.
The C library relies on contextual localization data for the time zone applicable to local time, but although it knows the current rules for daylight saving time, it does not necessarily know the rules that were in effect at the specified time. Moreover, there are edge cases at the changeovers between daylight time and standard time. For these and perhaps other reasons, you need to tell mktime() whether the time given refers to daylight saving time or standard time.
But UTC time doesn't change with a change of seasons, so why does
mktime() need to bother with daylight saving time at all?
Because mktime() accepts local time as input.
Isn't the
advantage of using UTC instead of local time that I won't have to
bother with daylight saving time?
That is one of the advantages, yes.
So why do I have to set tm_isdst
then?
Because mktime() accepts local time as input.
struct tm can be thought of as a timestamp.
Example: When a timezone goes from daylight time to standard time, the "day" has 25 hours and struct tm needs more info than just "Y-M-D h:m:s".
The local time may be 2:30 AM daylight time and then 1 hour later it is 2:30 AM standard time. .tm_isdst is especially useful to distinguish that case. Note that .tm_isdst is relevant in all timestamps.
As struct tm itself does not contain the timezone (that info exist elsewhere), .tm_isdst is designed to provide time stamps indication of standard time or daylight time (or even unknown standard/daylight).
If the .tm_isdst is set to -1 (unknown) and the timezone is UTC, then the expectation is that, for all timestamps, it is as if .tm_isdst == 0.
Sorry if this sounds like a puzzle, but it does have puzzling me for a while. :)
From a sqlite3 db file, one of the record has last_visit_time field of value 13010301178000000 (INTEGER type).
How come 13010301178000000 = 4/12/2013 9:32:58PM? (4/12/2013 9:32:58PM is got by an existing tool, which I know nothing about how it translates internally).
Can someone shed some light on how this is translated?
I've looked at http://www.epochconverter.com/, but had no luck.
Thanks.
January 1, 1601 is the epoch for Windows timestamps.
However, those timestamps use 100-nanosecond intervals, so it appears your value got divided by 10, or you're missing a zero for some reason.
To convert to/from Unix timestamps, divide/multiply by 1000000 to convert between second and microseconds, and adjust be the offset between 1970 and 1601 in seconds.
The function mktime takes a struct tm as argument. One of the members of struct tm is tm_isdst. You can set this to 1 for wintertime, 0 for summertime, or -1 if you don't know.
However, if during winter, you try to convert 2009-09-01 00:00, mktime fails to see that although it is currently winter, the date you are converting is summertime. So the result is one hour off. For me (GMT+1) it's 2009-08-31 22:00, while it should be 23:00.
Is there a way to determine if a particular date is in the summer or wintertime period? Is it possible to convert a summer date to utc in the winter?
(I hit upon this problem trying to answer this question)
This is one of the (many) deficiencies in the time handling interfaces in Standard C. See also the Olson time zone database. There isn't an easy way to find when a time zone switches between winter and summer (daylight saving and standard) time, for example. Anything in the future is, of course, a prediction — the rule sets change often (the current release is 2017a).
Is there as far as you know, a UNIX specific solution?
I took a look at the code in tzcode2017a.tar.gz, and the mktime() there behaves as you want if you set the tm_isdst to -1 (unknown). So, if you use that (public domain) code, you would be OK - probably. Quoting from 'localtime(3)' as distributed with the Olson code:
Mktime converts the broken‐down time, expressed as local time, in the structure pointed to by tm into a calendar time value with the same encoding as that of the values returned by the time function. The original values of the tm_wday and tm_yday components of the structure are ignored, and the original values of the other components are not restricted to their normal ranges. (A positive or zero value for tm_isdst causes mktime to presume initially that summer time (for example, Daylight Saving Time in the U.S.A.) respectively, is or is not in effect for the specified time. A negative value for tm_isdst causes the mktime function to attempt to divine whether summer time is in effect for the specified time; in this case it does not use a consistent rule and may give a different answer when later presented with the same argument.)
I believe that the last caveat about a 'consistent rule' means that if the specification of the time zone changes (for example, as when the USA changed from 1st week in April to 2nd week in March for the change to Daylight Saving Time) mean that if you determined some time before the rule changed and after the rule changed, the same input data would give different outputs.
(Note there are useful HTML files in ftp://ftp.iana.org/tz/ directory, such as ftp://ftp.iana.org/tz/tz-link.html.)