Windows FILETIME Converter
Convert 64-bit Windows FILETIME and Active Directory timestamps to human-readable dates and back, with support for hex, decimal, and LDAP formats.
Want to learn more?
Learn about Windows FILETIME and how it relates to Unix timestamps, epoch time, and Active Directory.
Read the guideConvert Windows FILETIME values to readable dates — and back again
You are looking at an Active Directory export, a registry dump or an event log, and one
of the columns holds a number like 133485408000000000. It is not a Unix
timestamp, it is not milliseconds, and pasting it into an ordinary epoch converter gives
you a date somewhere in the year 4 billion. That number is a Windows FILETIME, and this
tool turns it into a date you can act on.
Paste the value, pick a timezone, and the converted date appears immediately — no
button to press. The whole conversion runs in your browser using JavaScript
BigInt arithmetic, so nothing you paste leaves the machine. That matters
here more than it does for most converters, because FILETIME values usually arrive
attached to real account names out of a directory you are not supposed to be pasting into
a random web form.
What a FILETIME actually is
A FILETIME is a 64-bit integer counting 100-nanosecond intervals since 1 January 1601, 00:00:00 UTC. Two things about that definition explain almost every problem people have with it:
- The unit is tiny. There are 10,000,000 of these intervals in a single second, which is why any FILETIME from the modern era is an 18-digit number. The size is not an error or a corrupted field.
- The epoch is 1601, not 1970. Microsoft chose 1601 because it is the start of the first 400-year cycle of the Gregorian calendar that precedes the practical range of Windows dates. Everything else in computing counts from 1970, so a FILETIME is offset from a Unix timestamp by a fixed constant.
That constant is 11644473600 seconds — the number of seconds between
1601-01-01 and 1970-01-01. It is worth memorising if you work with Windows data, because
the two conversion formulas are built entirely out of it and the factor of ten million:
| Direction | Formula |
|---|---|
| FILETIME → Unix seconds | (filetime / 10000000) - 11644473600 |
| Unix seconds → FILETIME | (unix + 11644473600) * 10000000 |
A worked conversion, both ways
Take the value at the top of this page, 133485408000000000:
- Divide by 10,000,000:
133485408000000000 / 10000000 = 13348540800seconds since 1601. - Subtract the epoch offset:
13348540800 - 11644473600 = 1704067200seconds since 1970. 1704067200is 1 January 2024, 00:00:00 UTC.
Now run it backwards from a date. For 15 January 2025 at 12:30:45 UTC, the Unix
timestamp is 1736944245:
- Add the offset:
1736944245 + 11644473600 = 13381417845. - Multiply by 10,000,000:
133814178450000000. - In hexadecimal, the form you will see in a registry editor or an LDAP dump, that is
0x1DB67494C6C4880.
The tool accepts either notation on input. A value beginning 0x is treated
as hexadecimal automatically, and both representations of whatever you paste are shown
side by side in the results, so you can copy whichever one the next system wants.
The special values: 0 and 0x7FFFFFFFFFFFFFFF
Two FILETIME values are not timestamps at all, and Active Directory uses both heavily. If you convert them arithmetically you get nonsense — the year 1601 and the year 30828 respectively — so the tool intercepts them and tells you what they mean instead of producing a date.
| Value | Hex | Meaning |
|---|---|---|
0 | 0x0 | Never / not set. The attribute has no value yet. |
9223372036854775807 | 0x7FFFFFFFFFFFFFFF | Never expires. The maximum signed 64-bit integer, used as a sentinel. |
The distinction bites hardest on accountExpires, where both values
mean “this account never expires”. Which one you see depends on how the account
was created: the AD Users and Computers console writes 0 when you tick
“Never”, while accounts provisioned through some scripts and migrations carry
the max-int sentinel. Any report that filters on
accountExpires = 0 alone will quietly miss half the never-expiring accounts in
the domain. Check for both.
On lastLogon and pwdLastSet, a zero is more interesting than it
looks. pwdLastSet = 0 is the flag that forces a password change at next logon.
A lastLogon of zero does not mean the account has never been used — it
means it has never been used on the domain controller you queried, which is a very
different claim.
Where you will run into FILETIME
The format is not confined to Active Directory, but AD is where most people meet it. These attributes all store FILETIME:
| Attribute | What it records |
|---|---|
lastLogon | Last interactive logon, held per domain controller and never replicated |
lastLogonTimestamp | Replicated logon time, updated lazily — roughly every 14 days |
pwdLastSet | When the password was last changed |
accountExpires | Account expiry, or a “never” sentinel |
badPasswordTime | Last failed authentication attempt |
lockoutTime | When the account was locked out |
Outside the directory, FILETIME turns up in NTFS file timestamps, in registry values such
as USB device connection history and software install dates, in the SystemTime
and TimeCreated fields of EVTX event records, and across the forensic artefacts
built on top of them — prefetch files, LNK shortcuts, jump lists and shellbags. If you
are triaging a host and a timestamp field is an unexplained 18-digit integer, FILETIME is
the first thing to try.
The stale-account audit is the single most common reason people land here. Pull
lastLogonTimestamp for every user, convert the column, and anything older than
your threshold is a candidate for disabling — with the caveat that the attribute is
deliberately imprecise. It only replicates after a lag, so a date up to a fortnight behind
reality is expected behaviour, not a bug. For anything that needs to be accurate to the
hour you have to query lastLogon on every domain controller and take the
latest, because no single DC holds the whole picture.
Converting a whole column at once
Auditing rarely involves one timestamp. Switch to batch mode, paste one FILETIME per line straight out of your CSV or PowerShell output, and every row is converted into a table you can read down. Special values are labelled in place rather than silently producing a 1601 date, and unparseable lines are flagged individually instead of failing the whole batch — so a stray header row or a blank cell does not cost you the run.
The timezone selector applies to the whole conversion. UTC is the right default for incident timelines and anything you intend to correlate with logs from another system; switch to your local zone only when you are about to talk to a human about when something happened. The tool also detects your browser timezone on load and shows both the UTC and local renderings of every result, along with a relative age (“3 months ago”), which is usually the fastest way to eyeball whether an account is stale.
Doing it yourself, in the shell
For one-off checks you do not need a converter at all. PowerShell ships the conversion built in:
[DateTime]::FromFileTimeUtc(133485408000000000)gives you the UTC date;FromFileTimegives local time.- Going the other way,
(Get-Date).ToFileTimeUtc()produces the current FILETIME. - Reading it straight off an account:
Get-ADUser jsmith -Properties lastLogon | Select @{n='LastLogon';e={[DateTime]::FromFileTime($_.lastLogon)}}.
In Python there is no built-in, so you apply the formula:
datetime(1601,1,1) + timedelta(microseconds=filetime//10), dividing by 10
because a microsecond is ten 100-nanosecond ticks. In .NET, DateTime.FromFileTimeUtc
and DateTime.ToFileTimeUtc are the pair. The tool shows equivalent snippets for
PowerShell, Python, C# and JavaScript on the page.
Precision, range and other honest limits
FILETIME carries 100-nanosecond resolution, but almost nothing populates it that finely. The Windows system clock is typically updated on a timer tick of around 15.6 milliseconds, so the trailing digits of a real-world FILETIME are usually zeros or noise rather than meaningful precision. This converter divides down to whole seconds before formatting, which means the sub-second remainder is not shown. For account auditing and event correlation that is irrelevant; if you are doing sub-second forensic sequencing, work with the raw integer and compare the values directly rather than the rendered dates.
On range: the arithmetic is done with BigInt, so there is no 53-bit floating
point rounding of the kind that silently corrupts large timestamps in naive JavaScript
implementations. Values that resolve outside the year 1–9999 window are rejected with an
explicit error rather than being rendered as a plausible-looking wrong date. The theoretical
maximum of a signed 64-bit FILETIME lands in the year 30828, but you will only ever see that
value as the “never expires” sentinel described above.
Byte order: the trap in registry dumps
At the API level a FILETIME is not a single integer — it is a struct of two 32-bit
halves, dwLowDateTime and dwHighDateTime. On x86 and x64 those are
stored little-endian, so a raw hex dump of an eight-byte FILETIME shows the bytes in reverse
order from how you would write the number. A value that reads 00 C0 89 76 45 3C DA 01
in a binary editor is 0x01DA3C457689C000 as a number, which is the
1 January 2024 example above. If a hex FILETIME converts to something absurd — a date in
the 1600s, or thousands of years out — reversing the byte order is the first thing to
try. Tools that read the registry for you (reg query, PowerShell’s
Get-ItemProperty) hand you the assembled number and this does not apply; raw
hive parsing and hex editors are where it bites.
Questions people actually ask
- Is my data uploaded anywhere? No. The parsing, the arithmetic and the formatting all happen in your browser. There is no server call in the conversion path, so the account names and timestamps in an AD export stay on your machine.
- Why does the date come out in 1601? Because the value was zero, or close to it. That is the epoch itself, and it means “not set”, not “seventeenth century”.
- Why is my converted date the year 4 billion? You put a FILETIME into a Unix-epoch converter. It read ten-million-ticks-per-second as one-per-second.
- Can I link to a conversion? Yes — the value is mirrored into the URL, so a converted timestamp can be pasted into a ticket or a chat and it opens already converted for whoever clicks it.
- Does daylight saving affect the number? Never. A FILETIME is always UTC. Only the rendering you choose is affected, which is exactly why UTC is the safe choice for anything you plan to correlate later.
If the timestamp you are holding turns out to be seconds or milliseconds since 1970 rather than a FILETIME — the giveaway is a 10- or 13-digit number rather than an 18-digit one — our Unix timestamp converter handles that format instead.
Convert Windows FILETIME values to readable dates — and back again
You are looking at an Active Directory export, a registry dump or an event log, and one
of the columns holds a number like 133485408000000000. It is not a Unix
timestamp, it is not milliseconds, and pasting it into an ordinary epoch converter gives
you a date somewhere in the year 4 billion. That number is a Windows FILETIME, and this
tool turns it into a date you can act on.
Paste the value, pick a timezone, and the converted date appears immediately — no
button to press. The whole conversion runs in your browser using JavaScript
BigInt arithmetic, so nothing you paste leaves the machine. That matters
here more than it does for most converters, because FILETIME values usually arrive
attached to real account names out of a directory you are not supposed to be pasting into
a random web form.
What a FILETIME actually is
A FILETIME is a 64-bit integer counting 100-nanosecond intervals since 1 January 1601, 00:00:00 UTC. Two things about that definition explain almost every problem people have with it:
- The unit is tiny. There are 10,000,000 of these intervals in a single second, which is why any FILETIME from the modern era is an 18-digit number. The size is not an error or a corrupted field.
- The epoch is 1601, not 1970. Microsoft chose 1601 because it is the start of the first 400-year cycle of the Gregorian calendar that precedes the practical range of Windows dates. Everything else in computing counts from 1970, so a FILETIME is offset from a Unix timestamp by a fixed constant.
That constant is 11644473600 seconds — the number of seconds between
1601-01-01 and 1970-01-01. It is worth memorising if you work with Windows data, because
the two conversion formulas are built entirely out of it and the factor of ten million:
| Direction | Formula |
|---|---|
| FILETIME → Unix seconds | (filetime / 10000000) - 11644473600 |
| Unix seconds → FILETIME | (unix + 11644473600) * 10000000 |
A worked conversion, both ways
Take the value at the top of this page, 133485408000000000:
- Divide by 10,000,000:
133485408000000000 / 10000000 = 13348540800seconds since 1601. - Subtract the epoch offset:
13348540800 - 11644473600 = 1704067200seconds since 1970. 1704067200is 1 January 2024, 00:00:00 UTC.
Now run it backwards from a date. For 15 January 2025 at 12:30:45 UTC, the Unix
timestamp is 1736944245:
- Add the offset:
1736944245 + 11644473600 = 13381417845. - Multiply by 10,000,000:
133814178450000000. - In hexadecimal, the form you will see in a registry editor or an LDAP dump, that is
0x1DB67494C6C4880.
The tool accepts either notation on input. A value beginning 0x is treated
as hexadecimal automatically, and both representations of whatever you paste are shown
side by side in the results, so you can copy whichever one the next system wants.
The special values: 0 and 0x7FFFFFFFFFFFFFFF
Two FILETIME values are not timestamps at all, and Active Directory uses both heavily. If you convert them arithmetically you get nonsense — the year 1601 and the year 30828 respectively — so the tool intercepts them and tells you what they mean instead of producing a date.
| Value | Hex | Meaning |
|---|---|---|
0 |
0x0 |
Never / not set. The attribute has no value yet. |
9223372036854775807 |
0x7FFFFFFFFFFFFFFF |
Never expires. The maximum signed 64-bit integer, used as a sentinel. |
The distinction bites hardest on accountExpires, where both values
mean “this account never expires”. Which one you see depends on how the account
was created: the AD Users and Computers console writes 0 when you tick
“Never”, while accounts provisioned through some scripts and migrations carry
the max-int sentinel. Any report that filters on
accountExpires = 0 alone will quietly miss half the never-expiring accounts in
the domain. Check for both.
On lastLogon and pwdLastSet, a zero is more interesting than it
looks. pwdLastSet = 0 is the flag that forces a password change at next logon.
A lastLogon of zero does not mean the account has never been used — it
means it has never been used on the domain controller you queried, which is a very
different claim.
Where you will run into FILETIME
The format is not confined to Active Directory, but AD is where most people meet it. These attributes all store FILETIME:
| Attribute | What it records |
|---|---|
lastLogon | Last interactive logon, held per domain controller and never replicated |
lastLogonTimestamp | Replicated logon time, updated lazily — roughly every 14 days |
pwdLastSet | When the password was last changed |
accountExpires | Account expiry, or a “never” sentinel |
badPasswordTime | Last failed authentication attempt |
lockoutTime | When the account was locked out |
Outside the directory, FILETIME turns up in NTFS file timestamps, in registry values such
as USB device connection history and software install dates, in the SystemTime
and TimeCreated fields of EVTX event records, and across the forensic artefacts
built on top of them — prefetch files, LNK shortcuts, jump lists and shellbags. If you
are triaging a host and a timestamp field is an unexplained 18-digit integer, FILETIME is
the first thing to try.
The stale-account audit is the single most common reason people land here. Pull
lastLogonTimestamp for every user, convert the column, and anything older than
your threshold is a candidate for disabling — with the caveat that the attribute is
deliberately imprecise. It only replicates after a lag, so a date up to a fortnight behind
reality is expected behaviour, not a bug. For anything that needs to be accurate to the
hour you have to query lastLogon on every domain controller and take the
latest, because no single DC holds the whole picture.
Converting a whole column at once
Auditing rarely involves one timestamp. Switch to batch mode, paste one FILETIME per line straight out of your CSV or PowerShell output, and every row is converted into a table you can read down. Special values are labelled in place rather than silently producing a 1601 date, and unparseable lines are flagged individually instead of failing the whole batch — so a stray header row or a blank cell does not cost you the run.
The timezone selector applies to the whole conversion. UTC is the right default for incident timelines and anything you intend to correlate with logs from another system; switch to your local zone only when you are about to talk to a human about when something happened. The tool also detects your browser timezone on load and shows both the UTC and local renderings of every result, along with a relative age (“3 months ago”), which is usually the fastest way to eyeball whether an account is stale.
Doing it yourself, in the shell
For one-off checks you do not need a converter at all. PowerShell ships the conversion built in:
[DateTime]::FromFileTimeUtc(133485408000000000)gives you the UTC date;FromFileTimegives local time.- Going the other way,
(Get-Date).ToFileTimeUtc()produces the current FILETIME. - Reading it straight off an account:
Get-ADUser jsmith -Properties lastLogon | Select @{n='LastLogon';e={[DateTime]::FromFileTime($_.lastLogon)}}.
In Python there is no built-in, so you apply the formula:
datetime(1601,1,1) + timedelta(microseconds=filetime//10), dividing by 10
because a microsecond is ten 100-nanosecond ticks. In .NET, DateTime.FromFileTimeUtc
and DateTime.ToFileTimeUtc are the pair. The tool shows equivalent snippets for
PowerShell, Python, C# and JavaScript on the page.
Precision, range and other honest limits
FILETIME carries 100-nanosecond resolution, but almost nothing populates it that finely. The Windows system clock is typically updated on a timer tick of around 15.6 milliseconds, so the trailing digits of a real-world FILETIME are usually zeros or noise rather than meaningful precision. This converter divides down to whole seconds before formatting, which means the sub-second remainder is not shown. For account auditing and event correlation that is irrelevant; if you are doing sub-second forensic sequencing, work with the raw integer and compare the values directly rather than the rendered dates.
On range: the arithmetic is done with BigInt, so there is no 53-bit floating
point rounding of the kind that silently corrupts large timestamps in naive JavaScript
implementations. Values that resolve outside the year 1–9999 window are rejected with an
explicit error rather than being rendered as a plausible-looking wrong date. The theoretical
maximum of a signed 64-bit FILETIME lands in the year 30828, but you will only ever see that
value as the “never expires” sentinel described above.
Byte order: the trap in registry dumps
At the API level a FILETIME is not a single integer — it is a struct of two 32-bit
halves, dwLowDateTime and dwHighDateTime. On x86 and x64 those are
stored little-endian, so a raw hex dump of an eight-byte FILETIME shows the bytes in reverse
order from how you would write the number. A value that reads 00 C0 89 76 45 3C DA 01
in a binary editor is 0x01DA3C457689C000 as a number, which is the
1 January 2024 example above. If a hex FILETIME converts to something absurd — a date in
the 1600s, or thousands of years out — reversing the byte order is the first thing to
try. Tools that read the registry for you (reg query, PowerShell’s
Get-ItemProperty) hand you the assembled number and this does not apply; raw
hive parsing and hex editors are where it bites.
Questions people actually ask
- Is my data uploaded anywhere? No. The parsing, the arithmetic and the formatting all happen in your browser. There is no server call in the conversion path, so the account names and timestamps in an AD export stay on your machine.
- Why does the date come out in 1601? Because the value was zero, or close to it. That is the epoch itself, and it means “not set”, not “seventeenth century”.
- Why is my converted date the year 4 billion? You put a FILETIME into a Unix-epoch converter. It read ten-million-ticks-per-second as one-per-second.
- Can I link to a conversion? Yes — the value is mirrored into the URL, so a converted timestamp can be pasted into a ticket or a chat and it opens already converted for whoever clicks it.
- Does daylight saving affect the number? Never. A FILETIME is always UTC. Only the rendering you choose is affected, which is exactly why UTC is the safe choice for anything you plan to correlate later.
If the timestamp you are holding turns out to be seconds or milliseconds since 1970 rather than a FILETIME — the giveaway is a 10- or 13-digit number rather than an 18-digit one — our Unix timestamp converter handles that format instead.
You build the idea. I'll ship the product.
Productized MVP development for founders. 9 SaaS apps shipped — yours could be next, in 6 weeks. Secure by default.
Explore More Tools
Continue with these related tools
ℹ️ Disclaimer
This tool is provided for informational and educational purposes only. All processing happens entirely in your browser - no data is sent to or stored on our servers. While we strive for accuracy, we make no warranties about the completeness or reliability of results. Use at your own discretion.