On the various ways of getting the current time and date in Win32


There are a number of functions in Win32 that obtain the current date and time. Here's how they fit together:

The starting point is Get­System­Time­As­File­Time. This returns the current time in UTC in the form of a FILE­TIME structure. This also happens to be the time format used internally by the system, so this value can be retrieved with a minimum of fuss.

You can also call Get­System­Time which returns the current UTC time in the form of a SYSTEM­TIME structure. To do this, the operating system takes the current FILE­TIME and then calls the moral equivalent of File­Time­To­System­Time, which does a boatload of gnarly math to decompose the FILE­TIME into year, month, day, hour, minute, second, and millisecond.

Meanwhile, you can also get the current local time by taking the FILE­TIME returned by Get­System­Time­As­File­Time, then passing it to File­Time­To­Local­File­Time.

And finally, there's Get­Local­Time, which does the same thing as Get­System­Time, but it starts with the local file time.

In equations:

Format Time zone Function Algorithm
FILE­TIME UTC Get­System­Time­As­File­Time (Native format)
FILE­TIME Local (None) Get­System­Time­As­File­Time + File­Time­To­Local­File­Time
SYSTEM­TIME UTC Get­System­Time  Get­System­Time­As­File­Time + File­Time­To­System­Time
SYSTEM­TIME Local Get­Local­Time  Get­System­Time­As­File­Time + File­Time­To­Local­File­Time + File­Time­To­System­Time

I happen to be a fan of commutative diagrams. (Though since there are no closed loops, there is nothing to commute.)

A 2-by-2 grid of boxes. The top row is labeled FILE­TIME; the bottom row is labeled SYSTEM­TIME. The first column is labeled UTC; the second column is labeled Local. The upper left box is labeled Get­System­Time­As­File­Time. There is an outgoing arrow to the right labeled File­Time­To­Local­File­Time leading to the box in the second column labeled None. There is an outgoing arrow downward labeled File­Time­To­System­Time leading to the box in the second row, first column, labeled Get­System­Time. From the box in the upper right corner labeled None, there is an outgoing arrow downward labeled File­Time­To­System­Time leading to the box in the second row, second column, labeled Get­Local­Time.
UTC
Local
File­Time
Get­System­Time­As­File­Time
File­Time­To­Local­File­Time
(None)
File­Time­To­System­Time
File­Time­To­System­Time
SYSTEM­TIME
Get­System­Time
Get­Local­Time

To complete the commutative diagram, there would be an arrow connecting the bottom two boxes called System­Time­To­Local­Time, but there is no such function.

Today's article was inspired by some code I ran across which did this:

SYSTEMTIME stNow;
FILETIME ftNow;
GetSystemTime(&stNow);
SystemTimeToFileTime(&stNow, &ftNow);

That code unwittingly takes an excursion from Get­System­Time­As­File­Time through File­Time­To­System­Time to Get­System­Time, then back through System­Time­To­File­Time­ to return to Get­System­Time­As­File­Time, just so that it can end up where it started, but with a lot of extra math (and loss of resolution).

Exercise: How would you implement the System­Time­To­Local­Time function?

