diff --git a/README.md b/README.md index 1b55230..65ec0c3 100644 --- a/README.md +++ b/README.md @@ -104,3 +104,4 @@ Successfully mounted LittleFS filesystem. The FAT file system is not currently working, due to https://github.com/tinygo-org/tinygo/issues/3460. +FAT file timestamps use UTC, with two-second resolution. diff --git a/fatfs/go_fatfs_callbacks.go b/fatfs/go_fatfs_callbacks.go index fa4f258..8d69c7c 100644 --- a/fatfs/go_fatfs_callbacks.go +++ b/fatfs/go_fatfs_callbacks.go @@ -98,7 +98,7 @@ func go_fatfs_get_fattime() (t uint32) { secondsPerUnit = 2 // FAT timestamps have two-second resolution ) - now := time.Now() + now := time.Now().UTC() year, month, day := now.Date() hour, minute, second := now.Hour(), now.Minute(), now.Second() t |= uint32(year-epochYear) << yearShift diff --git a/fatfs/go_fatfs_test.go b/fatfs/go_fatfs_test.go index 4454d69..897d126 100644 --- a/fatfs/go_fatfs_test.go +++ b/fatfs/go_fatfs_test.go @@ -18,38 +18,51 @@ const ( ) func TestStoredModificationTime(t *testing.T) { - fs, dev, unmount := createTestFS(t) - defer unmount() - if typ := Type(fs.fs.fs_type); typ != TypeFAT12 && typ != TypeFAT16 { - t.Fatalf("expected FAT12 or FAT16, got %s", typ) - } - before := time.Now().Truncate(2 * time.Second) - f, err := fs.OpenFile("TIME.TXT", os.O_CREATE|os.O_WRONLY|os.O_TRUNC) - check(t, err) - _, err = f.Write([]byte("timestamp")) - check(t, err) - check(t, f.Close()) - after := time.Now() + for _, loc := range []*time.Location{ + time.UTC, + time.FixedZone("UTC+14", 14*60*60), + time.FixedZone("UTC-12", -12*60*60), + } { + t.Run(loc.String(), func(t *testing.T) { + // Stored timestamps must be UTC regardless of the local time zone. + local := time.Local + time.Local = loc + t.Cleanup(func() { time.Local = local }) - root := make([]byte, int(fs.fs.n_rootdir)*32) - _, err = dev.ReadAt(root, int64(fs.fs.dirbase)*SectorSize) - check(t, err) - for offset := 0; offset < len(root); offset += 32 { - entry := root[offset : offset+32] - if string(entry[:11]) != "TIME TXT" { - continue - } - // FAT directory entries store modification time at bytes 22-25. - clock := binary.LittleEndian.Uint16(entry[22:24]) - date := binary.LittleEndian.Uint16(entry[24:26]) - got := time.Date(1980+int(date>>9), time.Month(date>>5&15), int(date&31), - int(clock>>11), int(clock>>5&63), int(clock&31)*2, 0, time.Local) - if got.Before(before) || got.After(after) { - t.Fatalf("stored modification time = %s, want between %s and %s", got, before, after) - } - return + fs, dev, unmount := createTestFS(t) + defer unmount() + if typ := Type(fs.fs.fs_type); typ != TypeFAT12 && typ != TypeFAT16 { + t.Fatalf("expected FAT12 or FAT16, got %s", typ) + } + before := time.Now().UTC().Truncate(2 * time.Second) + f, err := fs.OpenFile("TIME.TXT", os.O_CREATE|os.O_WRONLY|os.O_TRUNC) + check(t, err) + _, err = f.Write([]byte("timestamp")) + check(t, err) + check(t, f.Close()) + after := time.Now().UTC() + + root := make([]byte, int(fs.fs.n_rootdir)*32) + _, err = dev.ReadAt(root, int64(fs.fs.dirbase)*SectorSize) + check(t, err) + for offset := 0; offset < len(root); offset += 32 { + entry := root[offset : offset+32] + if string(entry[:11]) != "TIME TXT" { + continue + } + // FAT directory entries store modification time at bytes 22-25. + clock := binary.LittleEndian.Uint16(entry[22:24]) + date := binary.LittleEndian.Uint16(entry[24:26]) + got := time.Date(1980+int(date>>9), time.Month(date>>5&15), int(date&31), + int(clock>>11), int(clock>>5&63), int(clock&31)*2, 0, time.UTC) + if got.Before(before) || got.After(after) { + t.Fatalf("stored modification time = %s, want between %s and %s", got, before, after) + } + return + } + t.Fatal("TIME.TXT not found in root directory") + }) } - t.Fatal("TIME.TXT not found in root directory") } func TestType_String(t *testing.T) {