From 2135879dda6c6b36988f8f9e0769e994dd8f1308 Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Sun, 3 Feb 2019 11:01:52 +0000 Subject: [PATCH] lsjson: use exactly the correct number of decimal places in the seconds --- cmd/lsjson/lsjson.go | 8 +++++++- fs/operations/lsjson.go | 40 ++++++++++++++++++++++++++++++++++------ fs/operations/rc_test.go | 4 ++-- 3 files changed, 43 insertions(+), 9 deletions(-) diff --git a/cmd/lsjson/lsjson.go b/cmd/lsjson/lsjson.go index c09848166..28a38982f 100644 --- a/cmd/lsjson/lsjson.go +++ b/cmd/lsjson/lsjson.go @@ -60,7 +60,13 @@ If "remote:path" contains the file "subfolder/file.txt", the Path for "file.txt" will be "subfolder/file.txt", not "remote:path/subfolder/file.txt". When used without --recursive the Path will always be the same as Name. -The time is in RFC3339 format with nanosecond precision. +The time is in RFC3339 format with up to nanosecond precision. The +number of decimal digits in the seconds will depend on the precision +that the remote can hold the times, so if times are accurate to the +nearest millisecond (eg Google Drive) then 3 digits will always be +shown ("2017-05-31T16:15:57.034+01:00") whereas if the times are +accurate to the nearest second (Dropbox, Box, WebDav etc) no digits +will be shown ("2017-05-31T16:15:57+01:00"). The whole output can be processed as a JSON blob, or alternatively it can be processed line by line as each item is written one to a line. diff --git a/fs/operations/lsjson.go b/fs/operations/lsjson.go index 79465eceb..760368611 100644 --- a/fs/operations/lsjson.go +++ b/fs/operations/lsjson.go @@ -24,16 +24,43 @@ type ListJSONItem struct { OrigID string `json:",omitempty"` } -// Timestamp a time in RFC3339 format with Nanosecond precision secongs -type Timestamp time.Time +// Timestamp a time in the provided format +type Timestamp struct { + When time.Time + Format string +} // MarshalJSON turns a Timestamp into JSON func (t Timestamp) MarshalJSON() (out []byte, err error) { - tt := time.Time(t) - if tt.IsZero() { + if t.When.IsZero() { return []byte(`""`), nil } - return []byte(`"` + tt.Format(time.RFC3339Nano) + `"`), nil + return []byte(`"` + t.When.Format(t.Format) + `"`), nil +} + +// Returns a time format for the given precision +func formatForPrecision(precision time.Duration) string { + switch { + case precision <= time.Nanosecond: + return "2006-01-02T15:04:05.000000000Z07:00" + case precision <= 10*time.Nanosecond: + return "2006-01-02T15:04:05.00000000Z07:00" + case precision <= 100*time.Nanosecond: + return "2006-01-02T15:04:05.0000000Z07:00" + case precision <= time.Microsecond: + return "2006-01-02T15:04:05.000000Z07:00" + case precision <= 10*time.Microsecond: + return "2006-01-02T15:04:05.00000Z07:00" + case precision <= 100*time.Microsecond: + return "2006-01-02T15:04:05.0000Z07:00" + case precision <= time.Millisecond: + return "2006-01-02T15:04:05.000Z07:00" + case precision <= 10*time.Millisecond: + return "2006-01-02T15:04:05.00Z07:00" + case precision <= 100*time.Millisecond: + return "2006-01-02T15:04:05.0Z07:00" + } + return time.RFC3339 } // ListJSONOpt describes the options for ListJSON @@ -61,6 +88,7 @@ func ListJSON(fsrc fs.Fs, remote string, opt *ListJSONOpt, callback func(*ListJS return errors.Wrap(err, "ListJSON failed to make new crypt remote") } } + format := formatForPrecision(fsrc.Precision()) err := walk.Walk(fsrc, remote, false, ConfigMaxDepth(opt.Recurse), func(dirPath string, entries fs.DirEntries, err error) error { if err != nil { fs.CountError(err) @@ -75,7 +103,7 @@ func ListJSON(fsrc fs.Fs, remote string, opt *ListJSONOpt, callback func(*ListJS MimeType: fs.MimeTypeDirEntry(entry), } if !opt.NoModTime { - item.ModTime = Timestamp(entry.ModTime()) + item.ModTime = Timestamp{When: entry.ModTime(), Format: format} } if cipher != nil { switch entry.(type) { diff --git a/fs/operations/rc_test.go b/fs/operations/rc_test.go index 6dedf7490..d4649929b 100644 --- a/fs/operations/rc_test.go +++ b/fs/operations/rc_test.go @@ -175,7 +175,7 @@ func TestRcList(t *testing.T) { assert.Equal(t, 2, len(list)) checkFile1 := func(got *operations.ListJSONItem) { - assert.WithinDuration(t, t1, time.Time(got.ModTime), time.Second) + assert.WithinDuration(t, t1, got.ModTime.When, time.Second) assert.Equal(t, "a", got.Path) assert.Equal(t, "a", got.Name) assert.Equal(t, int64(1), got.Size) @@ -209,7 +209,7 @@ func TestRcList(t *testing.T) { checkSubdir(list[1]) checkFile2 := func(got *operations.ListJSONItem) { - assert.WithinDuration(t, t2, time.Time(got.ModTime), time.Second) + assert.WithinDuration(t, t2, got.ModTime.When, time.Second) assert.Equal(t, "subdir/b", got.Path) assert.Equal(t, "b", got.Name) assert.Equal(t, int64(2), got.Size)