I'm creating a mock object to test my application so that it works at the boundary conditions of time. I'm using FILETIME in the Windows SDK.
The link shows the earliest time which is January 1, 1601 (I'm assuming midnight 00:00:00 and both dwLowDateTime and dwHighDateTime are 0x00000000), so I have that. What is the latest possible FILETIME?
My first instinct is to set dwLowDateTime and dwHighDateTime to 0xFFFFFFFF, but then I questioned if that really is a valid time that I need to test, due to where my linked page says the SetFileTime function uses 0xFFFFFFFF to specify that a file's previous access time should be preserved.
My understanding is that FILETIME was made to represent any valid SYSTEMTIME in 64 bits. If you take the limit of SYSTEMTIME (last millisecond in 30827) then you end up with a FILETIME of 0x7fff35f4f06c58f0 by using SystemTimeToFileTime().
However, if you put 0x7fffffffffffffff into FileTimeToSystemTime() then you will end up in the year 30828, although this date is invalid for SYSTEMTIME. Any larger value (0x8000000000000000 and above) causes FileTimeToSystemTime() to fail.
All in all, I would recommend not to go beyond 0x7fff35f4f06c58f0 in order to stay compatible with SYSTEMTIME.
According to the link, FILETIME represents:
...the number of 100-nanosecond intervals since January 1, 1601 (UTC).
so not Jan 1st 1970.
It also says
...the SetFileTime function [for example] uses 0xFFFFFFFF to specify that a file's previous access time should be preserved.
So I don't think you would expect 0xFFFFFFFF to be a valid max value.
According to patent 6853957, the range is 30,000 years before/after the epoch (Jan 1, 1601). That implies you can use it with negative dates (i.e. dates before the epoch) too.
EDIT: Just calculated: it can store (approx) 58,454 days worth of 100-nanosecond intervals, so +/- 30,000 years sounds like a good value to go with, if you accept negative dates of course.
There is an answer in this MSDN article - Test Cases for the RTC Real-Time Functions Test:
The test looks for the range beginning with the minimum possible FILETIME (FILETIME 0 is the start of Jan 1st 1601) and end with maximum possible FILETIME (Max FILETIME is the maximum 64-bit value).
Related
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.
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.
I'm trying to implement correct DST adjustment handling in my alarm clock app. So I'm reading description for DYNAMIC_TIME_ZONE_INFORMATION that I use to retrieve the current DST adjustment information via the GetTimeZoneInformationForYear API, and it says the following:
DaylightDate:
A SYSTEMTIME structure that contains a date and local
time when the transition from standard time to daylight saving time
occurs on this operating system. If the time zone does not support
daylight saving time or if the caller needs to disable daylight saving
time, the wMonth member in the SYSTEMTIME structure must be zero. If
this date is specified, the StandardDate member in this structure must
also be specified. Otherwise, the system assumes the time zone data is
invalid and no changes will be applied. 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.
I'm also checking the current DST adjustments observed all over the world, and if the relative DST adjustments seem pretty straightforward, I'm not exactly clear how the following adjustments could be conveyed via DYNAMIC_TIME_ZONE_INFORMATION -- with just an absolute month and a day.
For instance:
Egypt
-----
DST Start: May 15
DST End: Last Friday September
or this one:
Iran
----
DST Start: March 21–22
DST End: September 21–22
Does anyone know how to do this?
To understand the time zone structures, it helps to look at the Windows registry under the following key:
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\
Here you'll find all of the built-in time zones of the Microsoft time zone database, which is maintained by Microsoft via Windows Updates.
Let's look at one of the example cases you mentioned:
.\Egypt Standard Time\
.\Egypt Standard Time\Dynamic DST\
From this we can see that there are specific DST rules defined for year 2005-2011. Outside this range, we fall back to the TZI value of the root entry.
You'll notice that there the 2014 entry for Egypt is missing. That's because Egypt gave almost no notice about the upcoming change. You can expect that there will soon be a hotfix from Microsoft available with the update.
The binary data in the registry is deserialized to a REG_TZI_FORMAT structure, which looks like this:
typedef struct _REG_TZI_FORMAT
{
LONG Bias;
LONG StandardBias;
LONG DaylightBias;
SYSTEMTIME StandardDate;
SYSTEMTIME DaylightDate;
} REG_TZI_FORMAT;
An issue you should be aware of is that Windows doesn't like time zones that transition right at midnight. The workaround is that instead of saying "00:00 on the last Friday in September", you have to say "23:59:59.999 on the last Thursday in September". However, here you have to be careful because rules like these can sometime lead to erroneous derived dates. To counter that, sometimes each year will have it's own rule. The recurrence-pattern format is still used rather than the fixed-date format, mostly for consistency purposes.
However, there's another problem - This structure can only support two daylight saving time transitions in a year. One at the DaylightDate when DST begins, and one at the StandardDate when DST ends. Since Egypt is enacting DST except for Ramadan, there will be four transitions. This also happened in Egypt in 2010, and also occurs regularly in Morocco. To deal with this design flaw, Microsoft has traditionally released multiple updates, timed to coincide with the changes. (For example, see KB2297272.)
I'll assume Microsoft will push out the multiple-update changes, so for sake of example we'll leave out the Ramadan period. This rule starts DST on the 2nd Wednesday in May at 23:59:59.999 and ends it on the last Thursday in September at 23:59:59.999.
"TZI" = 88ffffff 00000000 c4ffffff 000009000400050017003b003b00e703 000005000300020017003b003b00e703
That corresponds to a REG_TZI_FORMAT structure having these values (as JSON for clarity):
{
"Bias" : -120, // Standard offset is UTC+2
"StandardBias" : 0,
"DaylightBias" : -60, // Subtract an hour for DST
"StandardDate" : {
"wYear" : 0, // Recurrence pattern
"wMonth" : 9, // September
"wDayOfWeek" : 4, // Thursday
"wDay" : 5, // Last occurrence
"wHour" : 23,
"wMinute" : 59,
"wSecond" : 59,
"wMilliseconds" : 999
},
"DaylightDate" : {
"wYear" : 0, // Recurrence pattern
"wMonth" : 5, // May
"wDayOfWeek" : 3, // Wednesday
"wDay" : 2, // Second occurrence
"wHour" : 23,
"wMinute" : 59,
"wSecond" : 59,
"wMilliseconds" : 999
}
}
I think this answer is long enough, so I'll leave it to you to extrapolate the rules for Iran if you like. Though, I'll point out that the Windows data for Iran has been incorrect since 2009 and has yet to receive an update. :-/
As a side note, if you want to specify a fixed date rule, you can provide a non-zero "real" year value. Then the day field represents the actual day - rather than the occurrence. However, this is typically avoided because it only makes sense for the Dynamic DST rules that apply to individual years. It doesn't makes sense to use a fixed-date in the generic TZI entry in the root node.
UPDATE
Microsoft has released an update for Egypt for 2014 in KB2967990.
Iran is an odd one in that the DST transition dates don't conform to the normal rules which the Microsoft registry expects. Eg: 2nd Sunday in March. So I agree that you need to use absolute dates but achieved using the registry format. The DST transition date's weekdays vary nearly every year.
Iran DST transition dates are based on the persian calendar
https://mm.icann.org/pipermail/tz/2003-March/012053.html
So the dynamic registry method would be the answer with a lot of changes every single year!
I'm fighting a few days with COleDateTime in MFC.
I have CTime with correct values. Correct years, days, months, hours, minutes and seconds.
I tried a few ways to convert CTime to COleDateTime:
-1.I put CTime data to constructor of COleDateTime
COleDateTime(int nYear,int nMonth,int nDay, int nHour, int nMin,int nSec );
-2. I formatted CTime to time.Format("%m/%d/%y %H:%M:%S");
and passed to ParseDateTime of COleDateTime.
-3. Also I tried to use SetDateTime of COleDateTime
After that I'm getting incorrect values of minutes 1-2 min. more or less.
I have never seen it before and I couldn't find nothing in internet.Everybody says abot loss precision but this a second, not a minute.
Please advice something for me!
Thank you
I think the problem is that COleDateTime internally uses a float for storage, and the value represents the number of days since 30 December 1899.
As the number of days gets larger, the precision of the smaller fields (like minutes) decreases. For example, a float can accurately store the values 1000000 and 0.0000001, but it CAN'T store 1000000.0000001. It doesn't have enough bits of precision.
This limitation is hinted at in the MSDN documentation:
This type is also used to represent date-only or time-only values. By convention, the date 0 (30 December 1899) is used for time-only values. Similarly, the time 0:00 (midnight) is used for date-only values.
So basically, if you want a precise time, set the date to 30 December 1899.
It seems like Microsoft could have just designed this class to store the "days" portion as an integer, but hey, that would be too EASY.
UPDATE: The "fraction of a second" parameter to Timestamp's constructor actually takes nanoseconds... I guessed it was hundredths of a second and my low values were rounded away. Question left for reference....
I'm struggling with Oracle's C++ library - OCCI. Summarily:
creating Timestamp objects and verifying they're good to hundredths of a second (though I'd like more!)
using stmt.setTimestamp then executeUpdate() to insert into a TIMESTAMP(6) column which should preserve microseconds
selecting the row in Oracle SQL Developer: the sub-second component is always 0-ed e.g. 14-JUL-11 06.03.27.000000000.
Problem
I need subsecond precision - hopefully microseconds! We've put a lot of work into capturing that precision in our servers and need (at least some of) it for analysis.
Details
I create a Timestamp from year/month/day hour/minute/second/millisecond, reducing the last to hundredths of a second as that seems to be what the constructor supports. (No Oracle documentation I can find specifies the interpretation, but in a fromText example "xff" clearly corresponds to a ".##" hundredths suffix in the value to convert. What's the point of TIMESTAMP(6) supporting 6 decimal places if you can't insert them?)
oracle::occi::Timestamp temp =
oracle::occi::Timestamp(_env, year, month, day,
hour, minute, second, millisecond / 10);
// re-extract the broken-down time from temp to prove it's stored successfully
int ye;
unsigned mo, da, ho, mi, se, fs;
temp.getDate(ye, mo, da);
temp.getTime(ho, mi, se, fs);
return temp;
Here, fs gets the milliseconds/10 value as expected.
I use this as in:
oracle::occi::Timestamp ts;
ts = _pImpl->makeOracleTimestamp(p->ATETimeStamp);
stmt.setTimestamp(11, ts);
Where field 11 is a TIMESTAMP(6).
Selecting the row in Oracle SQL Developer, the other parts of the timestamp column are correct but the sub-second component is 0-ed ala 14-JUL-11 06.03.27.000000000.
Any insight much appreciated!
(If relevant, using MSVC++ 2005, Oracle 10.2.0.4 sdk, SQL Developer 3.0.04 - please ask if something else might be relevant).
Thanks,
Tony
Turns out the "fractional seconds" field is nominally in nanoseconds rather than hundredths. I wish Oracle would say that in their documents! I say nominally because if it really preserved the least-significant digits then the hundredths values I had would have appeared as a number of nanoseconds and I might have immediately guessed at the problem - instead it seems values < 100 nanoseconds are lost anyway (and perhaps bigger - I haven't probed the cut-off point).
Thanks to anyone who had a look at the question or tried some research / investigation.