Commit 98f5fc5e authored by Robert Griesemer's avatar Robert Griesemer

archive/zip: support functions to get modified time in ns from MS-DOS time

R=rsc, r, bradfitz, r, adg
CC=golang-dev
https://golang.org/cl/4748056
parent 208e6e6d
...@@ -11,6 +11,7 @@ import ( ...@@ -11,6 +11,7 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"testing" "testing"
"time"
) )
type ZipTest struct { type ZipTest struct {
...@@ -24,8 +25,19 @@ type ZipTestFile struct { ...@@ -24,8 +25,19 @@ type ZipTestFile struct {
Name string Name string
Content []byte // if blank, will attempt to compare against File Content []byte // if blank, will attempt to compare against File
File string // name of file to compare to (relative to testdata/) File string // name of file to compare to (relative to testdata/)
Mtime string // modified time in format "mm-dd-yy hh:mm:ss"
} }
// Caution: The Mtime values found for the test files should correspond to
// the values listed with unzip -l <zipfile>. However, the values
// listed by unzip appear to be off by some hours. When creating
// fresh test files and testing them, this issue is not present.
// The test files were created in Sydney, so there might be a time
// zone issue. The time zone information does have to be encoded
// somewhere, because otherwise unzip -l could not provide a different
// time from what the archive/zip package provides, but there appears
// to be no documentation about this.
var tests = []ZipTest{ var tests = []ZipTest{
{ {
Name: "test.zip", Name: "test.zip",
...@@ -34,10 +46,12 @@ var tests = []ZipTest{ ...@@ -34,10 +46,12 @@ var tests = []ZipTest{
{ {
Name: "test.txt", Name: "test.txt",
Content: []byte("This is a test text file.\n"), Content: []byte("This is a test text file.\n"),
Mtime: "09-05-10 12:12:02",
}, },
{ {
Name: "gophercolor16x16.png", Name: "gophercolor16x16.png",
File: "gophercolor16x16.png", File: "gophercolor16x16.png",
Mtime: "09-05-10 15:52:58",
}, },
}, },
}, },
...@@ -47,6 +61,7 @@ var tests = []ZipTest{ ...@@ -47,6 +61,7 @@ var tests = []ZipTest{
{ {
Name: "r/r.zip", Name: "r/r.zip",
File: "r.zip", File: "r.zip",
Mtime: "03-04-10 00:24:16",
}, },
}, },
}, },
...@@ -58,6 +73,7 @@ var tests = []ZipTest{ ...@@ -58,6 +73,7 @@ var tests = []ZipTest{
{ {
Name: "filename", Name: "filename",
Content: []byte("This is a test textfile.\n"), Content: []byte("This is a test textfile.\n"),
Mtime: "02-02-11 13:06:20",
}, },
}, },
}, },
...@@ -136,18 +152,30 @@ func readTestFile(t *testing.T, ft ZipTestFile, f *File) { ...@@ -136,18 +152,30 @@ func readTestFile(t *testing.T, ft ZipTestFile, f *File) {
if f.Name != ft.Name { if f.Name != ft.Name {
t.Errorf("name=%q, want %q", f.Name, ft.Name) t.Errorf("name=%q, want %q", f.Name, ft.Name)
} }
mtime, err := time.Parse("01-02-06 15:04:05", ft.Mtime)
if err != nil {
t.Error(err)
return
}
if got, want := f.Mtime_ns()/1e9, mtime.Seconds(); got != want {
t.Errorf("%s: mtime=%s (%d); want %s (%d)", f.Name, time.SecondsToUTC(got), got, mtime, want)
}
var b bytes.Buffer var b bytes.Buffer
r, err := f.Open() r, err := f.Open()
if err != nil { if err != nil {
t.Error(err) t.Error(err)
return return
} }
_, err = io.Copy(&b, r) _, err = io.Copy(&b, r)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
return return
} }
r.Close() r.Close()
var c []byte var c []byte
if len(ft.Content) != 0 { if len(ft.Content) != 0 {
c = ft.Content c = ft.Content
...@@ -155,10 +183,12 @@ func readTestFile(t *testing.T, ft ZipTestFile, f *File) { ...@@ -155,10 +183,12 @@ func readTestFile(t *testing.T, ft ZipTestFile, f *File) {
t.Error(err) t.Error(err)
return return
} }
if b.Len() != len(c) { if b.Len() != len(c) {
t.Errorf("%s: len=%d, want %d", f.Name, b.Len(), len(c)) t.Errorf("%s: len=%d, want %d", f.Name, b.Len(), len(c))
return return
} }
for i, b := range b.Bytes() { for i, b := range b.Bytes() {
if b != c[i] { if b != c[i] {
t.Errorf("%s: content[%d]=%q want %q", f.Name, i, b, c[i]) t.Errorf("%s: content[%d]=%q want %q", f.Name, i, b, c[i])
......
...@@ -12,6 +12,7 @@ This package does not support ZIP64 or disk spanning. ...@@ -12,6 +12,7 @@ This package does not support ZIP64 or disk spanning.
package zip package zip
import "os" import "os"
import "time"
// Compression methods. // Compression methods.
const ( const (
...@@ -32,8 +33,8 @@ type FileHeader struct { ...@@ -32,8 +33,8 @@ type FileHeader struct {
ReaderVersion uint16 ReaderVersion uint16
Flags uint16 Flags uint16
Method uint16 Method uint16
ModifiedTime uint16 ModifiedTime uint16 // MS-DOS time
ModifiedDate uint16 ModifiedDate uint16 // MS-DOS date
CRC32 uint32 CRC32 uint32
CompressedSize uint32 CompressedSize uint32
UncompressedSize uint32 UncompressedSize uint32
...@@ -61,3 +62,27 @@ func recoverError(err *os.Error) { ...@@ -61,3 +62,27 @@ func recoverError(err *os.Error) {
panic(e) panic(e)
} }
} }
// msDosTimeToTime converts an MS-DOS date and time into a time.Time.
// The resolution is 2s.
// See: http://msdn.microsoft.com/en-us/library/ms724247(v=VS.85).aspx
func msDosTimeToTime(dosDate, dosTime uint16) time.Time {
return time.Time{
// date bits 0-4: day of month; 5-8: month; 9-15: years since 1980
Year: int64(dosDate>>9 + 1980),
Month: int(dosDate >> 5 & 0xf),
Day: int(dosDate & 0x1f),
// time bits 0-4: second/2; 5-10: minute; 11-15: hour
Hour: int(dosTime >> 11),
Minute: int(dosTime >> 5 & 0x3f),
Second: int(dosTime & 0x1f * 2),
}
}
// Mtime_ns returns the modified time in ns since epoch.
// The resolution is 2s.
func (h *FileHeader) Mtime_ns() int64 {
t := msDosTimeToTime(h.ModifiedDate, h.ModifiedTime)
return t.Seconds() * 1e9
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment