Time based on time zone in C++ - c++

What is the difference between gmtime() and localtime() ?
My requirement:
I need to display the time as per the time zone of my place.
I have a time in POSIX(UTC) format in seconds. i have time zone offset(between UTC and my current place) in seconds.
I either add or subtract the time zone offset.
After this i still have the time in POSIX format.
In order to convert it to human time, should I use gmtime() or localtime(). this is because i need the date too.
Please clarify.
Thanks!

You shouldn't provide the time zone offset yourself, but rather a real time zone specification via the environment. Then, localtime will give you the localized time. (gmtime always gives you UTC.) Suppose your code looks like
struct tm time_buffer, *localtime;
localtime = localtime_r(timep, &time_buffer);
printf("The hour is %i\n", localtime->tm_hour);
, then you call your program via:
$ TZ=Europe/Berlin my-program
and get the local hour of Berlin, correctly adjusted for daylight saving time.
If you absolutely need to provide a timezone yourself, read man tzset.

gmtime is UTC—coordinated universal time. It is always the
same, everywhere. localtime is an interpretation of the local time,
as determined by the time zone and whether summer time is in effect or
not. The time zone will be determined by the environment variable TZ,
which the user should have set to his local timezone; if this variable
is not set, an implementation defined default is used: typically the
timezone where the machine is located.

Related

FILETIME to/from ISO 8601 with Win32 API. Getting DST right?

Wrote something in .NET; it works well. Now I am trying to rewrite it as a shell extension with the Win32 API. Ultimately I want to convert FILETIMEs to and from ISO-8601 strings. This is doable without fuss, using GetTimeZoneInformation and FileTimeToSystemTime and SystemTimeToTzSpecificLocalTime and StringCchPrintf to assemble the members of the SYSTEMTIME and TIME_ZONE_INFORMATION structs into a string.
The problem, as usual when working with date/times, is Daylight Saving Time. Using GetTimeZoneInformation tells me the UTC offset that's in effect now. Using .NET's DateTime.ToString("o") takes into account the daylight saving time at the time represented in the DateTime.
Example for the same FILETIME:
Output of ToString("o"): 2017-06-21T12:00:00.0000000-05:00
Output of chained APIs: 2017-06-21T12:00:00-06:00
The UTC offset is wrong coming from the chained API calls. How does .NET's DateTime do it? How do we replicate that in Win32? Is there something like GetTimeZoneInformationForYear, but instead of for a year, for a moment in local time?
First, I use DYNAMIC_TIME_ZONE_INFORMATION structure and GetDynamicTimeZoneInformation
DYNAMIC_TIME_ZONE_INFORMATION and TIME_ZONE_INFORMATION also has a DaylightBias member:
The bias value to be used during local time translations that occur
during daylight saving time. This member is ignored if a value for the
DaylightDate member is not supplied.
This value is added to the value of the Bias member to form the bias
used during daylight saving time. In most time zones, the value of
this member is –60.
So, if the date is in daylight saving time, you need to add this DaylightBias to Bias.
In addition, you can determine whether the current date is daylight saving time according to the description in DaylightDate:
To select the correct day in the month, set the wYear member to zero,
the wHour and wMinute members to the transition time, the wDayOfWeek
member to the appropriate weekday, and the wDay member to indicate the
occurrence of the day of the week within the month (1 to 5, where 5
indicates the final occurrence during the month if that day of the
week does not occur 5 times).
If the wYear member is not zero, the transition date is absolute; it
will only occur one time. Otherwise, it is a relative date that occurs
yearly.

Determine if daylight savings applies to a certain date

