[go] archive/zip: honor Modified field in CreateRaw

0 views
Skip to first unread message

Gerrit Bot (Gerrit)

unread,
Dec 24, 2025, 11:33:48 PM (3 hours ago) Dec 24
to goph...@pubsubhelper.golang.org, Heath Dutton, golang-co...@googlegroups.com

Gerrit Bot has uploaded the change for review

Commit message

archive/zip: honor Modified field in CreateRaw

CreateRaw was ignoring the Modified field in FileHeader, requiring users to call the deprecated SetModTime method as a workaround.

This change adds the same timestamp handling logic to CreateRaw that CreateHeader already uses.

Fixes #76741
Change-Id: I3bd92f283fbed8fcde99a5e25bfa8ed112ddff90
GitHub-Last-Rev: 1041ef17ec5a74a90559f87524a2a1dd9df8fc70
GitHub-Pull-Request: golang/go#76986

Change diff

diff --git a/src/archive/zip/writer.go b/src/archive/zip/writer.go
index 0a31005..b55b516 100644
--- a/src/archive/zip/writer.go
+++ b/src/archive/zip/writer.go
@@ -446,6 +446,34 @@
fh.CompressedSize = uint32(min(fh.CompressedSize64, uint32max))
fh.UncompressedSize = uint32(min(fh.UncompressedSize64, uint32max))

+ // If Modified is set, this takes precedence over MS-DOS timestamp fields.
+ if !fh.Modified.IsZero() {
+ // Contrary to the FileHeader.SetModTime method, we intentionally
+ // do not convert to UTC, because we assume the user intends to encode
+ // the date using the specified timezone. A user may want this control
+ // because many legacy ZIP readers interpret the timestamp according
+ // to the local timezone.
+ //
+ // The timezone is only non-UTC if a user directly sets the Modified
+ // field directly themselves. All other approaches sets UTC.
+ fh.ModifiedDate, fh.ModifiedTime = timeToMsDosTime(fh.Modified)
+
+ // Use "extended timestamp" format since this is what Info-ZIP uses.
+ // Nearly every major ZIP implementation uses a different format,
+ // but at least most seem to be able to understand the other formats.
+ //
+ // This format happens to be identical for both local and central header
+ // if modification time is the only timestamp being encoded.
+ var mbuf [9]byte // 2*SizeOf(uint16) + SizeOf(uint8) + SizeOf(uint32)
+ mt := uint32(fh.Modified.Unix())
+ eb := writeBuf(mbuf[:])
+ eb.uint16(extTimeExtraID)
+ eb.uint16(5) // Size: SizeOf(uint8) + SizeOf(uint32)
+ eb.uint8(1) // Flags: ModTime
+ eb.uint32(mt) // ModTime
+ fh.Extra = append(fh.Extra, mbuf[:]...)
+ }
+
h := &header{
FileHeader: fh,
offset: uint64(w.cw.count),
diff --git a/src/archive/zip/writer_test.go b/src/archive/zip/writer_test.go
index 44592ce..4125502 100644
--- a/src/archive/zip/writer_test.go
+++ b/src/archive/zip/writer_test.go
@@ -533,6 +533,49 @@
}
}

