archive/zip/struct.go#L170 このファイルの

   170  func timeToMsDosTime(t time.Time) (fDate uint16, fTime uint16) {
   171      t = t.In(time.UTC)
   172      fDate = uint16(t.Day() + int(t.Month())<<5 + (t.Year()-1980)<<9)
   173      fTime = uint16(t.Second()/2 + t.Minute()<<5 + t.Hour()<<11)
   174      return
   175  }

を見ると

   171      t = t.In(time.UTC)

デフォルトでは強制でUTCになっちゃってます。。。

なので、前回の記事Golangで特定のディレクトリをZIP化する処理を書いたでは、

$ ls -la sample.txt
2016-06-30 01:29 sample.txt

というファイルをzip化して、展開すると

2016-06-29 16:29 sample.txt

と、見事に9時間前(UTC)に変わっちゃってました。。。

対応

package main

import (
    "archive/zip"
    "fmt"
    "io"
    "io/ioutil"
    "os"
    "path/filepath"
    "time"
)

func main() {
    filePaths, _ := find(".")
    zipPath := "/tmp/test.zip"
    archive(zipPath, filePaths)
}

// find
func find(targetDir string) ([]string, error) {

    var paths []string
    err := filepath.Walk(targetDir,
        func(path string, info os.FileInfo, err error) error {
            rel, err := filepath.Rel(targetDir, path)
            if err != nil {
                return err
            }

            if info.IsDir() {
                paths = append(paths, fmt.Sprintf("%s/", rel))
                return nil
            }

            paths = append(paths, rel)

            return nil
        })

    if err != nil {
        return nil, err
    }

    return paths, nil
}

func archive(output string, paths []string) error {
    var compressedFile *os.File
    var err error

    //ZIPファイル作成
    if compressedFile, err = os.Create(output); err != nil {
        return err
    }
    defer compressedFile.Close()

    if err := compress(compressedFile, ".", paths); err != nil {
        return err
    }

    return nil
}

func compress(compressedFile io.Writer, targetDir string, files []string) error {
    w := zip.NewWriter(compressedFile)

    for _, filename := range files {
        filepath := fmt.Sprintf("%s/%s", targetDir, filename)
        info, err := os.Stat(filepath)
        if err != nil {
            return err
        }

        if info.IsDir() {
            continue
        }

        file, err := os.Open(filepath)
        if err != nil {
            return err
        }
        defer file.Close()

        hdr, err := zip.FileInfoHeader(info)
        if err != nil {
            return err
        }

        hdr.Name = filename

        local := time.Now().Local()

        //現時刻のオフセットを取得
        _, offset := local.Zone()

        //差分を追加
        hdr.SetModTime(hdr.ModTime().Add(time.Duration(offset) * time.Second))

        f, err := w.CreateHeader(hdr)
        if err != nil {
            return err
        }

        contents, _ := ioutil.ReadFile(filepath)
        _, err = f.Write(contents)
        if err != nil {
            return err
        }
    }

    if err := w.Close(); err != nil {
        return err
    }

    return nil
}

このように変更しました!!

え、わかりにくいですね。。

        hdr, err := zip.FileInfoHeader(info)
        if err != nil {
            return err
        }

        hdr.Name = filename

        local := time.Now().Local()

        //現時刻のオフセットを取得
        _, offset := local.Zone()

        //差分を追加
        hdr.SetModTime(hdr.ModTime().Add(time.Duration(offset) * time.Second))

        f, err := w.CreateHeader(hdr)
        if err != nil {
            return err
        }

このFileInfoHeaderから、CreateHeaderの間の

                local := time.Now().Local()

        //現時刻のオフセットを取得
        _, offset := local.Zone()

        //差分を追加
        hdr.SetModTime(hdr.ModTime().Add(time.Duration(offset) * time.Second))

です。 ここでは、現在のロケールを取得して、その差分を archive/zip/struct.go#L185 に書いてる

hdr.SetModTime(hdr.ModTime().Add(time.Duration(offset) * time.Second))

で、設定しています。

内部的にUTC解釈されちゃいますが、それによって結果時間が実行環境と同じになります。

この問題は、、、まじトラップ