Files
nexus/internal/leviathan/analysis/timestamps.go
T
Andy DixonandClaude Sonnet 5 1a380a0e99 Rebrand: Nexus Terminal → Alchementary Link
Renames the product across the codebase: Go module path, config/data
paths, keychain service name, export file extension, build tooling,
docs, and UI text. Sidebar brand mark now reads ">_ AlchementaryLink"
with "Alchementary" in the connection-group gold accent colour, and
the window title is "Alchementary Link".

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019T8bJS8BCtY5GeSG2HBj1n
2026-09-03 16:41:23 +01:00

107 lines
2.8 KiB
Go

package analysis
import (
"encoding/binary"
"math"
"time"
"github.com/andydixon/link/internal/leviathan/document"
)
// ScanTimestamps walks the range looking for plausible timestamps in
// common encodings. This is a heuristic: it reports any 4/8-byte
// window whose decoded time falls in [1990-01-01, 2050-12-31].
func ScanTimestamps(d *document.Document, start, length int64, maxHits int) (map[string]any, error) {
if length < 0 {
length = d.Size() - start
}
if maxHits <= 0 {
maxHits = 20_000
}
minT := time.Date(1990, 1, 1, 0, 0, 0, 0, time.UTC).Unix()
maxT := time.Date(2050, 12, 31, 0, 0, 0, 0, time.UTC).Unix()
ok := func(sec int64) bool { return sec >= minT && sec <= maxT }
hits := make([]map[string]any, 0, 256)
counts := map[string]int{}
const chunk = 1 << 20
end := start + length
overlap := int64(7) // enough for 8-byte types
for off := start; off < end && len(hits) < maxHits; {
winEnd := off + chunk
if winEnd > end {
winEnd = end
}
readEnd := winEnd + overlap
if readEnd > end {
readEnd = end
}
buf, err := d.Range(off, readEnd-off)
if err != nil {
return nil, err
}
limit := int(winEnd - off)
for i := 0; i < limit && len(hits) < maxHits; i += 4 {
if i+4 <= len(buf) {
vLE := int64(int32(binary.LittleEndian.Uint32(buf[i:])))
if ok(vLE) {
hits = append(hits, hit("unix_le", off+int64(i), 4, time.Unix(vLE, 0).UTC()))
counts["unix_le"]++
continue
}
vBE := int64(int32(binary.BigEndian.Uint32(buf[i:])))
if ok(vBE) {
hits = append(hits, hit("unix_be", off+int64(i), 4, time.Unix(vBE, 0).UTC()))
counts["unix_be"]++
continue
}
}
if i+8 <= len(buf) {
v := binary.LittleEndian.Uint64(buf[i:])
// Windows FILETIME
sec := int64(v/10_000_000) - filetimeEpochDelta
if ok(sec) {
hits = append(hits, hit("filetime_le", off+int64(i), 8, time.Unix(sec, 0).UTC()))
counts["filetime_le"]++
continue
}
// WebKit (microseconds since 1601)
sec = int64(v/1_000_000) - filetimeEpochDelta
if ok(sec) {
hits = append(hits, hit("webkit_le", off+int64(i), 8, time.Unix(sec, 0).UTC()))
counts["webkit_le"]++
continue
}
// OLE automation date
fdays := math.Float64frombits(v)
if fdays > 2 && fdays < 75000 {
base := time.Date(1899, 12, 30, 0, 0, 0, 0, time.UTC)
t := base.Add(time.Duration(fdays*86400) * time.Second)
if t.Unix() >= minT && t.Unix() <= maxT {
hits = append(hits, hit("ole_le", off+int64(i), 8, t))
counts["ole_le"]++
continue
}
}
}
}
off = winEnd
}
return map[string]any{
"hits": hits,
"counts": counts,
}, nil
}
func hit(kind string, off int64, size int, t time.Time) map[string]any {
return map[string]any{
"kind": kind,
"offset": off,
"length": size,
"iso": t.Format(time.RFC3339),
"unix": t.Unix(),
}
}