Skip to content

Commit c17fcd2

Browse files
authored
Add standard time format (#52)
1 parent f5f57a2 commit c17fcd2

File tree

2 files changed

+67
-0
lines changed

2 files changed

+67
-0
lines changed

ltx.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"io"
1313
"regexp"
1414
"strconv"
15+
"time"
1516
)
1617

1718
const (
@@ -29,6 +30,10 @@ const (
2930
TrailerSize = 16
3031
)
3132

33+
// RFC3339Milli is the standard time format for LTX timestamps.
34+
// It uses fixed-width millisecond resolution which makes it sortable.
35+
const RFC3339Milli = "2006-01-02T15:04:05.000Z07:00"
36+
3237
// Checksum size & positions.
3338
const (
3439
ChecksumSize = 8
@@ -499,6 +504,29 @@ func ParseFilename(name string) (minTXID, maxTXID TXID, err error) {
499504
return TXID(min), TXID(max), nil
500505
}
501506

507+
// FormatTimestamp returns t with a fixed-width, millisecond-resolution UTC format.
508+
func FormatTimestamp(t time.Time) string {
509+
return t.UTC().Format(RFC3339Milli)
510+
}
511+
512+
// ParseTimestamp parses a timestamp as RFC3339Milli (fixed-width) but will
513+
// fallback to RFC3339Nano if it fails. This is to support timestamps written
514+
// before the introduction of the standard time format.
515+
func ParseTimestamp(value string) (time.Time, error) {
516+
// Attempt standard format first.
517+
t, err := time.Parse(RFC3339Milli, value)
518+
if err == nil {
519+
return t, nil
520+
}
521+
522+
// If the standard fails, fallback to stdlib format but truncate to milliseconds.
523+
t2, err2 := time.Parse(time.RFC3339Nano, value)
524+
if err2 != nil {
525+
return t, err // use original error on failure.
526+
}
527+
return t2.Truncate(time.Millisecond), nil
528+
}
529+
502530
var filenameRegex = regexp.MustCompile(`^([0-9a-f]{16})-([0-9a-f]{16})\.ltx$`)
503531

504532
// FormatFilename returns an LTX filename representing a range of transactions.

ltx_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"os"
1212
"reflect"
1313
"testing"
14+
"time"
1415

1516
"github.com/superfly/ltx"
1617
)
@@ -530,6 +531,44 @@ func TestParseTXID(t *testing.T) {
530531
})
531532
}
532533

534+
func TestFormatTimestamp(t *testing.T) {
535+
for _, tt := range []struct {
536+
t time.Time
537+
want string
538+
}{
539+
{time.Date(2000, 10, 20, 30, 40, 50, 0, time.UTC), "2000-10-21T06:40:50.000Z"},
540+
{time.Date(2000, 10, 20, 30, 40, 50, 123000000, time.UTC), "2000-10-21T06:40:50.123Z"},
541+
{time.Date(2000, 10, 20, 30, 40, 50, 120000000, time.UTC), "2000-10-21T06:40:50.120Z"},
542+
{time.Date(2000, 10, 20, 30, 40, 50, 100000000, time.UTC), "2000-10-21T06:40:50.100Z"},
543+
{time.Date(2000, 10, 20, 30, 40, 50, 100000, time.UTC), "2000-10-21T06:40:50.000Z"}, // submillisecond
544+
} {
545+
if got := ltx.FormatTimestamp(tt.t); got != tt.want {
546+
t.Fatalf("got=%s, want %s", got, tt.want)
547+
}
548+
}
549+
}
550+
551+
func TestParseTimestamp(t *testing.T) {
552+
for _, tt := range []struct {
553+
str string
554+
want time.Time
555+
}{
556+
{"2000-10-21T06:40:50.000Z", time.Date(2000, 10, 20, 30, 40, 50, 0, time.UTC)},
557+
{"2000-10-21T06:40:50.123Z", time.Date(2000, 10, 20, 30, 40, 50, 123000000, time.UTC)},
558+
{"2000-10-21T06:40:50.120Z", time.Date(2000, 10, 20, 30, 40, 50, 120000000, time.UTC)},
559+
{"2000-10-21T06:40:50.100Z", time.Date(2000, 10, 20, 30, 40, 50, 100000000, time.UTC)},
560+
{"2000-10-21T06:40:50Z", time.Date(2000, 10, 20, 30, 40, 50, 0, time.UTC)},
561+
{"2000-10-21T06:40:50.000123Z", time.Date(2000, 10, 20, 30, 40, 50, 0, time.UTC)},
562+
{"2000-10-21T06:40:50.000000123Z", time.Date(2000, 10, 20, 30, 40, 50, 0, time.UTC)},
563+
} {
564+
if got, err := ltx.ParseTimestamp(tt.str); err != nil {
565+
t.Fatal(err)
566+
} else if !got.Equal(tt.want) {
567+
t.Fatalf("got=%s, want %s", got, tt.want)
568+
}
569+
}
570+
}
571+
533572
func BenchmarkChecksumPage(b *testing.B) {
534573
for _, pageSize := range []int{512, 1024, 2048, 4096, 8192, 16384, 32768, 65536} {
535574
b.Run(fmt.Sprint(pageSize), func(b *testing.B) {

0 commit comments

Comments
 (0)