[1] 多くの[[ファイル]]を扱うシステムに[DFN[ファイルの時刻]]の[[属性]]がいくつかあります。


* 種別

[2] ほとんどが

- [3] [[birthtime]] : 作成日時
- [4] [[mtime]] : 変更日時
- [5] [[ctime]] : 属性を含む変更日時
- [6] [[atime]] : 最終読取日時

... のいずれかに分類できます。

[7] 
ただし各[[日時]]の厳密な意味は[[プラットフォーム]]と[[ファイルシステム]]/[[データ形式]]/[[プロトコル]]によって異なります。

[9] 
[[NTFS]] では [CODE[$STANDARD_INFORMATION]] (ファイルの内容のデータ側)
と 
[CODE[$FILE_NAME]] ([[ファイル名]]側)
にそれぞれ4種類あります。
大まかに言って、内容が変更されれば両者が変更され、
ファイル操作が行われると後者が変更されます。
ファイル1個の情報の読み書きでは基本的に前者が使われ、
ファイル一覧の表示では後者が使われます。
[[ハードリンク]]が作られると、後者が複数個になります。

* 時間帯

[10] 
[[FATの日時形式]]は[[地方時]]を保存します。[[時差]]の情報が含まれず、
どこの[[時刻]]かわかりませんし、[[標準時]]か[[夏時刻]]かもわかりません。

[11] 
それより新しいほとんどのシステムは[[時差]]の概念のない絶対的な時刻の値を保存します。
[[利用者]]に表示するときは[[システムの時間帯]]に合わせて表示されます。

[12] 
[[ZIP]] のように、古くからある標準の[[ファイルの時刻]]は >>10
で、後から拡張として追加された (つまりすべての [[ZIPファイル]]に含まれるわけではない)
[[ファイルの時刻]]は >>11 で両方が共存しているという[[書庫形式]]もあります。


* データ形式

[FIG(short list)[ [8] [[ファイルの時刻]]に関係する[[プロトコル要素]]と機能

- [[FATの日時形式]]
- [[NTFSの日時形式]]
- [[Unix time]]
- [[HFSの日時形式]]
- [[i-node]]
- [CODE[Content-Disposition]]
- [CODE[Last-Modified]]
- [[ZIP]]
- [CODE[stat]]
- [CODE[touch]]
- [CODE[ls]]
- [[プロパティ (Windows)]]

]FIG]


** 日時の換算

[190] 
[[ファイルの時刻]]の取り扱いで、意外と難関となるのが[[日時形式]] ([[日時データ型]])
の[[換算][暦の換算]]の問題です。

[191] 
[[OS]], [[ファイルシステム]], [[プログラミング言語]], [[圧縮形式]]、[[ファイル]]を扱う各種[[プロトコル]]でそれぞれ採用[[日時データ型]]が違うことが多いので、
処理の種類と過程に応じてどの[[データ型]]で[[日時]]を保持し、
どこでどう[[換算][暦の換算]]するかを開発者は熟慮しなければなりません。

[192] 
個別具体的には各項を参照されたいのですが、概略して次のような事項に配慮が必要です。

- [193] [[時間帯]]。古めのシステムは[[時差]]情報のない[[地方時]]を採用する傾向にあり、
新しめのシステムは [[UTC]] を採用する傾向にあります。[[プロトコル]]や[[情報交換]]用[[データ形式]]は[[時差]]や[[時間帯]]の情報を保持できることもあります。
-- [194] 特に課題となるのは、[[時差]]情報のない[[地方時]]をどう解釈するか、です。
-- [204] プログラムの内部で[[時差]], [[時間帯]], [[地方時]]であるか否か、といった[[時差の記述]]をどうするか。
- [195] [[値域]]。[[日時データ型]]次第で表現できる最小と最大の[[日時]]がまったく異なります。
-- [196] 入力データに内部データ型で扱えない値が出現したときにどうするか。
-- [197] 内部データ型に出力できない値が出現したときにどうするか。
-- [199] [[符号付き整数]]か[[符号無し整数]]か、仕様が曖昧で実装が割れているときどうするか。
-- [198] [N[0]] のような値が特殊な意味を持たないか。持つとしたらどうするか。
- [200] [[精度]]。[[日時データ型]]次第で表現できる[[日時]]の最小の刻み幅が違うことがあります。
-- [201] 入力データに内部データ型で扱えない値が出現したときにどうするか。
-- [202] 内部データ型に出力できない値が出現したときにどうするか。
-- [203] 作成 ≦ 修正のような関係が精度の限界によって成立しないときどうするか。
- [205] 重複。[[書庫形式]]や[[プロトコル]]は歴史的経緯や[[ファイルシステム]]との互換性などの理由から、
同じ意味の異なる[[日時]]データをいくつも持っていることがあります。
-- [206] 入力データからどう取捨選択するか。
-- [207] データごとに[[日時データ型]]が異なるとき、どう取り扱うか。
- [208] 異なる種類の日時。作成、修正など複数の[[日時]]が扱えることがありますが、
その意味は[[プラットフォーム]]ごとに少しずつ違い、
[[ファイルシステム]]にどれを読み書きできるかも少しずつ違います。
-- [209] [[データ形式]]や[[プロトコル]]と[[ファイルシステム]]との相互の行き来でどれとどれを対応付けるべきか。
-- [212] 不足するデータを他のデータと同値にするべきか、放置して成り行きに任せるべきか。
-- [210] 日時の大小が意味的に矛盾するときどうするべきか。
-- [211] [[ファイル]]を説明する[[メタデータ]]と[[ファイル]]に付属する属性データが相違するとき、
どちらを信用するべきか。


