diff --git a/format/apple/bookmark/apple_bookmark.go b/format/apple/bookmark/apple_bookmark.go index 3048394c..77a1cb36 100644 --- a/format/apple/bookmark/apple_bookmark.go +++ b/format/apple/bookmark/apple_bookmark.go @@ -332,7 +332,7 @@ func makeDecodeRecord() func(d *decode.D) { case dataTypeNumber64F: d.FieldF64("data") case dataTypeDate: - d.FieldF64BE("data", scalar.FltActualDate(cocoaTimeEpochDate, time.RFC3339)) + d.FieldF64BE("data", scalar.FltActualDateDescription(cocoaTimeEpochDate, time.Second, time.RFC3339)) case dataTypeBooleanFalse: case dataTypeBooleanTrue: case dataTypeArray: diff --git a/format/apple/bplist/bplist.go b/format/apple/bplist/bplist.go index 3c812fbe..8dfd9442 100644 --- a/format/apple/bplist/bplist.go +++ b/format/apple/bplist/bplist.go @@ -130,7 +130,7 @@ func decodeItem(d *decode.D, p *plist) bool { case elementTypeDate: n := 1 << decodeSize(d, d.UintAssert(4, 8)) d.FieldValueUint("size", uint64(n)) - d.FieldF("value", n*8, scalar.FltActualDate(cocoaTimeEpochDate, time.RFC3339)) + d.FieldF("value", n*8, scalar.FltActualDateDescription(cocoaTimeEpochDate, time.Second, time.RFC3339)) case elementTypeData: n := decodeSize(d) d.FieldValueUint("size", n) diff --git a/format/bitcoin/bitcoin_block.go b/format/bitcoin/bitcoin_block.go index 121f7284..f9efe1dd 100644 --- a/format/bitcoin/bitcoin_block.go +++ b/format/bitcoin/bitcoin_block.go @@ -66,7 +66,7 @@ func decodeBitcoinBlock(d *decode.D) any { d.FieldU32("version", scalar.UintHex) d.FieldRawLen("previous_block_hash", 32*8, rawHexReverse) d.FieldRawLen("merkle_root", 32*8, rawHexReverse) - d.FieldU32("time", scalar.UintActualUnixTime(time.RFC3339)) + d.FieldU32("time", scalar.UintActualUnixTimeDescription(time.Second, time.RFC3339)) d.FieldU32("bits", scalar.UintHex) d.FieldU32("nonce", scalar.UintHex) }) diff --git a/format/gzip/gzip.go b/format/gzip/gzip.go index 32d016d9..7f76d39b 100644 --- a/format/gzip/gzip.go +++ b/format/gzip/gzip.go @@ -76,7 +76,7 @@ func gzDecode(d *decode.D) any { hasComment = d.FieldBool("comment") d.FieldU3("reserved") }) - d.FieldU32("mtime", scalar.UintActualUnixTime(time.RFC3339)) + d.FieldU32("mtime", scalar.UintActualUnixTimeDescription(time.Second, time.RFC3339)) switch compressionMethod { case deflateMethod: d.FieldU8("extra_flags", deflateExtraFlagsNames) diff --git a/format/matroska/ebml/ebml.go b/format/matroska/ebml/ebml.go index ffd9a674..b728db75 100644 --- a/format/matroska/ebml/ebml.go +++ b/format/matroska/ebml/ebml.go @@ -1,5 +1,10 @@ package ebml +import "time" + +// 2001-01-01T00:00:00.000000000 UTC +var EpochDate = time.Date(2001, time.January, 1, 0, 0, 0, 0, time.UTC) + type ID int type Element interface { diff --git a/format/matroska/matroska.go b/format/matroska/matroska.go index 8ba25faf..1266a175 100644 --- a/format/matroska/matroska.go +++ b/format/matroska/matroska.go @@ -14,6 +14,7 @@ package matroska import ( "embed" "fmt" + "time" "github.com/wader/fq/format" "github.com/wader/fq/format/matroska/ebml" @@ -117,6 +118,8 @@ var lacingTypeNames = scalar.UintMapSymStr{ const tagSizeUnknown = 0xffffffffffffff +var sintActualMatroskaEpochDescription = scalar.SintActualDateDescription(ebml.EpochDate, time.Nanosecond, time.RFC3339) + func decodeLacingFn(d *decode.D, lacingType int, fn func(d *decode.D)) { if lacingType == lacingTypeNone { fn(d) @@ -383,28 +386,14 @@ func decodeMaster(d *decode.D, bitsLimit int64, elm *ebml.Master, unknownSize bo case *ebml.UTF8: d.FieldUTF8NullFixedLen("value", int(tagSize)) case *ebml.Date: - // TODO: - /* - proc type_date {size label _extra} { - set s [clock scan {2001-01-01 00:00:00}] - set frac 0 - switch $size { - 0 {} - 8 { - set nano [int64] - set s [clock add $s [expr $nano/1000000000] seconds] - set frac [expr ($nano%1000000000)/1000000000.0] - } - default { - bytes $size $label - return - } - } - - entry $label "[clock format $s] ${frac}s" $size [expr [pos]-$size] - } - */ - d.FieldRawLen("value", int64(tagSize)*8) + switch tagSize { + case 0: + d.FieldValueSint("value", 0, sintActualMatroskaEpochDescription) + case 8: + d.FieldS("value", int(tagSize)*8, sintActualMatroskaEpochDescription) + default: + d.FieldRawLen("value", int64(tagSize)*8) + } case *ebml.Binary: switch tagID { case ebml_matroska.SimpleBlockID: diff --git a/format/matroska/testdata/sweep-with-DC.mkvmerge13.mka.fqtest b/format/matroska/testdata/sweep-with-DC.mkvmerge13.mka.fqtest index bf6ad3c2..8f8c9876 100644 --- a/format/matroska/testdata/sweep-with-DC.mkvmerge13.mka.fqtest +++ b/format/matroska/testdata/sweep-with-DC.mkvmerge13.mka.fqtest @@ -159,7 +159,7 @@ $ fq dv sweep-with-DC.mkvmerge13.mka 0x10a0| 44 61 | Da | id: "date_utc" (0x4461) (The date and time that the Segment was created by the muxing application or library) 0x10a5-0x10a6.7 (2) | | | type: "date" 0x10a0| 88 | . | size: 8 0x10a7-0x10a7.7 (1) -0x10a0| 09 7b f0 a0 97 43 d2 00| .{...C..| value: raw bits 0x10a8-0x10af.7 (8) +0x10a0| 09 7b f0 a0 97 43 d2 00| .{...C..| value: 683404341000000000 (2022-08-28T18:32:21Z) 0x10a8-0x10af.7 (8) | | | [5]{}: element 0x10b0-0x10c2.7 (19) 0x10b0|73 a4 |s. | id: "segment_uuid" (0x73a4) (A randomly generated unique ID to identify the Segment amongst many others chosen) 0x10b0-0x10b1.7 (2) | | | type: "binary" diff --git a/format/mp4/boxes.go b/format/mp4/boxes.go index 29af2438..1c7a89cc 100644 --- a/format/mp4/boxes.go +++ b/format/mp4/boxes.go @@ -207,7 +207,7 @@ func decodeLang(d *decode.D) string { // Quicktime time seconds in January 1, 1904 UTC var quicktimeEpochDate = time.Date(1904, time.January, 1, 0, 0, 0, 0, time.UTC) -var uintActualQuicktimeEpoch = scalar.UintActualDate(quicktimeEpochDate, time.RFC3339) +var uintActualQuicktimeEpochDescription = scalar.UintActualDateDescription(quicktimeEpochDate, time.Second, time.RFC3339) func decodeMvhdFieldMatrix(d *decode.D, name string) { d.FieldStruct(name, func(d *decode.D) { @@ -398,13 +398,13 @@ func decodeBox(ctx *decodeContext, d *decode.D, typ string) { d.FieldU24("flags") switch version { case 0: - d.FieldU32("creation_time", uintActualQuicktimeEpoch) - d.FieldU32("modification_time", uintActualQuicktimeEpoch) + d.FieldU32("creation_time", uintActualQuicktimeEpochDescription) + d.FieldU32("modification_time", uintActualQuicktimeEpochDescription) d.FieldU32("time_scale") d.FieldU32("duration") case 1: - d.FieldU64("creation_time", uintActualQuicktimeEpoch) - d.FieldU64("modification_time", uintActualQuicktimeEpoch) + d.FieldU64("creation_time", uintActualQuicktimeEpochDescription) + d.FieldU64("modification_time", uintActualQuicktimeEpochDescription) d.FieldU32("time_scale") d.FieldU64("duration") default: @@ -458,14 +458,14 @@ func decodeBox(ctx *decodeContext, d *decode.D, typ string) { }) switch version { case 0: - d.FieldU32("creation_time", uintActualQuicktimeEpoch) - d.FieldU32("modification_time", uintActualQuicktimeEpoch) + d.FieldU32("creation_time", uintActualQuicktimeEpochDescription) + d.FieldU32("modification_time", uintActualQuicktimeEpochDescription) trackID = int(d.FieldU32("track_id")) d.FieldU32("reserved1") d.FieldU32("duration") case 1: - d.FieldU64("creation_time", uintActualQuicktimeEpoch) - d.FieldU64("modification_time", uintActualQuicktimeEpoch) + d.FieldU64("creation_time", uintActualQuicktimeEpochDescription) + d.FieldU64("modification_time", uintActualQuicktimeEpochDescription) trackID = int(d.FieldU32("track_id")) d.FieldU32("reserved1") d.FieldU64("duration") @@ -493,13 +493,13 @@ func decodeBox(ctx *decodeContext, d *decode.D, typ string) { // TODO: timestamps switch version { case 0: - d.FieldU32("creation_time", uintActualQuicktimeEpoch) - d.FieldU32("modification_time", uintActualQuicktimeEpoch) + d.FieldU32("creation_time", uintActualQuicktimeEpochDescription) + d.FieldU32("modification_time", uintActualQuicktimeEpochDescription) d.FieldU32("time_scale") d.FieldU32("duration") case 1: - d.FieldU64("creation_time", uintActualQuicktimeEpoch) - d.FieldU64("modification_time", uintActualQuicktimeEpoch) + d.FieldU64("creation_time", uintActualQuicktimeEpochDescription) + d.FieldU64("modification_time", uintActualQuicktimeEpochDescription) d.FieldU32("time_scale") d.FieldU64("duration") default: diff --git a/format/tls/tls.go b/format/tls/tls.go index b90b7c5b..81eee174 100644 --- a/format/tls/tls.go +++ b/format/tls/tls.go @@ -316,7 +316,7 @@ func decodeTLSHandshake(d *decode.D, tc *tlsCtx) { tc.version = d.FieldU16("version", versionNames, scalar.UintHex) copy(tc.random[:], d.PeekBytes(32)) d.FieldStruct("random", func(d *decode.D) { - d.FieldU32("gmt_unix_time", scalar.UintActualUnixTime(time.RFC3339)) + d.FieldU32("gmt_unix_time", scalar.UintActualUnixTimeDescription(time.Second, time.RFC3339)) d.FieldRawLen("random_bytes", 28*8) }) diff --git a/pkg/scalar/scalar.go b/pkg/scalar/scalar.go index 584b0056..5e2a5340 100644 --- a/pkg/scalar/scalar.go +++ b/pkg/scalar/scalar.go @@ -246,24 +246,35 @@ func (m RawBytesMap) MapBitBuf(s BitBuf) (BitBuf, error) { var unixTimeEpochDate = time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC) -func UintActualDate(epoch time.Time, format string) UintFn { +func UintActualDateDescription(epoch time.Time, unit time.Duration, format string) UintFn { return UintFn(func(s Uint) (Uint, error) { - s.Description = epoch.Add(time.Duration(s.Actual) * time.Second).Format(format) + s.Description = epoch.Add(time.Duration(s.Actual) * unit).Format(format) return s, nil }) } -func UintActualUnixTime(format string) UintFn { - return UintActualDate(unixTimeEpochDate, format) +func UintActualUnixTimeDescription(unit time.Duration, format string) UintFn { + return UintActualDateDescription(unixTimeEpochDate, unit, format) } -func FltActualDate(epoch time.Time, format string) FltFn { +func SintActualDateDescription(epoch time.Time, unit time.Duration, format string) SintFn { + return SintFn(func(s Sint) (Sint, error) { + s.Description = epoch.Add(time.Duration(s.Actual) * unit).Format(format) + return s, nil + }) +} + +func SintActualUnixTimeDescription(unit time.Duration, format string) SintFn { + return SintActualDateDescription(unixTimeEpochDate, unit, format) +} + +func FltActualDateDescription(epoch time.Time, unit time.Duration, format string) FltFn { return FltFn(func(s Flt) (Flt, error) { - s.Description = epoch.Add(time.Duration(s.Actual) * time.Second).Format(format) + s.Description = epoch.Add(time.Duration(s.Actual) * unit).Format(format) return s, nil }) } -func FltActualUnixTime(format string) FltFn { - return FltActualDate(unixTimeEpochDate, format) +func FltActualUnixTimeDescription(unit time.Duration, format string) FltFn { + return FltActualDateDescription(unixTimeEpochDate, unit, format) }