⭐ Starlight Tools / Mac HFS+ Timestamp

Mac HFS+ Timestamp Converter

Convert HFS+ seconds (since 1904-01-01 UTC) to human-readable dates — and back. Accepts hex (0x…) or decimal. Fully client-side.

Related Tools

UNIX Timestamp Converter · Apple Cocoa / Core Foundation Time · LDAP / FILETIME Converter · Unix Hex Timestamp

Results always include UTC; this controls the extra local view.
UNIX ↔ HFS+ offset: 2,082,844,800 seconds (to/from 1970-01-01 UTC)

1) HFS+ Seconds → Date

Hex with/without 0x is accepted. Negative values supported.
If you pasted a 4-byte hex like 11 22 33 44 read little-endian, toggle swap.

2) Date → HFS+ Seconds

Interpreted in your current local time zone.
If an offset is provided, it will be respected.
Unsigned view matches classic 32-bit HFS+ fields (wraps at 2^32).

About HFS+ Time

HFS+ stores timestamps as whole seconds since the Mac epoch 1904-01-01T00:00:00Z. Compared to UNIX (1970-01-01T00:00:00Z), the exact gap is 2,082,844,800 seconds.

  • HFS+ → UNIX seconds: unix = hfs - 2,082,844,800
  • UNIX seconds → HFS+: hfs = unix + 2,082,844,800
  • Endianness: On-disk HFS+ structures are big-endian; byte order matters when reading raw bytes.
  • Range: Classic 32-bit fields wrap at 2^32 (≈ 2106-02-07 for UNIX).

HFS+ Time: Code Snippets

Copy-ready examples for converting between HFS+ seconds (since 1904-01-01 UTC) and human time.

Swift

let HFS_OFFSET: TimeInterval = 2_082_844_800 // seconds
func hfsToDate(_ hfs: TimeInterval) -> Date { Date(timeIntervalSince1970: hfs - HFS_OFFSET) }
func dateToHfs(_ date: Date) -> TimeInterval { date.timeIntervalSince1970 + HFS_OFFSET }

// Hex helper
func parseHexOrDec(_ s: String) -> Int64 {
    let t = s.trimmingCharacters(in: .whitespacesAndNewlines)
    if t.lowercased().hasPrefix("0x") { return Int64(t.dropFirst(2), radix:16)! }
    if t.range(of: "[a-fA-F]", options:.regularExpression) != nil { return Int64(t, radix:16)! }
    return Int64(t)!
}

Objective-C

static const NSTimeInterval kHFSOffset = 2082844800.0;
NSDate * HFSPlusToDate(long long hfs){ return [NSDate dateWithTimeIntervalSince1970:(hfs - kHFSOffset)]; }
long long DateToHFSPlus(NSDate *d){ return llround([d timeIntervalSince1970] + kHFSOffset); }

JavaScript

const HFS_OFFSET = 2082844800n; // seconds
function floorDiv(a,b){ const q=a/b, r=a%b; return r===0n || (a>=0n)===(b>=0n) ? q : q-1n; }
function parseIntFlexible(s){
  s = s.trim();
  if (/^-?0x[0-9a-f]+$/i.test(s)) return BigInt(s);
  if (/^-?[0-9a-f]+$/i.test(s) && /[a-f]/i.test(s)) return BigInt((s[0]==='-'?'-':'') + '0x' + s.replace(/^-/,'')); 
  if (/^-?\d+$/.test(s)) return BigInt(s);
  throw new Error('bad int');
}
function hfsToDate(s){ const hfs = parseIntFlexible(s); const ms = (hfs - HFS_OFFSET) * 1000n; return new Date(Number(ms)); }
function dateToHfs(d=new Date()){ const ms=BigInt(d.getTime()); const sec=floorDiv(ms,1000n); return (sec + HFS_OFFSET).toString(); }

Python

import datetime, re
HFS_OFFSET = 2_082_844_800
def parse_int_flexible(s:str)->int:
    s=s.strip()
    if s.lower().startswith('-0x'): return -int(s[3:],16)
    if s.lower().startswith('0x'):  return int(s[2:],16)
    if re.search(r'[a-f]', s, re.I): return int(s,16)
    return int(s,10)
def hfs_to_datetime(v:int)->datetime.datetime:
    return datetime.datetime.fromtimestamp(v - HFS_OFFSET, tz=datetime.timezone.utc)
def datetime_to_hfs(dt:datetime.datetime)->int:
    if dt.tzinfo is None: dt = dt.astimezone()
    return int(dt.timestamp()) + HFS_OFFSET

C (32-bit unsigned view)

#include <time.h> #include <stdint.h>
#define HFS_OFFSET 2082844800u
time_t hfs_to_unix(uint32_t hfs){ return (time_t)((uint32_t)hfs - HFS_OFFSET); }
uint32_t unix_to_hfs(time_t unix){ return (uint32_t)(unix + HFS_OFFSET); }

Byte-swap tip

If a 4-byte hex was read little-endian (e.g., 44 33 22 11) but HFS+ expects big-endian, swap to 11223344 before converting.