** FAT

[SEE[ [[FATの日時形式]] ]]


*** 他の日時形式との換算

[171] 
[CITE['''['''PATCH V5 0/4''']''' fat: timestamp updates]], [TIME[2026-01-01T07:40:50.000Z]] <https://lore.kernel.org/all/2663d3083c4dd62f00b64612c8eaf5542bb05a4c.1538363961.git.sorenson@redhat.com/T/>

[189] >>188



[213] [[生成]]時、[[2秒]]未満は[[切り捨て]] :

[REFS[

- [214] 
[CITE@en[fs/fat/misc.c - kernel/common - Git at Google]], [TIME[2026-01-02T09:40:36.000Z]] <https://android.googlesource.com/kernel/common/%2B/88b912e02d75b/fs/fat/misc.c>
- [219] 
[CITE@en[c - Unix timestamp to FAT timestamp - Stack Overflow]], [TIME[2026-01-02T07:33:21.000Z]] <https://stackoverflow.com/questions/15763259/unix-timestamp-to-fat-timestamp>


]REFS]

[215] [[生成]]時、 [[秒]]を [[round]] :

[REFS[

- [216] [CITE@en[fat-date/src/index.js at master · desaroger/fat-date · GitHub]], [TIME[2026-01-02T09:28:37.000Z]] <https://github.com/desaroger/fat-date/blob/master/src/index.js>

]REFS]




**** 地方時の時刻の取り扱い

[164] 
[CITE[Re: '''['''PATCH''']''' fat: Allow out-of-range FAT modification timestamps]], [TIME[2021-09-02T13:09:07.000Z]], [TIME[2026-01-01T07:21:21.836Z]] <https://lists.gnu.org/archive/html/grub-devel/2021-09/msg00021.html>


[165] 
[CITE@en[timezone - VFAT, Linux: Invalid file timestamps shown after a reboot - Unix & Linux Stack Exchange]], [TIME[2026-01-01T07:28:02.000Z]] <https://unix.stackexchange.com/questions/640555/vfat-linux-invalid-file-timestamps-shown-after-a-reboot>

