Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

SystemTimeToFileTime: Why Is My Time Conversion Off?

Having issues using SystemTimeToFileTime? Learn how incorrect UTC to local time conversion can cause file timestamp errors.
Developer confused over SystemTimeToFileTime and FileTimeToLocalFileTime mismatch with highlighted timezone error Developer confused over SystemTimeToFileTime and FileTimeToLocalFileTime mismatch with highlighted timezone error
  • 🕓 VariantTimeToSystemTime always assumes input times are in UTC, not local.
  • 🔁 Misordered API calls in time conversions can create subtle timestamp bugs.
  • 🌍 FileTimeToLocalFileTime is the only API that converts UTC to local, factoring in DST.
  • ⚠️ Incorrect assumptions about Excel/Automation DATEs being local time lead to misaligned schedules.
  • 🛠️ Always validate conversions across machines and time zones to catch configuration-based bugs.

When Time Stamps Don’t Add Up

You are changing dates in your Windows application, and things are not right—your file timestamps don't match up. Whether you are getting data from Excel automation or changing times to a local format, small errors in the order of your API calls can make your program work wrong. This often happens because of how and when you use important Win32 API functions like VariantTimeToSystemTime, SystemTimeToFileTime, and FileTimeToLocalFileTime. Here, we'll look at how to use each one the right way to get your time conversions correct.


Understanding the Win32 Time APIs

The Win32 API provides many functions to handle dates and times, especially when you work with the system or files. We will explain three important functions that help you understand and change dates in Windows programs:

VariantTimeToSystemTime

This function changes a DATE — a number with a decimal, used by OLE Automation (like Excel, COM programs) — into a SYSTEMTIME structure. It is important to know that the date is seen as UTC. But many programmers think these interfaces give local time. This causes many wrong conversions when local functions are used too soon.
Inside, the DATE format counts days (and parts of a day) since December 30, 1899, at midnight. The whole number part is the day, and the decimal part is the time. VariantTimeToSystemTime will take this date and fill a simple structure with things like year, month, day, and hour. All these are in UTC.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

SystemTimeToFileTime

After you have a SYSTEMTIME structure (in UTC), SystemTimeToFileTime turns it into a FILETIME. This is key for working with file systems and related programs, because Windows keeps time records in the FILETIME format. This is a 64-bit number that counts 100-nanosecond steps since January 1, 1601 (UTC).
Because the change assumes UTC, you must not give it local SYSTEMTIME values. Do this only if you already changed them by hand. Otherwise, you will likely get wrong times or times that do not match.

FileTimeToLocalFileTime

This function is the one that finally adds local time. It takes a FILETIME in UTC. And then it changes it based on the computer's time zone and any Daylight Saving Time (DST) rules. The new FILETIME value will show local time when you display or use it.
This is the only one of the three that truly changes UTC to local time. So, it should always be the last step when you need to show local time.


Win32 Date-Time Types: Know the Building Blocks

To fully control time changes, you must clearly understand the Win32 structures used. Let's explain the main parts.

VARIANT / DATE

  • Type: double (a number with a decimal)
  • What it is: It shows the number of days since December 30, 1899.
  • Used in: Programs that run things automatically (like Excel macros, COM programs).
  • Time Zone: Always UTC, unless the papers say something else.
  • Problem: People often think it's local time.
    This type is small and used often to show time in scripting languages, OLE automation, and Office programs. But its format is too simple and does not know about time zones. This makes it easy to get wrong.

SYSTEMTIME

  • Type: Struct
  • What it is: Time you can read easily, with parts like year, month, day, hour, minute, and so on.
  • Time Zone: Assumed to be UTC when used with file functions, unless told otherwise.
    SYSTEMTIME is a time record broken down into parts. It is good for people to read and for fixing problems. Its structure is clear, but its meaning is not always clear. You must use it with the right API rules about time zones.

FILETIME

  • Type: Struct (2 DWORDs)
  • What it is: A 64-bit number showing time. It counts 100-nanosecond steps since January 1, 1601 (UTC).
  • Time Zone: Always UTC
  • Used by: File system details, event records, computer processes.
    FILETIME is hard for people to understand. It is mainly for computer operations. Handle it with care. Do not change it by hand unless you truly understand how it works.

The Correct Order of Operations

A common problem in time changes is getting the order wrong. If you start with a DATE and want a local FILETIME in the end, do this:

  1. Use VariantTimeToSystemTime() to change it to a SYSTEMTIME in UTC.
  2. And then, call SystemTimeToFileTime() to get a FILETIME that is in UTC.
  3. Lastly, call FileTimeToLocalFileTime() to change the UTC FILETIME to a local FILETIME.

✨ Example Code ✨

DATE variantDate = /* comes from Excel Automation */;
SYSTEMTIME utcSystemTime;
FILETIME utcFileTime, localFileTime;

// Step 1
VariantTimeToSystemTime(variantDate, &utcSystemTime);

// Step 2
SystemTimeToFileTime(&utcSystemTime, &utcFileTime);

// Step 3
FileTimeToLocalFileTime(&utcFileTime, &localFileTime);

Each step uses the one before it. This keeps each value correct. This way is safe, can be done many times, and knows about time zones.


Common Pitfall: Misinterpreting VariantTime

A common problem happens when people do not understand the time for DATE values from Excel or COM programs. Programmers often think these are local times. Then they use change functions that expect UTC. This makes the results wrong.

For example:

DATE dt = ...; // From Excel
SYSTEMTIME st;
VariantTimeToSystemTime(dt, &st); // Assumes dt is UTC, not local

