1

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)

Shane Bishop
  • 3,905
  • 4
  • 17
  • 47
  • Possible duplicate of https://stackoverflow.com/questions/21888229/timestamp-in-c-with-milliseconds-precision – Zakk Apr 16 '22 at 17:31
  • From [microsoft documentation](https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtime). – Zakk Apr 16 '22 at 17:33
  • @Zakk The answers to that question are all millisecond granularity, and according to [`GetSystemTimePreciseAsFileTime()`'s docs](https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtimepreciseasfiletime), that function supports microsecond granularity. – Shane Bishop Apr 16 '22 at 17:33
  • Why don't you convert the result into nanoseconds? – Zakk Apr 16 '22 at 17:34
  • @Zakk The SYSTEMTIME structure also only has millisecond granularity. – Shane Bishop Apr 16 '22 at 17:34
  • @Zakk What I want to do is get a timestamp in microsecond granularity, and then convert that result to nanoseconds. A millisecond granularity is too coarse for my use case. – Shane Bishop Apr 16 '22 at 17:35
  • [QueryPerformanceCounter()](https://learn.microsoft.com/fr-fr/windows/win32/api/profileapi/nf-profileapi-queryperformancecounter?redirectedfrom=MSDN). – Zakk Apr 16 '22 at 17:37
  • @Zakk From what I understand, QueryPerformanceCounter() only is useful for time interval use cases. For my use case I am comparing event timestamps against a known timestamp, so I don't think QueryPerformanceCounter() will suit my use case. See the "My use case" section I added for more details on my use case. – Shane Bishop Apr 16 '22 at 17:49
  • "_I am unable to subscribe to events as they occur_" Why? – Zakk Apr 16 '22 at 17:54
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/243953/discussion-between-shane-bishop-and-zakk). – Shane Bishop Apr 16 '22 at 18:06
  • Add 6 zeros on the end of your milliseconds – David Heffernan Apr 16 '22 at 19:51
  • 1
    Nanosecond precision is easy, nanosecond accuracy is not. – Martin James Apr 16 '22 at 19:52
  • 2
    "local timezone timestamp" --> Timestamp missing daylight savings time flag. In November, there is a day with 25 hours. Better to use `UTC` than local time - far less complications. – chux - Reinstate Monica Apr 16 '22 at 21:41
  • 2
    you loss data at `FileTimeToSystemTime` call. if want *UTC* time - simply use `GetSystemTimePreciseAsFileTime` or `GetSystemTimeAsFileTime` (not different). if want convert it to local time - query `NtQuerySystemInformation(SystemTimeOfDayInformation..)` and use `SYSTEM_TIMEOFDAY_INFORMATION.TimeZoneBias.QuadPart` – RbMm Apr 17 '22 at 07:58
  • 1
    Outside of displaying it to a user, local time has no value. You definitely don't want to use local time for any computational operation. As for the actual [problem](https://stackoverflow.com/q/71876280/1889329) you are trying to solve, this may not be a solution. The answer you [received](https://stackoverflow.com/a/71879750/1889329) was authored by someone who's part of a bunch of absolutely clueless. You can identify them by their *"- MSFT"* suffix. It may well be completely wrong. – IInspectable Apr 17 '22 at 14:26
  • @ShaneBishop If you have resolved your issue please [mark](https://meta.stackexchange.com/a/5235) it as accepted – Jeaninez - MSFT Apr 20 '22 at 08:14

1 Answers1

1

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.

Shane Bishop
  • 3,905
  • 4
  • 17
  • 47
  • Yes, you should use UTC for this sort of thing. Also be aware that sub-millisecond accurate timestamps are virtually impossible to achieve without specialized hardware clock devices. Even 1ms accuracy requires custom networking and special configuration. Some articles: [1](https://docs.microsoft.com/windows-server/networking/windows-time-service/support-boundary) [2](https://docs.microsoft.com/windows-server/networking/windows-time-service/configuring-systems-for-high-accuracy) [3](https://docs.microsoft.com/windows-server/networking/windows-time-service/windows-server-2016-improvements) – Matt Johnson-Pint Apr 18 '22 at 17:47
  • Also, from your question, "... leap milliseconds and such that might differ between timezones..." - there is no such thing. Leap *seconds* exist, but are part of the UTC standard - there are no per-time-zone differences with regard to leap seconds. In other words, when a leap second occurs - it's simultaneously applied to the entire world at the exact same moment. – Matt Johnson-Pint Apr 18 '22 at 17:50