[166] 
[CITE@en[c# - How do I get the correct modified datetime of a FAT32 file, regardless of timezone in .NET? - Stack Overflow]], [TIME[2026-01-01T07:29:49.000Z]] <https://stackoverflow.com/questions/10068855/how-do-i-get-the-correct-modified-datetime-of-a-fat32-file-regardless-of-timezo>

[167] 
[CITE@ja-jp[ファイル時間 - Win32 apps | Microsoft Learn]], [[stevewhims]], [TIME[2025-04-15T10:36:31.000Z]], [TIME[2026-01-01T07:30:33.261Z]] <https://learn.microsoft.com/ja-jp/windows/win32/sysinfo/file-times?redirectedfrom=MSDN>


[168] 
[CITE@en[Handle daylight savings time changes with FAT filesystems · Issue #9227 · syncthing/syncthing]], [TIME[2026-01-01T07:35:47.000Z]] <https://github.com/syncthing/syncthing/issues/9227>

[169] 
[CITE@en[Unwanted full rescan&resync @ SD card @ Syncthing Android after timezone/daylight saving change - Support / Android - Syncthing Community Forum]], [TIME[2026-01-01T07:36:11.000Z]] <https://forum.syncthing.net/t/unwanted-full-rescan-resync-sd-card-syncthing-android-after-timezone-daylight-saving-change/21064>





** ZIP

[15] 
[[ZIP]] は[[ファイル]]に対して[[日時]]を表す欄を多数有しています。

- [16] last mod file date / last mod file time (date and time fields)
-- [61] [[FATの日時形式]]
-- [68] 各2バイト
-- [114] 必須。
-- [23] [[central directory]] が暗号化され [[general purpose bit flag 13]]
が masking を表すとき、 [[local header]] の値は 0 となる
- [17] 拡張 [N[0x000D]] : [[UNIX]] 用
-- [63] Atime (File last access time), Mtime (File last modification time)
-- [62] 各[[32ビット符号無し整数]] [[小エンディアン]]
--- [67] [[Unix time]]
--- [103] 
[[符号無し整数]]の実装事例 [SRC[>>102]]
-- [18] ほとんど使われていないという。実例未発見。
-- [96] 実装はあるにはあるらしい [SRC[>>95]]。 
- [19] 拡張 [N[0x000A]] : [[NTFS]] 用
-- [64] Mtime (File last modification time), Atime (File last access time), 
Ctime (File creation time)
-- [65] 各[[64ビット符号無し整数]] [[小エンディアン]]
--- [66] [[NTFSの日時形式]]
-- [22] 主に [[Windows]] で使われる。
-- [119] それなりによく使われているがすごく多いともいえない。
- [20] 拡張 [N[0x5455]] : [[Extended Timestamp Extra Field]] ([[Info-ZIP]])
-- [75] [[central directory header]] / [[local header]] : 
ModTime (time of last modification)
-- [76] [[central directory header]] :
ModTime (time of last modification),
AcTime (time of last access),
CrTime (time of original creation)
-- [74] 各[[32ビット符号無し整数]] [[小エンディアン]]
--- [73] [[Unix time]]
--- [101] 
[[符号無し整数]]の実装事例 [SRC[>>100, >>102]]
--- [94] 
[[符号付き整数]]とする実装がある [SRC[>>93, >>105]]
-- [21] 主に [[Unix]] で使われる。
-- [115] それなりによく使われているがすごく多いともいえない。
-- [77] flags でどの時刻が含まれるかを指定できる。5個分、
将来の拡張で時刻の追加ができるとされている。
-- [80] flags は [[local header]] に含まれる時刻を表すとされ、
[[central directory header]] は ModTime のみ含められ、
flags は [[local header]] と同じものを記述するとされる。
--- [81] 実際には [[local header]] と [[central directory header]]
で flags が異なる[[ZIPファイル]]が流通している。
- [37] 拡張 [N[0x5855]] : [[Info-ZIP Unix Extra Field]]
-- [83] [[central directory header]] と [[local header]]
にそれぞれ、 AcTime (time of last access), ModTime (time of last modification)
-- [82] 各[[32ビット符号無し整数]] [[小エンディアン]]
--- [85] [[Unix time]]
--- [104] 
[[符号無し整数]]の実装事例 [SRC[>>102]]
-- [116] たまに使われている。
- [69] 拡張 [N[0x07C8]] : [[Info-ZIP Macintosh Extra Field]]
-- [71] [[central directory header]] と [[local header]]
にそれぞれ、 各[[32ビット整数]] [[大エンディアン]]
--- [84] おそらく[[符号無し整数]]
--- [70] [[HFSの日時形式]]
-- [72] [CODE[CrDat]] ([CODE[ioFlCrDat]]),
[CODE[MdDat]] ([CODE[ioFlMdDat]])
-- [117] 実例未見。
- [38] 拡張 [N[0x334D]] : [[Info-ZIP Macintosh Extra Field (new)]]
-- [86] [[local header]] の Attribs に
FlCrDat (date and time of creation),
FlMdDat (date and time of last modification),
FlBkDat (date and time of last backup)
--- [87] [[Mac]] [[FileTime]] 値
--- [88] [[地方時]]
--- [89] [[32ビット符号無し整数]] [[小エンディアン]]
Flags により64ビットに切り替えることができるとされる。
ただし Currently は32ビットで future version may 64ビットに対応するとある。
-- [90]  [[local header]] の Attribs に
CrGMTOffs, MdGMTOffs, BkGMTOffs
(difference "local [VAR[...]] time - UTC")
--- [91] [[32ビット符号付き整数]] [[小エンディアン]]
--- [92] Flags により含めるかどうかを指定できる。
-- [118] 実例未見。


[60] [[ZIP]] は[[ファイル]]ごとに [[central directory header]] と [[local header]]
があります。前者は [[ZIP]] 全体の[[索引]]として一括されたものであり、
後者は個別の[[ファイル]]のデータに付与されたメタデータです。
どちらにも書き込まれる情報、一方にのみ書き込まれる情報、
両方に別の値で書き込まれる情報があります。
どう書き込むか仕様上で定まっている場合もあれば曖昧な場合もあり、
実データが仕様と一致していないこともよくあります。
こうした性格は[[日時]]情報各種にも当てはまります。

[120] 
拡張の日付がどれも使われていない [[ZIPファイル]]は今でも非常に多いです。
つまり[[地方時]]だけで[[時差]]の情報が皆無のことが多いです。

[121] 
拡張の日付が複数使われる場合もありますが、あまり多くありません。
拡張の日付が使われる場合はほとんどが mtime を指定していますが、
それ以外が指定される場合や、 mtime が 0 の場合もあります。
拡張の mtime と last mod file date / time は[[時差]]を除けば一致すると思われる場合が多いですが、
そうでない場合も散見されます。

[136] 
拡張のどれが使われ、拡張のどの時刻が指定されるかは、
ファイルごとに異なる可能性があります。
特に[[ディレクトリー]]と一般[[ファイル]]で違うケースが散見されます。


[135] 
last mod file date / time は[[地方時]]であり、どこの[[時刻]]であるかは、
ファイルごとに異なる可能性が否定できません。
拡張の時刻があれば、それとの比較で[[時差]]を算出しこれを検出できる可能性があります。


[REFS[

- [36] 
[CITE[null]], [TIME[2022-11-01T17:34:41.000Z]], [TIME[2025-12-30T15:47:51.658Z]] <https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT>
- [35] 
[CITE[null]], [TIME[2020-06-30T08:46:01.000Z]], [TIME[2025-12-30T15:47:33.052Z]] <https://libzip.org/specifications/extrafld.txt>
- [13] [CITE@en[zip展開後のタイムスタンプがおかしい · Issue #31 · koron/netupvim]], [TIME[2025-12-30T14:50:21.000Z]] <https://github.com/koron/netupvim/issues/31>
- [14] [CITE@en[archive/zip: handle mtime in NTFS/UNIX/ExtendedTS extra fields (18274) · Gerrit Code Review]], [TIME[2025-12-30T14:50:33.000Z]] <https://go-review.googlesource.com/c/go/+/18274>
- [79] 
[CITE@en[A non-scientific survey of ZIP Metadata]], [TIME[2025-12-31T09:27:51.000Z]] <https://publicobject.com/2024/02/26/zip-metadata/>
- [93] 
[CITE@en[X5455_ExtendedTimestamp (The Adobe Experience Manager SDK 2022.11.9850.20221116T162329Z-220900)]], [TIME[2025-11-06T10:59:11.000Z]], [TIME[2025-12-31T09:56:30.240Z]] <https://developer.adobe.com/experience-manager/reference-materials/cloud-service/javadoc/org/apache/commons/compress/archivers/zip/X5455_ExtendedTimestamp.html>
- [95] 
[CITE@en[7-Zip / Bugs / #1432 7ZFM can't recognize UNIX timestamps in .ZIP archive]], [TIME[2025-12-31T10:01:42.000Z]] <https://sourceforge.net/p/sevenzip/bugs/1432/>
- [100] 
[CITE@en[add support for Info-ZIP timestamp extra field by thejoshwolfe · Pull Request #86 · thejoshwolfe/yazl · GitHub]], [TIME[2025-12-31T10:14:17.000Z]] <https://github.com/thejoshwolfe/yazl/pull/86/files>
- [102] 
[CITE@en[go/src/archive/zip/reader.go at 9ddb8ea73724d717a9bbf44be7d585ba5587504f · golang/go · GitHub]], [TIME[2025-12-31T10:18:01.000Z]] <https://github.com/golang/go/blob/9ddb8ea73724d717a9bbf44be7d585ba5587504f/src/archive/zip/reader.go#L404-L440>
- [105] 
[CITE[File: ziptools/ziptools/ziptools/zipmodtimeutc.py]], [TIME[2025-12-31T10:25:34.000Z]] <https://learning-python.com/cgi/showcode.py?name=ziptools/ziptools/ziptools/zipmodtimeutc.py>
- [132] 
[CITE@ja-JP[Will *.zip file stop working after 2038 ? : r/software]], [TIME[2025-12-31T12:53:04.000Z]] <https://www.reddit.com/r/software/comments/jqnc9u/will_zip_file_stop_working_after_2038/>


]REFS]


*** 実装戦略

[122] 実装は複数の日付のうちどれを使うかの選択を迫られます。
利用実態や精度を鑑みれば、

= [123] [N[0x000A]] ([[NTFS]])
= [124] [N[0x5455]]
= [125] [N[0x5855]]
= [126] [N[0x000D]] ([[Unix]])
= [127] last mod file date / time

... の優先順が良いと思われます。それぞれ [[central directory header]] → [[local header]]
の順で、値が [N[0]] や無指定なら無視して次を参照します。


[128] 
[[日時]]の読み書きでは、 [[ZIPファイル]]上の[[データ構造]]や[[プラットフォーム]]・[[ファイルシステム]]・[[プログラミング言語]]の[[データ構造]]の違いと、
それに伴う[[値域]]や[[精度]]の違いに注意する必要があります。

[FIG[
- [129] [[FATの日時形式]] : [TIME[1980-01-01T00:00:00]] - [TIME[2107-12:31T23:59:58]] [[地方時]] 2s 単位
- [133] [[32ビット符号付き整数]] [[秒]]単位 : [TIME[1901-12-13T20:45:52Z]] - [TIME[2038-01-19T03:14:07Z]] ([[西暦2038年問題]])
- [134] [[32ビット符号無し整数]] [[秒]]単位 : [TIME[1970-01-01T00:00:00Z]] - [TIME[2106-02-07T06:28:15Z]]
]FIG]

[173] 
[TIME[西暦1901年途中][1901]]から[TIME[西暦1979年][1979]]までは [[Unix time]] で記述できますが、 [[FATの日時形式]]では記述できません。 [[ZIP]] の[[生成]]を強行するなら値を不一致とせざるを得ません。 [[Unix time]] [N[0]] ([TIME[1970-01-01T00:00:00Z]]) がこの範囲に含まれることに注意が必要です。

[174] 
[TIME[西暦2038年][2038]]途中から[TIME[西暦2106年][2106]]途中までは[[32ビット符号付き整数]] [[Unix time]] で記述できますが、[[32ビット符号無し整数]] [[Unix time]] では記述できません。逆に[[32ビット符号無し整数]] [[Unix time]] でなければ[TIME[西暦1969年][1969]]までが記述できません。[[ビット列]]だけを見てもこのどちらであるかを判定できません。ところで [[FATの日時形式]]では都合がいいことに[TIME[西暦2107年][2107]]まで記述できますので、[[32ビット符号無し整数]]で[TIME[西暦2038年][2038]][[以上]]であることは[[FATの日時形式]]の[[年]]から判断できます。都合が悪いことに[[FATの日時形式]]では[TIME[西暦1979年][1979]][[以下]]が記述できないため、 [[32ビット符号付き整数]]であることを確信はできませんが、[[32ビット符号無し整数]]と確定できないなら[[32ビット符号付き整数]]と判断するのが良いと思われます。

[FIG[
- [175] [[64ビット符号付き整数]] [[秒]]単位 : [TIME[−292277022657-01-27T08:29:52Z]] - [TIME[292277026596-12-04T15:30:07Z]]
- [176] [[64ビット符号無し整数]] [[秒]]単位 : [TIME[1970-01-01T00:00:00Z]] - [TIME[584554051223-11-09T07:00:15Z]]
- [177] [[64ビット符号付き整数]] [[ミリ秒]]単位 : [TIME[-292275055-05-16T16:47:04.192Z]] - [TIME[292278994-08-17T07:12:55.807Z]]
- [178] [[64ビット符号付き整数]] [[ナノ秒]]単位 : [TIME[1677-09-21T00:12:43.145224192Z]] - [TIME[2262-04-11T23:47:16.854775807Z]]
]FIG]

[179] 
[[POSIX]] 系の多くの新しい[[プラットフォーム]]は[[64ビット符号付き整数]]の[[秒]]単位 [[Unix time]] を扱えます。 [[FATの日時形式]]と[[32ビット整数]] [[Unix time]] の範囲が完全に収まるので、 [[ZIPファイル]]からの読み込みには適切といえます。 [[ZIPファイル]]への書き込みでは、[[32ビット符号付き整数]]の範囲外の値を適切に処理する必要があります。 [[Java]] の[[ミリ秒]]単位や [[Go]] の[[ナノ秒]]単位の [[Unix time]] でも同じことが言えます。

[180] 
[[64ビット符号無し整数]]は、[TIME[西暦1969年][1969]]以前を扱えないので、適切とはいえません。

[FIG[
- [181] [[64ビット符号付き整数]] : [TIME[-27627-04-19T21:11:54.522419200Z]] - [TIME[-30828-09-14T02:48:05.477580700Z]] 100ns 単位
- [182] [[64ビット符号無し整数]] : [TIME[1601-01-01T00:00:00.000000000Z]] - [TIME[60056-05-28T05:36:10.955161500Z]] 100ns 単位
]FIG]

[183] 
[[NTFSの日時形式]]は[[符号付き整数]]説と[[符号無し整数]]説があります。
実装上も実用上も安定して使えるのは両者の重なりの更に一部だけとされます。 [SEE[ [[NTFSの日時形式]] ]]
従って、 [[ZIPファイル]]に含まれる[[NTFSの日時形式]]がいずれであるかを問題とする必要はありません。
[[ZIP]] の処理では、その仕様に従い[[符号無し整数]]とするのが良いと考えられます。

[184] 
どちらと解釈するにせよ、 [[64ビット符号付き整数]]の[[秒]]単位や[[ミリ秒]]単位の [[Unix time]] を扱えるものの、精度を保つことはできません。実装は [[Unix time]] に統一して[[下位桁溢れ]]を妥協するか、2種類の[[データ型]]を併用するかの選択を求められます。

[185] 
逆に [[Windows]] 系の実装は [[NTFSの日時形式]]を正とし、 [[Unix time]] の範囲の両端を扱えないことを妥協するという選択肢もあり、その方法でも実用上は問題ないと考えられます。

[186] 
[[ナノ秒]]単位の [[Unix time]] は [[NTFSの日時形式]]の精度を完全に記述できるものの、その範囲の両端を扱えないことを妥協する必要があり、それでも実用上は問題ないと考えられます。

[187] 
いずれにせよ、範囲外となる値は適切に処理する必要があります。

[EG[

[130] [[Perlモジュール]] [CODE[Archive::Zip]] は last mod file date / time
が [[Unix time]] で [TIME[31.686060][unix:31.686060]]
[[未満]]の値に設定されたとき、 [TIME[31.686060][unix:31.686060]]
に変更します。 [[時差]]の問題を避けるために[TIME[西暦1980年][1980]][[始][年始]]から12時間1分ずらされています。

[217] 
なお、範囲できない大きな値の検査はしておらず。成り行きとなります。

[218] 
[[FATの日時形式]]で[[2秒]]単位しか表現できない[[秒]]の桁は[[切り捨て]]られます。

]EG]

[EG[
[131] 
>>78 の実装は範囲外の値となるとき、直近の値 (つまり境界の値) を入れます。
]EG]

[188] 
なお、これらの問題は [[ZIP]] 固有でなく、 [[FAT]] や [[NTFS]] や [[SMB]] などでも発生します。しかし、
どの種類の値が組合せて供給されるかなど、条件が少しずつ違います。


[REFS[

[FIG(quote)[
[FIGCAPTION[
[78] 
[CITE@en[GitHub - thejoshwolfe/yazl: yet another zip library for node]], [TIME[2025-12-31T09:25:14.000Z]] <https://github.com/thejoshwolfe/yazl>
]FIGCAPTION]

>When attempting to encode an [CODE[mtime]] outside the supported range for either format, such as the year 1970 in the DOS format or the year 2039 for the modern format, the time will clamped to the closest supported time.

]FIG]

]REFS]

@@
[170] 
[CITE@en[python - Getting Error as "ValueError: Zip does not support timestamps before 1980" while installing Setuptools-scm Pypi - Stack Overflow]], [TIME[2026-01-01T07:37:15.000Z]] <https://stackoverflow.com/questions/62915376/getting-error-as-valueerror-zip-does-not-support-timestamps-before-1980-while>

**** 地方時の時差の推定

[139] 
多くの実装は[[地方時]]しかないとき、[[プラットフォームの時差]]によるものと解釈します。 [[ZIPファイル]]の送受信者は同じ地域に所在することが多いと推測されますから、これでうまくいく場合が多いと思われます。


[140] 
ところが、 [[Webサイト]]からのダウンロードで遠隔地の [[ZIPファイル]]を入手することがあると、この前提は成り立たなくなります。あちこちから収集した [[ZIPファイル]]に機械的な処理をしたいときにも困ります。


[137] 
[[地方時]]しか判明しないときでも、[[ファイル名の文字コード]]の情報があれば、[[文字コード]]と強く結びついた[[時間帯]]と推定できる場合があります。
[SEE[ [[ロケール等による文字コード判定の補助]] ]]

[138] 
[[文字コード]]から判断できない [[UTF-8]] のような場合でも、[[言語判定]]によって[[言語]]と強く結びついた[[時間帯]]と推定できる場合があります。


[142] 
[[ZIPファイル]]が[[インターネット]]から取得したものなら、入手に使った [[URL]] の [[ccTLD]] を利用できることがあります。政府機関等の公開データは [[ccTLD]] を使った [[Webサイト]]で配布されていることが多いので、多くの国で有効と期待できます。



[141] 
その他ファイルの中身の分析なども考えられ、例えば[[ワープロ]]の[[文書形式]]は[[メタ情報]]として変更日時を記録していることが多そうですし、ただの[[テキストファイル]]でも[[言語判定]]で推測できるかもしれませんが、判定コストが高い割に都合がいいファイルが [[ZIPファイル]]に入っている確率は高くなさそうですし、推測の精度もそれほど良くならないことが多そうです。

-*-*-

[143] 推奨される判定方法 :

[FIG(steps)[

= [145] 強制的な指定値がある場合、
== [223] その値を返し、ここで停止します。
= [224] 各ファイルに対し、
== [144] [VAR[拡張の mtime]] がある場合、
=== [221] [VAR[t]] を、
[VAR[last mod file date / time]] -  [VAR[拡張の mtime]] 
に設定します。
=== [222] 
[VAR[t]]を、
sign ([VAR[t]]) ([N[15]] × [N[60]])  ⌊ [FRAC[ |[VAR[t]]| + [FRAC[ [N[15]] × [N[60]] ][2]] ][ [N[15]] × [N[60]] ]] ⌋
に設定します。
=== [146] |[VAR[t]]| ≦ 24 × 60 × 60 の場合、
==== [147] [VAR[t]]を返し、ここで停止します。
= [148] [[ファイル名の文字コード]]から推定時差を1つに定められる場合、
== [149] その値を返します。
= [150] [[ファイル名の文字コード]]から推定時差を2つ ([[標準時]]と[[夏時刻]]) に定められる場合、
== [151] [VAR[地域]]を、その推定時差の地域に設定します。
== [152] [[ccTLD]] の[[国]]が [CODE[null]] でなく、[VAR[地域]]に属する場合、
=== [153] [[ccTLD]] の[[国]]の代表の[[時間帯]]を返します。
== [154] それ以外の場合、
=== [155] [VAR[地域]]の代表の[[時間帯]]を返します。
= [156] それ以外の場合、
== [157] [[ccTLD]] の[[国]]が [CODE[null]] でない場合、
=== [158] [[ccTLD]] の[[国]]の代表の[[時間帯]]を返します。
== [159] それ以外の場合、
=== [160] 決定不能を返します。

]FIG]


*** 利用例

[46] 実利用例:

[REFS[

- [49] 
[TIME[2025-12-31T02:15:50.500Z]]
<https://statistique.quebec.ca/en/fichier/rda-startup-kit.zip>
-- [52] [TIME[2025-12-31T02:20:17.100Z]]
<https://web.archive.org/web/20251231021855/https://statistique.quebec.ca/en/fichier/rda-startup-kit.zip>
--- [53] 古いものは同じ [[URL]] でも別ファイル (日付構造も違う) なので注意。
-- [50] [[ディレクトリー]]は 
[[local header]]
の
[N[0x5455]]
に mtime, atime, ctime (flag もこれに一致)
[[central directory]]
に mtime (flag もこれに一致)
-- [51] 一般[[ファイル]]は
[[central directory]]
に mtime (flag もこれに一致)
- [110] 
[TIME[2025-12-31T12:13:12.800Z]]
<https://www.city.yokohama.lg.jp/kosodate-kyoiku/kyoiku/gakku-meibo/tsugakukuiki/bunnpuzu-gis.files/0019_20250910.zip>
-- [111] 一般ファイルは [[central directory]] [N[0x5455]] に mtime,
last mod file date / time と時差を除き同じ値
- [112] 
[TIME[2025-12-31T12:19:50.000Z]]
<https://www.city.yokohama.lg.jp/business/bunyabetsu/kankyo-koen-gesui/kiseishido/kagaku/risukuhyoka.files/0043_20251201.zip>
-- [113] 一般ファイルは [[central directory]] [N[0x5455]] に mtime,
last mod file date / time と時差を除き同じ値
- [54] 
[TIME[2025-12-31T03:01:46.400Z]]
<https://www.sio.gov.sa/assets/files/guide_files/Arabic.zip>
-- [55] [[ディレクトリー]]は [N[0x5855]] に
[[central directory]], [[local header]] の modtime, actime の計4つ同じ値。
--- [56] おそらく[[時差]]を除いて last mod file date / time とも同じ値。
-- [57] [[ファイル]]は [N[0x5855]] に
[[central directory]] のみ modtime, actime の2つが別の値。
--- [58] last mod file date / time と modtime が同じ値。
- [97] 
[TIME[2025-12-31T10:05:01.00Z]]
<https://web.archive.org/web/20110706022639/http://www.youngcreativechevrolet.eu/app/webroot/uploads/fonts_ycc.zip>
-- [98] [[ディレクトリー]]は [N[0x5855]] に
[[central directory]], [[local header]] の modtime, actime の計4つの値。
-- [99] [[ファイル]]は [N[0x5855]] に
[[central directory]] のみ modtime, actime の2つの値。
- [106] 
[TIME[2025-12-31T12:06:24.300Z]]
<https://catalog-data.city.kanazawa.ishikawa.jp/dataset/89d93f28-38b4-4971-9988-2ff2d3227f56/resource/50049b19-fe9f-4c1f-a9ea-9d0a24141644/download/172103_bus.zip>
-- [107] 一般ファイルの [[central directory]] に
[[NTFS]] の mtime, ctime, atime
--- [108] ctime, atime は [N[0]]
--- [109] mtime と last mod file date / time が時差を除き同じ値
- [47] [TIME[2025-12-30T16:09:29.800Z]]
<https://opendata.attica.gov.gr/content/genikh-dieythynsh-eswterikhs-leitoyrgias/d-nsh-dioikhtikwn-yphresiwn/155-diakhrykseis?fdl=WGNVMDZpUXBkL0VvczA4L0RHM1NMVWxCMkRQSUdMdWlDbjNFY1VGZTZSQXk2aEJoWGhoSkRGZWtSa0diR2c0MVltZ3ZNa3R1VTA1MlZUWnNSM0p6V21wWk5ERk5VVDA5>
-- [48] [[ディレクトリー]], 一般[[ファイル]]とも [[central directory]] に [[NTFS]] 時刻

]REFS]


[25] 人工的な利用例:

[REFS[

- [24] [TIME[2025-12-30T15:12:40.200Z]]<https://bugs.python.org/file48724/t.zip>
-- [26] [[ディレクトリー]]は [N[0x5455]] に
--- [27] [[central directory]] では
flag では mtime と atime が入っていることになっているが値は mtime だけ
(仕様通り)
--- [28] [[local header]] では
mtime と atime の両方が値もある
-- [29] 一般[[ファイル]]は [N[0x5455]] に
--- [30] [[central directory]] では
flag では mtime と atime が入っていることになっているが値は mtime だけ
--- [31] [[local header]] には [N[0x5455]] が無し
- [32] [TIME[2025-12-30T15:26:42.000Z]]<https://github.com/icsharpcode/SharpZipLib/files/2522058/UTF8_Filenames.zip>
-- [33] >>24 と同じ構造
- [161] 
[TIME[2026-01-01T00:49:37.100Z]]
<https://github.com/rubyzip/rubyzip/raw/refs/heads/main/test/data/osx-archive.zip>
-- [162] 
一般[[ファイル]]は 
[[central directory]]
の
[N[0x5455]]
に
atime, ctime, mtime
- [39] [TIME[2025-12-30T15:51:44.800Z]]
<https://github.com/user-attachments/files/16069900/default.zip>
-- [40] [[central directory]] に NTFS タイムスタンプがあります。
3つの時刻のどれも last mod file date / time と微妙にずれています。丸めの差?
- [41] [TIME[2025-12-30T16:02:49.400Z]]
<https://github.com/thejoshwolfe/yauzl/files/1973376/default.zip>
-- [42] [[ディレクトリー]]は [N[0x5855]] に
[[central directory]], [[local header]] の modtime, actime の計4つ同じ値。
--- [43] おそらく[[時差]]を除いて last mod file date / time とも同じ値。
-- [44] [[ファイル]]は [N[0x5855]] に
[[central directory]] のみ modtime, actime の2つが別の値。
--- [45] last mod file date / time とは一致しないが、おそらく modtime
と少し前後した時刻で丸めの差か。

]REFS]




** メモ

[34] 
[CITE@ja[アーカイブファイルフォーマットのタイムスタンプまとめ]], [TIME[2025-12-30T15:42:11.000Z]] <https://zenn.dev/sorairolake/articles/timestamp-of-archive-formats>



[59] 
[[Git]] は [[commit]] に日付があり、[[ファイル]]には[[日付]]がありません。
他の多くの[[版管理システム]]でも同様です。


[163] 
[CITE@en-US[Filesystem Timestamps: What Makes Them Tick?]], [TIME[2026-01-01T07:18:02.000Z]] <https://www.sans.org/white-papers/36842>


* 関連

[SEE[ [[システムの時間帯]], [[地方時]] ]]

* メモ


[172] 
[CITE@en[FatFs - Configuration Options]], [TIME[2025-11-30T15:07:35.000Z]], [TIME[2026-01-01T08:02:08.432Z]] <https://elm-chan.org/fsw/ff/doc/config.html#fs_nortc>

[220] 
[CITE@ja[ファイルのタイムスタンプが変更されるタイミング]], [TIME[2026-01-02T12:43:35.000Z]] <https://zenn.dev/megeton/articles/e58ea554ba5650>