So, if you then call SystemTimeToFileTime() and think it is local time, you will get a time that is too early or too late. This might be off by a full time zone, like 8 hours in PST.

✅ Best Practice

Only change to local time using FileTimeToLocalFileTime at the very end. Never skip or change the order of steps. Do not guess about the data you get.


Time Zones and the Windows System Clock

When Windows figures out "local time," it uses the computer's time zone settings and its own DST rules.

The API GetTimeZoneInformation() gives you current time differences, DST markers, and past changes. Inside, FileTimeToLocalFileTime uses this data. This means the same code might give different results on computers with different settings.

This difference is very important for:

  • Programs used in many countries
  • Servers set to run tasks in UTC
  • Programs that change time zones using the registry

💡 Tip: To prevent unexpected problems when using programs in different places, think about changing everything to UTC inside your program. Then only make it local when you show it.


Debugging Timestamp Differences

When problems show up, detailed logs are your best help.

You can check each step of the time change process with clear messages:

printf("SYSTEMTIME UTC: %04d-%02d-%02d %02d:%02d:%02d\n",
    utcSystemTime.wYear, utcSystemTime.wMonth, utcSystemTime.wDay,
    utcSystemTime.wHour, utcSystemTime.wMinute, utcSystemTime.wSecond);

printf("FILETIME UTC: %08x%08x\n",
    utcFileTime.dwHighDateTime, utcFileTime.dwLowDateTime);

printf("FILETIME Local: %08x%08x\n",
    localFileTime.dwHighDateTime, localFileTime.dwLowDateTime);

🛠️ PowerShell is also helpful for viewing or reverse-engineering values:

[datetime]::FromFileTimeUtc(132632832000000000)
[datetime]::FromFileTime(132632832000000000) # Interprets as local

Compare these tests in different computer setups to make sure they are correct.


Impact of Daylight Saving Time on Conversions

Daylight Saving Time (DST) makes things harder. It only applies when you change UTC to local time, not before.

  • SystemTimeToFileTime and VariantTimeToSystemTime assume UTC for what they take and give. No DST is used.
  • FileTimeToLocalFileTime, however, uses the computer's time zone rules to apply DST. It does this if the UTC FILETIME is within a period when DST was or is active.

So, it is very important not to add local time info too early. During DST change weeks, even one wrong step can shift times by an hour. This can mess up planned tasks or how logs are read.


Hands-on Example: Fixing a Broken Conversion Chain

Let's look at a bad example and fix it.

❌ Buggy Approach (Incorrect Conversion Order):

DATE dateFromExcel = /* assumed to be local (wrong) */;
SYSTEMTIME localSystemTime;
VariantTimeToSystemTime(dateFromExcel, &localSystemTime);
SystemTimeToFileTime(&localSystemTime, &fileTime);

Here, the DATE is really UTC, but people read it as local. And then SystemTimeToFileTime() saves it without checking it is UTC. This makes a wrong FILETIME.

✅ Correct Approach:

DATE dateFromExcel = ...;
SYSTEMTIME utcTime;
FILETIME utcFileTime, localFileTime;

VariantTimeToSystemTime(dateFromExcel, &utcTime);
SystemTimeToFileTime(&utcTime, &utcFileTime);
FileTimeToLocalFileTime(&utcFileTime, &localFileTime);

This keeps UTC correct until the last step. That is when it finally changes to local time.


Validation Tools & Tips

To make sure your time handling works well:

  • 🔍 Use Event Viewer timestamps for known-good comparison
  • 🧪 PowerShell:
    [datetime]::FromFileTimeUtc(utcValue)
    [datetime]::FromFileTime(localValue)
    
  • 🔃 Explicitly test across different time zones:
    • Change system time zones
    • Simulate DST changes
  • 🧾 Compare GetSystemTime() vs GetLocalTime() in custom logs

Make a small set of tests for each change. Do this especially for tricky cases like leap years or DST change times.


Best Practices for Reliable Date Handling in Win32

  1. 📌 Always assume FILETIME, SYSTEMTIME, and OLE DATE are UTC. Do this unless you are clearly told otherwise.
  2. 🎯 Use FileTimeToLocalFileTime() as the last step to change to local time.
  3. 🔒 Never write in fixed time zone differences, like "-8" hours. Use system functions instead.
  4. 📘 Write down each step in your change process.
  5. 🧪 Make tests for changes in different time zones and DST times.
  6. 🔍 Use logs and viewing tools (like Event Viewer and PowerShell) when fixing problems.

Convert with Confidence

Handling time changes badly can cause big problems, from wrong times in logs to tasks that don't run and confusing screen info. But if you learn VariantTimeToSystemTime, SystemTimeToFileTime, and FileTimeToLocalFileTime well, you will clearly control every time record your program uses. Always use UTC inside your program. Check it in different time zones. And do not make it local until you show it. With these good steps, your time changes will finally be correct.

Need more clarity or running into edge cases? Dig deeper into the official docs:


Citations

Microsoft. (n.d.). SystemTimeToFileTime function. Retrieved from https://learn.microsoft.com/en-us/windows/win32/api/timezoneapi/nf-timezoneapi-systemtimetofiletime

Microsoft. (n.d.). FileTimeToLocalFileTime function. Retrieved from https://learn.microsoft.com/en-us/windows/win32/api/timezoneapi/nf-timezoneapi-filetimetolocalfiletime

Microsoft. (n.d.). DATE data type (VariantTime). Retrieved from https://learn.microsoft.com/en-us/cpp/cpp/date-data-type

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading