Windows FILETIME / Active Directory Timestamp Converter
Convert between Windows FILETIME — the 100-nanosecond-since-1601 format behind lastLogonTimestamp, pwdLastSet and accountExpires — and ordinary Unix time, in both directions.
Result
Common AD attributes using FILETIME
| Attribute | Meaning |
|---|---|
| lastLogonTimestamp | Replicated, approximate last interactive logon |
| pwdLastSet | When the password was last changed; 0 means change required at next logon |
| accountExpires | When the account expires; 0 or Int64 max both mean never |
| lockoutTime | When the account was locked out; 0 means not locked out |
How to use this tool
- Switch modes depending on which direction you are converting — a human date into a FILETIME to search for in LDAP, or a raw FILETIME pulled from an attribute back into a readable date.
- Dates are always treated as UTC, matching how Active Directory stores and compares these attributes internally — there is no time zone offset to configure.
- If you paste a FILETIME and get “never / not set” or “never expires”, that is not an error — 0 and the Int64 maximum are reserved sentinel values, not real dates.
- Use the reference table to check what a given attribute’s sentinel value actually means —
pwdLastSetandaccountExpirestreat 0 differently from most other attributes. - The Unix seconds and milliseconds values are what you will typically feed into scripting languages that do not have native FILETIME support.
Windows FILETIME counts 100-nanosecond intervals elapsed since 1601-01-01 00:00:00 UTC. The starting epoch of 1601 is not arbitrary — it is the start of the first 400-year Gregorian calendar cycle prior to when the format was defined, chosen so the cycle’s leap-year arithmetic works out cleanly. Active Directory stores several timestamp attributes this way, including lastLogonTimestamp, pwdLastSet, accountExpires and lockoutTime, and every one of them is a plain integer when you read it through LDAP — there is no timezone metadata attached, because the value is already UTC by definition.
Converting to Unix time is a two-step unit change: divide the FILETIME value by 10,000,000 to get seconds, then subtract 11,644,473,600 — the number of seconds between 1601-01-01 and 1970-01-01. That constant is worth memorizing if you write LDAP tooling regularly; it shows up in almost every FILETIME conversion snippet you will find, in every language. Going the other direction just reverses the two steps: add the constant back, then multiply by 10,000,000.
The reason this tool uses BigInt rather than ordinary JavaScript numbers is precision. A FILETIME value routinely exceeds 253, the largest integer JavaScript’s Number type can represent exactly, so a naive conversion silently rounds and produces a date that is off by whole seconds — a real bug, not a theoretical one, and one that is easy to miss because the result still looks like a plausible date. The two sentinel values make this worse: 0 is unambiguous, but 9223372036854775807, the maximum signed 64-bit integer, is roughly 1.5 million times larger than what Number can represent exactly.
Both sentinels need explicit handling rather than being run through the date math. 0 conventionally means “no value has been set”, though pwdLastSet overloads it with an active meaning: a value of 0 there specifically flags that the user must change their password at next logon, distinct from the attribute simply never having been written. 9223372036854775807 conventionally means “never expires” on attributes like accountExpires — though some tools and some directories use 0 for that same meaning instead, so it is worth confirming which convention a given environment actually uses before writing automation against it.
lastLogonTimestamp deserves a specific caution: it is deliberately imprecise. To avoid replication storms across domain controllers, AD only updates it when it differs from the current value by more than a randomized window, typically averaging around 9 to 14 days. It is a good signal for finding accounts that are genuinely stale, and a bad signal for anything that needs to know the actual last logon time — use the non-replicated lastLogon attribute per-domain-controller for that, aggregated yourself.
Frequently asked questions
What is the exact FILETIME-to-Unix conversion formula?
Divide the FILETIME value by 10,000,000 to convert 100-nanosecond intervals to seconds, then subtract 11,644,473,600 to shift the epoch from 1601-01-01 to 1970-01-01. To go the other way, add that constant back and multiply by 10,000,000.
Why does this tool use BigInt instead of a normal number?
FILETIME values regularly exceed 253, the largest integer a JavaScript Number can represent exactly. Using ordinary numbers introduces silent rounding errors of whole seconds; BigInt keeps the arithmetic exact all the way through.
What does a FILETIME of 0 mean?
Generically, “not set”. On pwdLastSet it has an additional specific meaning: the account must change its password at next logon. It is not a real timestamp and should not be converted to a date.
What does 9223372036854775807 mean?
It is the maximum signed 64-bit integer, used as a “never expires” sentinel on attributes like accountExpires. Some environments use 0 for the same meaning instead, so check which convention applies before automating against it.
Why is lastLogonTimestamp off by several days from the real last logon?
By design. Active Directory only replicates updates to that attribute when the change exceeds a randomized threshold, usually averaging 9 to 14 days, specifically to avoid flooding replication traffic. For an accurate last-logon time, query the non-replicated lastLogon attribute on every domain controller and take the maximum.