Ruby - How to Parse Windows FILETIME

June 15, 2024

Back to Articles

When working with the Microsoft Windows system you may have encountered an interesting timestamp that approximates 130 quadrillion or "130000000000000000". This is known as the Windows FILETIME format, and it represents the number of 100-nanosecond intervals since January 1st, 1601.

Why did Windows decide on this convention? How does it relate to the UNIX time epoch (which is January 1st, 1970), and most importantly, how can it be parsed by Ruby? All those questions will be answered in this article. After that, I'll introduce a small gem I wrote that will provide you with a convenient method for those who don't want to write their own algorithm.

Why 1601?

First of all, this date is not unique to Windows. It's also the starting date for ANSI Cobol. This blog postwritten by Raymond Chen at Microsoft explains some of the reasoning behind it.

What about the UNIX Epoch?

The UNIX epoch starts on January 1st, 1970. MacOS and Linux operating systems base time off of the number of seconds since this date. So does the Ruby Time class, which can be easily seen with the :at method.

Time.at(0).utc
# => 1970-01-01 00:00:00 UTC

Time.at(60).utc
# => 1970-01-01 00:00:01 UTC

How can I parse Windows FILETIME using Ruby's Time class?

The Time.at method will not work. For instance, this is the result of me trying to parse my current time in the Windows FILETIME format using Ruby's built-in method.

Time.at(133629585330000000)
# => 4234554956-04-18 05:20:00 UTC

Since I'm not writing this in the year 4 billion, it appears that we need to come up with an algorithm to convert this to a format Ruby can recognize. This can be done in three simple steps: 1.) obtain the multiplier to convert 100-nanosecond intervals to seconds, 2.) find the difference in seconds between 1601 and 1970, and 3.) write a conversion method implementing these constants.

  1. Obtain the Multiplier

A nanosecond is one billionth of a second or 1 / 1,000,000,000. That means that 100 nanoseconds is 10 millionths of a second or 1 / 10,000,000. There's our multiplier. First we need to set this multiplier as a constant.

MULTIPLIER = 10_000_000
  1. Find the Difference in Seconds

I'm sure this could be done using math, but actually Ruby can do this for us. We will parse the date, convert it to time, and format it in seconds.

Date.parse('01-01-1601').strftime('%s')
# => "-11644473600"

This tells us that 1601 was about 11 billion seconds before 1970. Just to be sure, let's see what happens when Ruby parses this negative number.

Time.at(-11644473600).utc
# => 1601-01-01 00:00:00 UTC

Perfect. Now, we will set this negative integer as another constant.

DIFFERENCE => -11644473600 
  1. Write a Ruby Algorithm

We will call this method at_winft because it's similar to the Time.at method. The suffix winft stands for Windows FILETIME. (This is the method name I chose in my gem "winft" 🙂). Ruby let's us add methods to its core classes.

class TIME
  MULTIPLIER = 10_000_000
  DIFFERENCE = -11644473600

  def self.at_winft(filetime)
    # notice this is a class method like `:at`
  end
end

To allow for an Integer or String argument, we will convert the filetime to an integer for parsing. Then, we will divide it by our MULTIPLIER so that it represents seconds. After that, we will add the difference (which will be subtracting) to account for the time between 1970 and 1601. All of this will be parsed by the original :at method.

def self.at_winft(filetime)
  at((filetime.to_i / MULTIPLIER) + DIFFERENCE)
end

Finally, I will test this by obtaining my current time in the Windows FILETIME format (which is "133629601790000000") and parsing it with this new algorithm.

Time.at_winft('133629601790000000').utc
# => 2024-06-15 21:22:59 UTC

It works! To use this method in your project, you can use the winft gem. Simply add winft to your Gemfile, or run gem install winft.

-🥑

Back to Articles