I'm attempting to modify the creation dates of files to the date they were released. I'm first converting a string such as "2 April 2005" into a std::tm. I then create a SYSTEMTIME as follows:
std::tm dt = from_string("2 April 2005");
SYSTEMTIME st { 0 };
st.wYear = dt.tm_year + 1900; // dt is years from 1900
st.wMonth = dt.tm_mon + 1; // dt is month index 0
st.wDay = dt.tm_mday;
st.wHour = 6; // FILETIME is based on UTC, which is 6 hours ahead
Afterwards I convert the SYSTEMTIME to a FILETIME and use that to apply the changes.
This sets the file time to 2 April 2005 12:00:00 AM which is correct. However, videos after April 2nd were being set to 1:00:00 AM and sure enough, daylight savings happened on April 3rd 2005.
How can I determine if a certain date is before or after daylight savings so I can adjust st.wHour accordingly? The goal is to have all times set to 12:00:00 AM. Preferably this would work on dates dating back to the 60s as well as current.
I tried using TIME_ZONE_INFORMATION and GetTimeZoneInformation but I only get back TIME_ZONE_ID_STANDARD.
A few things:
SYSTEMTIME is just a plain structure. It has separate fields for year, month, day-of-week, day (of month), hour, minute, second and millisecond. It is not either UTC or local time, or anything else by itself. That doesn't come into consideration until you pass it into a function.
FILETIME is another plain structure. It represents the number of 100-nanosecond intervals since midnight of 1601-01-01. Much of the docs would make you think it is always in UTC, but there are functions like FileTimeToLocalFileTime that disprove that. Thus, like SYSTEMTIME, it is up to each individual function to decide how to interpret it.
The SystemTimeToFileTime function accepts a pointer to a SYSTEMTIME that is interpreted as UTC, and returns a pointer to a FILETIME that is also in terms of UTC. There is no local time zone involved.
Don't try to adjust for local time yourself (st.wHour = 6 in your code). The hour will need to vary depending both on time zone and on DST.
You didn't see any response from GetTimeZoneInformation other than TIME_ZONE_STANDARD, because that tells you what is currently in effect - which is disconnected from the dates you might be working with.
You shouldn't be trying to figure out how to adjust for DST on your own. DST is not universally applicable, and it's not always one hour offset. Instead, use a function that converts from the time zone you care about to UTC.
Ultimately it sounds like you are asking about how to set the file time to midnight of the local time zone on a particular date. Thus, I suggest you go through these steps:
Construct a SYSTEMTIME with the date you care about and the time components set to zeros (the default).
Get the local time zone using the GetDynamicTimeZoneInformation function. You want the "dynamic" version such that any historical differences in rules for standard time and DST that Windows knows about are taken into consideration, rather than just the current set of rules.
Pass those two values to the TzSpecificLocalTimeToSystemTimeEx function. It will interpret the input time as being in the input time zone (which was the system's local time zone). The result is a FILETIME that is in terms of UTC.
Pass that value to SetFileTime, which expects the input in terms of UTC.
Also, keep in mind that not all file systems track file times in the same way:
NTFS stores the actual UTC time, so you can move files between computers and the timestamp represents the same point in Universal Time even if the computers have different time zone settings.
FAT and its variants store local time. So when you call SetFileTime, Windows translates from UTC to the local time zone and writes the result. If you then open the file on a system with a different time zone, the date will be interpreted in that time zone, resulting in a different UTC time. (This is often seen on USB sticks, memory cards, etc. when moving files from cameras to computers.)
Lastly, you said:
... Preferably this would work on dates dating back to the 60s as well as current.
Unfortunately, Windows time zones do not track historical dates that far. Microsoft's time zone policy is to track time zone and DST rules for 2010 and forward for all populated places on Earth. Although, several time zones track some historical changes from before 2010 as artifacts from before this policy was formalized. (They are accurate within a given zone, just not uniform in starting years across all zones).
If historical dates are significant to your application, you'll need a very different approach - one that doesn't use the Windows time zone data, but instead uses the IANA time zone database. (More on these in the timezone tag wiki.) Here are some ideas you could explore:
The ICU project has time zone support and a C implementation. It's a bit heavy for this one purpose, but good if you are using it for other localization aspects of an application.
Howard Hinnant (who commented on the question above) has an excellent Date library with IANA time zone support.
It might be possible to get what you need from the Windows.Globalization.Calendar UWP class. I haven't tested to see if it will use historical rules from IANA data or if it uses the Windows data. (If I get a chance to check, I'll come back and update this answer.)
Keep in mind that the IANA database only guarantees from 1970 forward. You said you needed from 1960, and though there are some zones with data for that (and some much older), there is no guarantee of correctness in that period.

Does _finddata_t structure return time_write as system time or it is influenced by session timezone?

