[インデックス 19274] ファイルの概要
このコミットは、Go言語の標準ライブラリであるarchive/zip
パッケージ内のstruct.go
ファイルに対する変更です。archive/zip
パッケージは、ZIPアーカイブの読み書きをサポートするための機能を提供します。struct.go
ファイルには、ZIPファイルのヘッダー構造や、タイムスタンプの変換に関連するヘルパー関数などが定義されています。
このコミットの主な目的は、FileHeader
構造体のModTime
メソッドおよびSetModTime
メソッドが扱う時刻が常にUTC(協定世界時)であることを明示的にドキュメントに追加することです。これにより、ZIPファイルのタイムスタンプの解釈における曖昧さを解消し、異なるシステム間での互換性を向上させます。
コミット
commit be781a72c43cb346d155152ec25cc80790b753fd
Author: Tyler Bunnell <tylerbunnell@gmail.com>
Date: Sun May 4 23:00:47 2014 -0400
archive/zip: Document ModTime is always UTC
Fixes #7592
LGTM=robert.hencke, adg
R=golang-codereviews, robert.hencke, gobot, adg
CC=golang-codereviews
https://golang.org/cl/90810043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/be781a72c43cb346d155152ec25cc80790b753fd
元コミット内容
archive/zip: Document ModTime is always UTC
Fixes #7592
LGTM=robert.hencke, adg
R=golang-codereviews, robert.hencke, gobot, adg
CC=golang-codereviews
https://golang.org/cl/90810043
変更の背景
ZIPファイルフォーマットでは、ファイルのエントリに最終更新日時(Modification Time)が記録されます。このタイムスタンプは、MS-DOSのタイムフォーマットで格納されることが一般的です。MS-DOSタイムフォーマットは、日付と時刻をそれぞれ16ビットの整数で表現しますが、このフォーマット自体はタイムゾーン情報を含んでいません。そのため、ZIPファイルを作成したシステムがローカルタイムでタイムスタンプを記録した場合、異なるタイムゾーンのシステムでそのZIPファイルを展開すると、時刻がずれて表示されるという問題が発生する可能性がありました。
Go言語のarchive/zip
パッケージでは、FileHeader
構造体のModTime()
メソッドでこの最終更新日時を取得し、SetModTime()
メソッドで設定します。しかし、これらのメソッドが返す、あるいは受け取るtime.Time
オブジェクトがどのタイムゾーン(ローカルタイムかUTCか)を基準としているのかが、以前のドキュメントでは明確ではありませんでした。
この曖昧さが原因で、開発者がZIPファイルのタイムスタンプを扱う際に誤解が生じ、互換性の問題や予期せぬ動作につながる可能性がありました。このコミットは、この曖昧さを解消し、ModTime
が常にUTCであることを明示することで、開発者がより正確にタイムスタンプを扱えるようにすることを目的としています。コミットメッセージにあるFixes #7592
は、この問題が特定のバグ報告(Issue 7592)に対応するものであることを示唆しています。
前提知識の解説
Go言語のarchive/zip
パッケージについて
archive/zip
パッケージは、Go言語でZIPアーカイブを扱うための標準ライブラリです。このパッケージを使用することで、ZIPファイルの作成、既存のZIPファイルからのファイルの読み出し、およびZIPファイル内のエントリ情報の取得などが可能です。FileHeader
構造体は、ZIPアーカイブ内の個々のファイル(エントリ)に関するメタデータ(ファイル名、サイズ、最終更新日時など)を保持します。
ZIPファイルフォーマットにおけるタイムスタンプの扱い
ZIPファイルフォーマットの仕様では、ファイルエントリの最終更新日時を記録するために、MS-DOSのタイムスタンプ形式が広く用いられています。この形式は、日付と時刻をそれぞれ独立したフィールドで表現します。
MS-DOSタイムフォーマット
MS-DOSタイムフォーマットは、日付と時刻をそれぞれ16ビットの整数で表現するコンパクトな形式です。
-
時刻 (16ビット):
- ビット 0-4: 秒 (2秒単位、0-29)
- ビット 5-10: 分 (0-59)
- ビット 11-15: 時 (0-23)
- 例: 10:30:00 は、秒が0、分が30、時が10としてエンコードされます。秒の解像度が2秒であるため、偶数秒しか表現できません。
-
日付 (16ビット):
- ビット 0-4: 日 (1-31)
- ビット 5-8: 月 (1-12)
- ビット 9-15: 年 (1980年からのオフセット、例: 2014年は34)
このフォーマットの重要な特徴は、タイムゾーン情報を含まないことです。通常、このタイムスタンプはファイルが作成または更新されたシステムのローカルタイムで記録されます。これが、異なるタイムゾーンのシステム間でZIPファイルをやり取りする際に問題を引き起こす原因となります。
UTCとローカルタイムの重要性
- UTC (Coordinated Universal Time): 協定世界時。世界の標準時であり、タイムゾーンや夏時間の概念を含まない普遍的な時刻です。異なる地域やシステム間で時刻を正確に同期・比較する際に非常に重要です。
- ローカルタイム: 特定の地域やシステムに設定されたタイムゾーンに基づいた時刻です。夏時間の影響を受けることがあります。
ZIPファイルのようなアーカイブフォーマットでタイムスタンプを扱う場合、ローカルタイムで記録すると、アーカイブが作成された環境のタイムゾーンに依存してしまいます。これにより、別のタイムゾーンの環境でアーカイブを解凍した際に、ファイルのタイムスタンプが期待と異なる時刻になる可能性があります。これを避けるためには、タイムスタンプをUTCで統一的に扱うことが推奨されます。
技術的詳細
このコミットは、archive/zip/struct.go
ファイル内のFileHeader
構造体のメソッドであるModTime()
とSetModTime()
のドキュメンテーションを修正しています。
ModTime()
:FileHeader
に格納されているMS-DOS形式のタイムスタンプをGoのtime.Time
型に変換して返します。SetModTime()
: 与えられたtime.Time
オブジェクトをMS-DOS形式のタイムスタンプに変換し、FileHeader
に設定します。
これらのメソッドの内部では、msDosTimeToTime()
とtimeToMsDosTime()
というヘルパー関数が使用されています。これらの関数は、MS-DOSタイムフォーマットとGoのtime.Time
型との間の変換を担っています。
変更前は、これらのメソッドのドキュメントには単に「modification time(更新日時)」と記載されていました。しかし、MS-DOSタイムフォーマットがタイムゾーン情報を持たないため、Goのtime.Time
オブジェクトがどのタイムゾーン(ローカルタイムかUTCか)を基準としているのかが不明確でした。
このコミットでは、ドキュメントに「in UTC」という記述を追加することで、ModTime()
が返す時刻、およびSetModTime()
が受け取る時刻が、内部的にMS-DOSタイムフォーマットに変換される際にUTCとして扱われることを明確にしています。
なぜUTCであることが重要かというと、ZIPファイルは異なるオペレーティングシステムや地域間で頻繁にやり取りされるためです。もしタイムスタンプがローカルタイムで記録されると、例えば日本で作成されたZIPファイルをアメリカで解凍した場合、タイムスタンプが9時間ずれて表示されるといった問題が発生します。UTCで統一することで、このようなタイムゾーンによるずれをなくし、普遍的で正確なタイムスタンプの表現が可能になります。これにより、ZIPファイルの互換性が向上し、開発者はタイムスタンプの解釈について迷うことなく、信頼性の高いアプリケーションを構築できます。
コアとなるコードの変更箇所
--- a/src/pkg/archive/zip/struct.go
+++ b/src/pkg/archive/zip/struct.go
@@ -174,13 +174,13 @@ func timeToMsDosTime(t time.Time) (fDate uint16, fTime uint16) {
return
}
-// ModTime returns the modification time.\n+// ModTime returns the modification time in UTC.\n // The resolution is 2s.\n func (h *FileHeader) ModTime() time.Time {\n return msDosTimeToTime(h.ModifiedDate, h.ModifiedTime)\n }\n \n-// SetModTime sets the ModifiedTime and ModifiedDate fields to the given time.\n+// SetModTime sets the ModifiedTime and ModifiedDate fields to the given time in UTC.\n // The resolution is 2s.\n func (h *FileHeader) SetModTime(t time.Time) {\n h.ModifiedDate, h.ModifiedTime = timeToMsDosTime(t)\n```
## コアとなるコードの解説
変更は、`ModTime()`と`SetModTime()`の関数シグネチャではなく、その上のコメント行にのみ行われています。
* **`ModTime()`メソッドの変更**:
* 変更前: `// ModTime returns the modification time.`
* 変更後: `// ModTime returns the modification time in UTC.`
* この変更により、`ModTime()`が返す`time.Time`オブジェクトが、ZIPファイル内のMS-DOSタイムスタンプをUTCとして解釈した結果であることを明確にしています。開発者は、このメソッドから取得した時刻がタイムゾーンの影響を受けないUTCであることを前提にコードを書くことができます。
* **`SetModTime()`メソッドの変更**:
* 変更前: `// SetModTime sets the ModifiedTime and ModifiedDate fields to the given time.`
* 変更後: `// SetModTime sets the ModifiedTime and ModifiedDate fields to the given time in UTC.`
* この変更により、`SetModTime()`に渡す`time.Time`オブジェクトがUTCであるべきことを明確にしています。もしローカルタイムの`time.Time`オブジェクトを渡した場合、内部でUTCに変換されるか、あるいは意図しない時刻がZIPファイルに記録される可能性があるため、開発者に対してUTCの時刻を渡すよう促しています。これにより、ZIPファイルに記録されるタイムスタンプの一貫性が保たれます。
これらのコメントの追加は、コードの動作自体を変更するものではありませんが、APIの振る舞いに関する重要な情報を提供し、開発者の誤解を防ぐためのものです。特に、タイムゾーンの扱いはプログラミングにおいて複雑な問題を引き起こしやすいため、このような明確なドキュメンテーションは非常に価値があります。
## 関連リンク
* Go言語 `archive/zip` パッケージのドキュメント: [https://pkg.go.dev/archive/zip](https://pkg.go.dev/archive/zip)
* ZIPファイルフォーマットの仕様 (PKWARE): [https://pkware.com/docs/casestudies/APPNOTE.TXT](https://pkware.com/docs/casestudies/APPNOTE.TXT)
* Go言語 `time` パッケージのドキュメント: [https://pkg.go.dev/time](https://pkg.go.dev/time)
## 参考にした情報源リンク
* MS-DOS Date/Time Format: [https://www.kaitai.io/docs/formats/dos_datetime/](https://www.kaitai.io/docs/formats/dos_datetime/)
* Microsoft Docs - FAT Date/Time: [https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-dosdatetimetofiletime](https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-dosdatetimetofiletime)
* Archive Team Wiki - DOS Date/Time: [https://wiki.archiveteam.org/index.php/DOS_Date/Time](https://wiki.archiveteam.org/index.php/DOS_Date/Time)