+func TestWriterCreateRawTime(t *testing.T) {
+ // Test that CreateRaw honors the Modified field in FileHeader.
+ // See https://go.dev/issue/76741
+ content := []byte("test content")
+ modified := time.Date(2023, 6, 15, 10, 30, 45, 0, time.UTC)
+
+ // Write a zip file using CreateRaw with Modified set.
+ var buf bytes.Buffer
+ w := NewWriter(&buf)
+
+ h := &FileHeader{
+ Name: "test.txt",
+ Method: Store,
+ CRC32: crc32.ChecksumIEEE(content),
+ CompressedSize64: uint64(len(content)),
+ UncompressedSize64: uint64(len(content)),
+ Modified: modified,
+ }
+ raw, err := w.CreateRaw(h)
+ if err != nil {
+ t.Fatalf("CreateRaw: %v", err)
+ }
+ if _, err := raw.Write(content); err != nil {
+ t.Fatalf("Write: %v", err)
+ }
+ if err := w.Close(); err != nil {
+ t.Fatalf("Close: %v", err)
+ }
+
+ // Read it back and verify the Modified time.
+ r, err := NewReader(bytes.NewReader(buf.Bytes()), int64(buf.Len()))
+ if err != nil {
+ t.Fatalf("NewReader: %v", err)
+ }
+ if len(r.File) != 1 {
+ t.Fatalf("got %d files; want 1", len(r.File))
+ }
+ got := r.File[0]
+ if !got.Modified.Equal(modified) {
+ t.Errorf("Modified = %v; want %v", got.Modified, modified)
+ }
+}
+
func testCreate(t *testing.T, w *Writer, wt *WriteTest) {
header := &FileHeader{
Name: wt.Name,

Change information

Files:
  • M src/archive/zip/writer.go
  • M src/archive/zip/writer_test.go
Change size: M
Delta: 2 files changed, 71 insertions(+), 0 deletions(-)
Open in Gerrit

Related details

Attention set is empty
Submit Requirements:
  • requirement is not satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
  • requirement is not satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: newchange
Gerrit-Project: go
Gerrit-Branch: master
Gerrit-Change-Id: I3bd92f283fbed8fcde99a5e25bfa8ed112ddff90
Gerrit-Change-Number: 732600
Gerrit-PatchSet: 1
Gerrit-Owner: Gerrit Bot <letsus...@gmail.com>
Gerrit-CC: Heath Dutton <heath...@gmail.com>
unsatisfied_requirement
satisfied_requirement
open
diffy

Gopher Robot (Gerrit)

unread,
Dec 24, 2025, 11:33:51 PM (3 hours ago) Dec 24
to Heath Dutton, Gerrit Bot, goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

Gopher Robot added 1 comment

Patchset-level comments
File-level comment, Patchset 1 (Latest):
Gopher Robot . unresolved

I spotted some possible problems with your PR:

  1. You have a long 130 character line in the commit message body. Please add line breaks to long lines that should be wrapped. Lines in the commit message body should be wrapped at ~76 characters unless needed for things like URLs or tables. (Note: GitHub might render long lines as soft-wrapped, so double-check in the Gerrit commit message shown above.)

Please address any problems by updating the GitHub PR.

When complete, mark this comment as 'Done' and click the [blue 'Reply' button](https://go.dev/wiki/GerritBot#i-left-a-reply-to-a-comment-in-gerrit-but-no-one-but-me-can-see-it) above. These findings are based on heuristics; if a finding does not apply, briefly reply here saying so.

To update the commit title or commit message body shown here in Gerrit, you must edit the GitHub PR title and PR description (the first comment) in the GitHub web interface using the 'Edit' button or 'Edit' menu entry there. Note: pushing a new commit to the PR will not automatically update the commit message used by Gerrit.

For more details, see:

(In general for Gerrit code reviews, the change author is expected to [log in to Gerrit](https://go-review.googlesource.com/login/) with a Gmail or other Google account and then close out each piece of feedback by marking it as 'Done' if implemented as suggested or otherwise reply to each review comment. See the [Review](https://go.dev/doc/contribute#review) section of the Contributing Guide for details.)

Open in Gerrit

Related details

Attention set is empty
Submit Requirements:
    • requirement is not satisfiedCode-Review
    • requirement is not satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    • requirement is not satisfiedTryBots-Pass
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: go
    Gerrit-Branch: master
    Gerrit-Change-Id: I3bd92f283fbed8fcde99a5e25bfa8ed112ddff90
    Gerrit-Change-Number: 732600
    Gerrit-PatchSet: 1
    Gerrit-Owner: Gerrit Bot <letsus...@gmail.com>
    Gerrit-CC: Gopher Robot <go...@golang.org>
    Gerrit-CC: Heath Dutton <heath...@gmail.com>
    Gerrit-Comment-Date: Thu, 25 Dec 2025 04:33:46 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: No
    unsatisfied_requirement
    open
    diffy

    Gerrit Bot (Gerrit)

    unread,
    Dec 24, 2025, 11:42:43 PM (3 hours ago) Dec 24
    to Heath Dutton, goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

    Gerrit Bot uploaded new patchset

    Gerrit Bot uploaded patch set #2 to this change.
    Open in Gerrit

    Related details

    Attention set is empty
    Submit Requirements:
    • requirement is not satisfiedCode-Review
    • requirement is not satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    • requirement is not satisfiedTryBots-Pass
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: newpatchset
    Gerrit-Project: go
    Gerrit-Branch: master
    Gerrit-Change-Id: I3bd92f283fbed8fcde99a5e25bfa8ed112ddff90
    Gerrit-Change-Number: 732600
    Gerrit-PatchSet: 2
    unsatisfied_requirement
    open
    diffy
    Reply all
    Reply to author
    Forward
    0 new messages