lsjson: use exactly the correct number of decimal places in the seconds

This commit is contained in:
Nick Craig-Wood 2019-02-03 11:01:52 +00:00
parent da90069462
commit 2135879dda
3 changed files with 43 additions and 9 deletions

View file

@ -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.

View file

@ -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) {

View file

@ -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)