Apple / macOS / iOS timestamp formats¶
HFS+¶
HFS+ stores dates as unsigned 32-bit seconds since 1904-01-01 00:00:00 GMT, defined in Apple Technical Note TN1150 "HFS Plus Volume Format". The spec is explicit that this differs from classic HFS:
"These dates are stored in unsigned 32-bit integers … the number of seconds since midnight, January 1, 1904, GMT. This is slightly different from HFS, where the value represents local time. The maximum representable date is February 6, 2040 at 06:28:15 GMT. The date values do not account for leap seconds."
Two local-time caveats
- Classic Mac HFS stored LOCAL time with no offset — you cannot recover absolute UTC without knowing the machine's timezone at write time. HFS+ switched to GMT.
- The HFS+ volume-header creation date is itself an exception — TN1150 states it
"is NOT stored in GMT; it is stored in local time", so backup tools can use it as
a stable volume identifier. Do not normalise the volume
createDateto UTC.
- Range: epoch 1904-01-01 to 2040-02-06 06:28:15 GMT (the 2³² s overflow).
- Gotcha: some HFS+ auxiliary structures use a different epoch — e.g. the Hot Files B-tree time fields are "seconds since Jan 1, 1970 GMT". Verify the epoch per field.
Cocoa / CFAbsoluteTime / NSDate¶
The Core Foundation / "Mac absolute time" scale: seconds since 2001-01-01 00:00:00
UTC, stored as a signed IEEE-754 double. Apple's open-source CoreFoundation makes
the definition and constants explicit (CFDate.h / CFDate.c,
apple-oss-distributions/CF):
/* the reference date (epoch) is 00:00:00 1 January 2001. */
const CFTimeInterval kCFAbsoluteTimeIntervalSince1970 = 978307200.0L;
const CFTimeInterval kCFAbsoluteTimeIntervalSince1904 = 3061152000.0L;
NSDate / Swift Date wrap the same scale: timeIntervalSinceReferenceDate is the
CFAbsoluteTime; timeIntervalSince1970 = CFAbsoluteTime + 978307200. Human-facing docs:
CFAbsoluteTime.
- Signed: negative values are valid and mean before 2001; the fraction carries sub-second precision.
- Worked example: CFAbsoluteTime
700000000.0→ Unix1678307200→ 2023-03-08. - Gotcha: mis-reading the epoch as Unix-1970 yields a ~31-year error (the
978307200 offset is the tell). Small magnitudes are dates near 2001, not "epoch zero".
On disk it is 8 raw little-endian
doublebytes — a common mistake is reading them as an integer.
APFS¶
APFS inode timestamps are uint64 nanoseconds since 1970-01-01 00:00 UTC, per the
Apple File System Reference
(j_inode_val_t): create_time, mod_time, change_time, access_time, each "the
number of nanoseconds since January 1, 1970 at 0:00 UTC, disregarding leap seconds."
- Worked example:
1500000000000000000ns = 2017-07-14. - Gotcha: divide by 1 000 000 000 to reach Unix seconds. APFS gives a true four-stamp
(B/M/C/A) set at nanosecond resolution — finer than HFS+ (1 s) — so sub-second
event ordering is meaningful. (
access_timeupdates are flag-governed and not reliably wall-accurate.)
Core Data¶
Core Data persists Date/NSDate attributes in its SQLite store as the
CFAbsoluteTime value (the 2001-epoch signed double, in a REAL column). The scale is
the verified CFAbsoluteTime scale above; the specific on-disk column encoding is a strong
forensic convention rather than an Apple-documented guarantee — confirm empirically (read
the raw REAL, add 978307200, sanity-check).
Note
The same .sqlite file may also hold Unix-epoch integers in non-Core-Data tables.
Never assume a single epoch across all time columns — detect per column.
The Safari-vs-Chrome "WebKit" trap¶
"WebKit" is overloaded, and the two meanings differ by ~395 years and a 10⁶ unit scale:
| Source | Scale |
|---|---|
Apple Safari (the WebKit browser engine) — e.g. History.db visit_time |
CFAbsoluteTime: 2001 epoch, seconds (float) |
Chromium "WebKit time" (base::Time) — Chrome history/cookies |
1601 epoch, microseconds (int64) — see Unix/web/DB |
Danger
Identify the browser first. Reading a Safari visit_time (2001 epoch seconds) with
Chrome's 1601-epoch-microsecond rule — or vice versa — produces a wildly wrong date.
(Apple does not publish a History.db schema; the 2001-epoch double convention is
established forensic practice — verify empirically.)
Quick reference¶
| Format | Epoch | Unit | Type | TZ |
|---|---|---|---|---|
| Classic HFS | 1904-01-01 | seconds | UInt32 | local |
| HFS+ | 1904-01-01 GMT | seconds | UInt32 | UTC (vol-header createDate = local) |
| CFAbsoluteTime / NSDate | 2001-01-01 UTC | seconds | signed double | UTC |
| APFS | 1970-01-01 UTC | nanoseconds | uint64 | UTC |
Constants: kCFAbsoluteTimeIntervalSince1970 = 978307200;
kCFAbsoluteTimeIntervalSince1904 = 3061152000; 1904→1970 = 2 082 844 800 s.