I am referring the documentation of _filefirst() and _findnext() APIs here
These APIs return file information in a _finddata_t structure. I need to access file modification time from time_write element. Though documentation says that
time is stored in UTC format (It is a times stamp). Documentation doesn't clarify if this time represents local time or UTC time. It seems to me that time_write doesn't return the UTC time instead its value is influenced by the system time zone settings.
My Question is - Does time_write returns local time represented in the UTC timestamps ?
Edit1
Here I explain what actually I am trying to understand. My system is in IST timezone. Now, there is a file emp10.ibd for which windows shows
Date Created - 10/21/2016 10:51 AM
Date Modified -10/21/2016 10:51 AM
I used epoch converter to find out the the epoch timestamp for which it turn out to be as following -
Now if I retrieve the time_write element from _finddata_t structure which has been returned by _findnext() for the same file i.e. emp10.ibd. I expect the returned timestamp should be close to
Epoch timestamp 1477027260 as shown in the image above.
But I get the time_write as 1477043509
If I again use epoch converter I get the following
I am trying to understand why there is 4:30 Hours of time difference in GMT in both images shared above? IMO timestamp should have been almost same. What obvious I am missing here ?
Edit2
For those folks who were asking for sample code. Here I paste link of another post which I had asked a year ago for the same reason but scenario was little different, There I was referring to _stati64 struct. I didn't troubleshoot the problem further at that time. By now it is pretty clear that
_finddata_t and _stati64 APIs are affected by _tzset environment variable as Harry mentioned in this post while FILETIME struct is not.
Local time is UTC plus a geographical offset plus potentially a seasonal offset. A UTC timestamp has no such offsets.
In this particular case, the exact format is seconds since1970-01-01T00:00:00Z i.e. January 1st, 1970, at midnight UTC.
To troubleshoot further, next I used GetFileTime API to retrieve the
the file modification time in FILETIME struct and converted the time into UTC timestamp. I got the time according the time set on my computer. I was expecting the same.
At this point I started investigating the way we execute our program through a perl script. I found that perl script was setting the timezone to GMT-1.
Since my computer was in timezone GMT+5:30, therefore I used to get resultant +04:30 hrs of difference as mentioned in the original post.
Therefore I would like to sum up my experience as - the outcome of _finddata_t strcut is affected by the timezone set in the session but the outcome of FILETIME struct is not affected by the time zone set in the session, instead it is the time according the system timezone. Since I was retrieving one time using FILETIME struct and another using _finddata_t strcut that was causing the problem. Took me ~48Hrs to find out this interesting observation.
Why does that happen? Perhaps the answer is provided by Harry in the comment section.I am pasting the same here as it is -
changing the timezone in Perl is probably causing the TZ environment variable to be set, which affects the C runtime library as per the documentation for _tzset. It isn't a per-session setting, at least not in the way Windows uses the word "session"."
Edit1
From File Times, I read the following -
FindFirstFile retrieves the local time from the FAT file system and converts it to UTC by using the current settings for the time zone and daylight saving time.
Though I was using the NTFS file system but I believe it uses the same mechanism i.e. retrieve the local time from file system and converts it to UTC by using current settings. That's the reason I noticed the difference.

Calculating epoch time where difference of seconds is zero, getting 5:30 hrs more time

The time_orign_epoch,should be 1970-01-01 00:00:00,why i am getting 5:30 more?
time_origin_epoch = datetime.datetime.fromtimestamp(0)
print time_origin_epoch
1970-01-01 05:30:00
It's because you live in India!
How did I know that?
Well, a timestamp of 0 means 1970-01-01 00:00:00 UTC. Since your output shows 05:30:00, your time zone is UTC+05:30. And India is one of the few places in the world with a timezone offset which is not a whole number of hours.
When you construct a datetime in Python using fromtimestamp(), the default is to use your local timezone for the conversion. This corresponds to calling the classic C function localtime() rather than gmtime().
Running your code gives me a result that is five hours behind what it should be. This is consistent with the fact that I am in EST and am five hours behind UTC.
If you are simply looking to confirm correct output, then check your time zone, I would guess you are somewhere in Eastern Asia. However, if you need a zeroes value produced by the program itself, try resetting your timezone to UTC from the program, and running that snippet again.
From this answer:
If you are using Unix, you can use time.tzset to change the process's local timezone:
import os
import time
os.environ['TZ'] = tz
time.tzset()
You could then convert the datetime strings to NumPy datetime64's using
def using_tzset(date_strings, tz):
os.environ['TZ'] = tz
time.tzset()
return np.array(date_strings, dtype='datetime64[ns]')

Is there an API to convert time in FILETIME format from UTC to local time?

I know that SystemTimeToTzSpecificLocalTime API can convert from UTC time to local time, but it takes time in SYSTEMTIME format. I'm curious if there is an API that accepts FILETIME format instead?
PS. I know that I can achieve this by using FileTimeToSystemTime() and then SystemTimeToFileTime(). I was just trying to save two steps for converting to SYSTEMTIME and back.
Have a look at FileTimeToLocalFileTime()
Converts a (UTC-based) file time to a local file time.
And LocalFileTimeToFileTime():
Converts a local file time to a file time based on the Coordinated Universal Time (UTC).
However, you cannot convert between a UTC FILETIME and a local FILETIME in a specified timezone. The current timezone of the local machine is used for the conversion. The documentation states:
To account for daylight saving time when converting a file time to a local time, use the following sequence of functions in place of using FileTimeToLocalFileTime:
1. FileTimeToSystemTime
2. SystemTimeToTzSpecificLocalTime
3. SystemTimeToFileTime
And
LocalFileTimeToFileTime uses the current settings for the time zone and daylight saving time. Therefore, if it is daylight saving time, this function will take daylight saving time into account, even if the time you are converting is in standard time.