Comments (40)
  1. Damien says:

    SystemTimeToFileTime + FileTimeToLocalFileTime + FileTimeToSystemTime?

  2. Grzechooo says:

    I'm waiting for a snarky remark on how Windows sets BIOS to local time.

  3. Henke37 says:

    I'd figure out how to host a JavaScript engine and then use the Date class.

  4. Adam Rosenfield says:

    Exercise: Just increment/decrement by the current time zone offset (expressed in 100-nanosecond intervals).

    Your nice little commutative diagram fails to render the arrows in Firefox, and they render at a tiny size in Chrome.  I don't know if the problem is in the browsers or in the SVG, but either way, only IE gets it right.

  5. Damien says:

    Also, given the theme of todays post – was this also meant to be your semi-annual "the clocks change in most of the US this Sunday" reminder post?

  6. voo says:

    Interestingly enough chrome doesn't show the arrows correctly in the diagram – IE does fine though. I'm by no means a webdev, but from a short glance into the html it looks like a SVG graphic – aren't those non-standard IE extensions or just not supported by chrome?

  7. @voo says:

    Don't work in Firefox either…

    It's HTML5 inline SVG. All modern browsers support it, perhaps Raymond used some SVG tags that the other browsers don't support.

  8. Zenju says:

    The table above makes time conversions look too easy! :)

    It should mention the second way to get from UTC FILETIME to local SYSTEMTIME which ends up with a slightly different result for FAT/NTFS:

    FileTimeToSystemTime + SystemTimeToTzSpecificLocalTime

    Reference: msdn.microsoft.com/…/ms724277(VS.85).aspx

  9. David says:

    Well, it is annoying that Windows sensibly uses UTC internally, but has no supported way to set the BIOS in UTC, thus allowing DST to be handled correctly in systems that multi-boot.

    (Yes, there is the undocumented registry setting, but unfortunately even when RealtimeIsUniversal is set SetWaitableTimer()/SetWaitableTimerEx() still set wake alarms in local time, so Scheduled Tasks wake the computer at the wrong time [or fail to wake it at all].)

  10. David says:

    Sorry, to follow up on my own comment, but intriguingly RealTimeIsUniversal is now documented as 'recommended' for some scenarios (in Windows Embedded, at least): msdn.microsoft.com/…/ff793798%28v=winembedded.60%29.aspx

    (But it still says 'This is an unsupported feature of Windows, and the Windows UI might not honor this registry entry. Therefore, the CMOS clock should be set to UTC within the BIOS and not within Windows.' here: msdn.microsoft.com/…/ff794720%28v=winembedded.60%29.aspx )

  11. Joshua says:

    That's bad. Due to multiple overlapping scenarios, HKEY_LOCAL_MACHINESYSTEMCurrentControlSetControlTimeZoneInformationRealTimeIsUniversal should be fully supported.

  12. jader3rd says:

    I've wondered for a while, why is it called File Time in the first place?

  13. DWalker says:

    SYSTEMTIME stNow;

    FILETIME ftNow;

    GetSystemTime(&st);

    SystemTimeToFileTime(&stNow, &ftNow);

    Shouldn't &st be &stnow in the second line?

    [Fixed thanks. Normally I delete simple typos, but the follow-up comment was funny, so I'm leaving both. Hey, this time I wrote two lines of code and only one of them was wrong! (Well, okay, they're still both wrong, but only one of them was blatantly wrong.) -Raymond]
  14. V-SHorn says:

    @DWalker: Only if your line numbers start at 0.

  15. dave says:

    >I've wondered for a while, why is it called File Time in the first place?

    In the kernel, the '64-bit count of 100-nS units' is called a 'system time' as indeed it should be, since it's the time format used by the system.

    I suppose that the Win32 API already had a SYSTEMTIME (possibly because Win16 had one), so it had to invent a new name for the same concept.  And the first place 64-bit times showed up in Win32 was (more guessing) in the file system APIs.

    (The file system APIs are closer to kernel thinking than other places: e.g., they know 0 can be a valid HANDLE)

  16. avakar86 says:

    The documentation of FILETIME says that it is the number of 100ns intervals from January 1, 1601 (UTC) and that's what GetSystemTimeAsFileTime returns. So what exactly is FILETIME in local form? Is it the number of 100ns intervals since January 1, 1601 (local)? And finally, how does FileTimeToSystemTime determine which form the FILETIME is in?

  17. Myria says:

    I just had to implement gmtime_r on Windows for an open-source project that wasn't using Visual Studio (so no gmtime_s), so this topic was on my mind =)  Windows really does handle time much better than UNIX.  A coworker once whined at me about how Windows uses the "really strange" epoch of January 1, 1601 N.S. UTC, but I explained how that's actually an ingenious choice of epoch.  The period of the Gregorian calendar is 400 years, so choosing a year that is either a multiple of 400 or the year after makes a lot of sense for making conversion to calendar dates simpler.

    The one thing I wish Windows would add to its time API is the ability to look up a TimeZoneKeyName given a POSIX time zone name.  For example, to map "America/Los_Angeles" to "Pacific Standard Time".

    @Maurits: Why doesn't FileTimeToLocalFileTime just work directly?  As for a "local FILETIME", if you divide by 10000000 and subtract 11644473600LL, you have a local time_t.  I do find it interesting that FileTimeToLocalFileTime is prohibited in Windows Store programs.

    @David: I would love to be able to use RealTimeIsUniversal on my work machine.  My primary work machine has both Windows 7 and Mountain Lion installed, and both are on the NT domain.  Every time I switch back to Mac OS, I have to log in as a local user and fix the time, because Mac OS's Kerberos authentication stuff rejects the tickets coming off Active Directory's domain controllers.

  18. @Myria: I understand the utility of a (UTC time_t <=> UTC FILETIME) formula.

    support.microsoft.com/…/167296

    I also understand that you can use the same formula to do ("local time_t" <=> "local FILETIME"). But isn't that just converting a chimera from one language to another? What would you do with the "local time_t" once you had it?

  19. AndyCadley says:

    "How would you implement the System­Time­To­Local­Time function?"

    Well first I'd write a function that enumerates every possible LocalTime…. ;-)

  20. dave says:

    >But isn't that just converting a chimera from one language to another?

    This all depends on what the definitions are.

    A FILETIME is a 64-bit integer representing the number of 100-nS units since 1601-01-01 UTC.

    time_t is simply an arithmetic type suitable for storing system time values in standard C. It is undefined what the values mean: no definition of start of the range, no definition of what time interval is represented by a unit increase in the value, etc.

    So a FILETIME is by its own definition a bad choice for a local time value; time_t is just fine.

  21. Henri Hein says:

    OT: is it possible to tell when a post is closed to comments?  As it is, I cannot tell if the comment box is missing because I'm not logged in, or missing because comments are closed.

  22. hexatron says:

    I used the GetSystemTime – SystemTimeToFileTime construct. In my defense, 1. I needed SYSTEMTIME before I needed FILETIME 2. GetSystemTime has a shorter name, so it probably runs faster than SystemTimeToFileTime, and 3. Thinking about this takes orders of magnitude more time than doing it the slower way.

    I was dealing with a SQL Server database, and using SystemTimeToVariantTime (number of days since 30Dec1899 as a double) which seems not to have a FILETIME version.

    SystemTimeToVariantTime also trashes milliseconds, so I use SystemTimeToVariantTime(&st, &vt); vt += st.wMilliSeconds/86400000.0;

  23. @Henri Hein: this blog allows anonymous comments, so if you see…

    … no boxes, it means the post is closed;

    … only a comment box, it means the post is open, and you're signed in;

    … both a comment box and a username box, it means the post is open, and you're not signed in.

    There's also another way to tell whether you're signed in; the top right corner of the site will show your username if you're signed in, or "Sign in" if you're not.

  24. avakar86 says:

    The concept of "local FILETIME" seems inherently flawed to me. There is only one "number of time units since epoch". It makes no sense to talk about "UTC" and "local" forms of that number. Time zones do not enter the picture until you start showing the time to the user (in a SYSTEMTIME-like form).

    I'm guessing that the intent of FileTimeToLocalFileTime is for it to look at the numbers on local clocks at the specified FILETIME, and then search for a FILETIME at which the numbers on UTC clocks are the same (thus allowing you to use FileTimeToSystemTime to format local time). This search can fail in the presence of leap seconds though: there is for instance no FILETIME at which UTC clocks would show "2012-07-01, 00:59:60", a perfectly valid local time in +01:00 time zone.

    @Myria, as for Windows Store programs, I hope that supporting leap seconds one day is one of the motivations for disallowing FileTimeToLocalFileTime.

  25. > what exactly is FILETIME in local form

    A chimera. When you have a chimera, it is a signal that perhaps there is a better way. I find the documentation for FileTimeToLocalFileTime to be interesting:

    — begin quote —

    msdn.microsoft.com/…/ms724277(v=vs.85).aspx

    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

    — end quote —

    You have the following perfectly sensible data at the various stages:

    1) START: you have a FILETIME (which is by definition in UTC.)

    2) After FileTimeToSystemTime you have a SYSTEMTIME in UTC.

    3) After SystemTimeToTzSpecificLocalTime you have a SYSTEMTIME in your local time zone.

    This gets you to every point of the diagram except the top right corner. The documentation then says you *can* get to the top right corner by calling SystemTimeToFileTime again.

    But why do you *want* to get to the top right corner? What would you do with a "local FILETIME"?

  26. Ah, a post that reminds me what a pain running Windows and Linux in a multi-boot setting is. Windows sets the BIOS time to local time and Linux sets the BIOS time to UTC time. Result: What Max Payne calls "an old familiar feeling".

  27. smf says:

    Anyone who thinks that Linux is right for storing UTC should set all the clocks in their house to UTC. It would have been better to come up with a protocol for marking whether the RTC has already been adjusted for DST.

  28. Neil says:

    smf: Does bit 1 of CMOS byte 0x0B do what you want?

  29. jwatt says:

    What is the intention of the SVG 'switch' element in the markup of this post? Only one of the direct children of a switch should be showing at once, but you seem to be relying on a bug in IE which means that both direct children show. Firefox obeys the spec, so your diagram doesn't show correctly there. It'd at least be interesting to know why you're using 'switch'.

    [IE correctly shows only one of the switch children. I am using a switch so that I can show HTML arrows only on browsers that don't support SVG. -Raymond]
  30. jwatt says:

    To be clear, you seem to be wanting both direct children of the 'switch' to show, so I don't understand why you've wrapped them in a 'switch' element. The only effect seems to be to break the diagrams for non-IE, spec compliant browsers. Spec text is here: http://www.w3.org/…/struct.html

    [I want only one of the switches to display. One draws the arrows via SVG. The other draws the arrows via Unicode. I like how people ascribe to malice what can be more easily explained by stupidity. -Raymond]
    [I want only one. The HTML should appear in browsers which don't support SVG, and the SVG should appear in browsers that do. Otherwise, you get double sets of arrows. -Raymond]
  31. Toby says:

    Using GetSystemTime followed by SystemTimeToFileTime is useful if you want to write code which works on both desktop Windows and Windows CE.

  32. Neil says:

    The <g> draws some nice arrows when I delete the <foreignObject> so I don't know why the latter exists at all.

    Chrome appears to render the <foreignObject> (which is a set of text arrow characters) with a 100% width and height, while Firefox defaults to zero width and height, which is why it doesn't show anything at all.

    [It's there for browser which don't support SVG. I tweaked it and I think it's better now. (I find it interesting that nobody seemed too upset that three browsers all disagree on the behavior here.) -Raymond]
  33. voo says:

    @smf: I assume you're also complaining to intel why they implemented binary arithmetic because hey nobody does their math in binary? Talk about apologists..

    Storing the time in UTC avoids the usual problems with several OSes as well as DST. And computers are surprisingly capable of converting the UTC to the user's local time before displaying it (I know it's amazing).

    Heck Windows stores the time internally as UTC anyhow. The only reason it's still stored in local time is backcomp with earlier versions and I don't see that changing anytime soon :(

    [BIOSes are not very good at converting between UTC and local time. It's not like Phoenix sends out firmware updates every time Brazil changes a time zone rule. -Raymond]
  34. Kapil_K says:

    I believe you start with offering the user a full screen modal dialog asking them the time required for their daily commute. Then you try to get their email address from outlook or windows contacts and  stick it into linkedin's web API to find their place of work. Next you get the traffic congestion and traffic speed limit information from google maps and calculate an approximate perimeter of their location. Next you record background noise from their microphone to determine proximity to a freeway, playground, etc and narrow down the location. Once you're close enough, pick the location that rhymes with awesome. Now that you have the location, its time to find the time. Approximate the earths location in time by running a big-bang simulation using location information determined earlier. To get current state of the universe, reflash the the wi-fi radio firmware to pickup the cosmic microwave background radiation. You now have the local time. Now.. I know what you're thinking. This still leaves one more important thing to consider. If we get rid of the SYSTEMTIME input parameter, how many apps will it break? Sorry.. you have to think about that yourself.

  35. Deduplicator says:

    BIOSes don't do any time conversion? Nice to know.

    I thought in addition to thoroughly and layman-compatibly explaining all their myriad options they could auto-config everything and certainly do that trivial time-adjustment.

    You really seriously suppose that displaying the local time together with all these unexplained arcanae is such a clear win as to outshine any disadvantages regarding multiboot/dst/rescue-systems/uniform treatment of time offsets?

    Then why doesn't windows consequently do it for NTFS and ban all UTC functions?

    Well, mostly I woulddn't actually care that much, if RealtimeIsUniversal was officially supported and completely worked in Windows. That way anyone needing or wanting sensible behavior could get it. It doesn't though, at least till Win7. Is it any better for Win8?

    As a bonus, whenever RealtimeIsUniversal is activated/gets mandatory one can drop the auto-set DST checkbox.

  36. voo says:

    [BIOSes are not very good at converting between UTC and local time. It's not like Phoenix sends out firmware updates every time Brazil changes a time zone rule. -Raymond]

    I'm not talking about the BIOS, I'm talking about the OS. Sure the BIOS would show the UTC time, but then what? It's not as if your average user would just stumble into the BIOS and be surprised that some field called "UTC" wouldn't show her local time.

    Heck they'll be a whole lot more confused by all the other cryptic fields they'll see in there. PCIE clock, CPU clock ratio, CPU northbridge frequency, HT link width,.. your average user will be confused by every single one of those settings – on the other hand a reasonably educated person should know very well what UTC is (just tested it with a friend who's studying psychology and econ, she didn't seem very confounded – well apart from me asking her such a thing out of the blue).

    ["When I turn on my computer, it told me CMOS battery failure, and then asked me to enter the current time in UTC. I didn't understand the question, so I just entered the current time. How was I supposed to know I needed to enter the current time in Iceland!?" -Raymond]
  37. Muzer says:

    'I'm not talking about the BIOS, I'm talking about the OS. Sure the BIOS would show the UTC time, but then what? It's not as if your average user would just stumble into the BIOS and be surprised that some field called "UTC" wouldn't show her local time.'

    According to previous posts on this blog, they do – or at least, used to.

  38. Marc K says:

    Alex Trebek: "The API call that returns the time format used internally by the operating system and doesn't involve a data conversion:

    Get­System­Time­As­File­Time

    Get­System­Time

    Get­Local­Time"

    Contestant: "What is Get­System­Time, Alex?"

    Alex Trebek: "Oh, I'm sorry.  That's going to cost you."

  39. Henri Hein says:

    @Maurits: thanks.  I knew about the upper right, but sometimes when I log in, not all the elements on the page refresh.  (In general, not specifically on MSDN).  Before refreshing like crazy, it's nice to know if there is a point to it or not.  Now I know what to look for, at least on MSDN blogs.

  40. DWalker says:

    @V-SHorn:  Well, I was going to call it the 3nd line, but that wouldn't work.  Maybe I'll have my line numbers start at -1 from now on.  :-)

Comments are closed.