Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/time/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,8 @@ func (t Time) AppendFormat(b []byte, layout string) []byte {
return t.appendFormatRFC3339(b, false)
case RFC3339Nano:
return t.appendFormatRFC3339(b, true)
case imfFixdate:
return t.appendFormatIMFFixdate(b)
default:
return t.appendFormat(b, layout)
}
Expand Down
41 changes: 41 additions & 0 deletions src/time/format_imffixdate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright 2026 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package time

// imfFixdate is the IMF-fixdate layout: the format HTTP senders are required
// to generate for header fields carrying a date, defined in RFC 9110, section
// 5.6.7 as a fixed-length, single-zone subset of the Internet Message Format
// (RFC 5322). It is the value of net/http.TimeFormat, and after RFC 3339 it is
// the most widely used machine-readable layout, which is why it is
// specialized here.
//
// "GMT" is a literal in this layout, not a zone abbreviation, so formatting
// does not convert. An HTTP-date represents an instant in UTC; supplying one
// is the caller's responsibility, as net/http does.
const imfFixdate = "Mon, 02 Jan 2006 15:04:05 GMT"

// appendFormatIMFFixdate is a specialization of appendFormat for imfFixdate.
// It formats t in its own location, exactly as the general path does.
func (t Time) appendFormatIMFFixdate(b []byte) []byte {
_, _, abs := t.locabs()
days := abs.days()
year, month, day := days.date()
hour, min, sec := abs.clock()

b = append(b, days.weekday().String()[:3]...)
b = append(b, ',', ' ')
b = appendInt(b, day, 2)
b = append(b, ' ')
b = append(b, month.String()[:3]...)
b = append(b, ' ')
b = appendInt(b, year, 4)
b = append(b, ' ')
b = appendInt(b, hour, 2)
b = append(b, ':')
b = appendInt(b, min, 2)
b = append(b, ':')
b = appendInt(b, sec, 2)
return append(b, " GMT"...)
}
22 changes: 22 additions & 0 deletions src/time/format_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1091,3 +1091,25 @@ func FuzzParseRFC3339(f *testing.F) {
}
})
}

// TestAppendFormatIMFFixdate checks the specialized formatter for the
// IMF-fixdate layout (RFC 9110, section 5.6.7) against the general one, across
// many instants and several locations. The non-UTC cases matter: "GMT" is a
// literal in this layout, not a zone abbreviation, so the time must not be
// converted.
func TestAppendFormatIMFFixdate(t *testing.T) {
const imfFixdate = "Mon, 02 Jan 2006 15:04:05 GMT" // net/http.TimeFormat
locs := []*Location{UTC, Local, FixedZone("CEST", 2*60*60), FixedZone("X", -11*3600-1800)}
base := Date(1990, January, 1, 0, 0, 0, 0, UTC)
var got, want [64]byte
for _, loc := range locs {
for i := range 20000 {
tm := base.Add(Duration(i) * 7919 * Second).In(loc)
g := tm.AppendFormat(got[:0], imfFixdate)
w := AppendFormatAny(tm, want[:0], imfFixdate)
if !bytes.Equal(g, w) {
t.Fatalf("loc=%v t=%v AppendFormat=%q general=%q", loc, tm, g, w)
}
}
}
}
16 changes: 16 additions & 0 deletions src/time/time_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1545,6 +1545,22 @@ func BenchmarkFormatRFC3339Nano(b *testing.B) {
}
}

func BenchmarkFormatIMFFixdate(b *testing.B) {
t := Unix(1265346057, 0)
for i := 0; i < b.N; i++ {
t.Format("Mon, 02 Jan 2006 15:04:05 GMT")
}
}

// BenchmarkFormatRFC1123 uses a layout the same length as the HTTP date
// format.
func BenchmarkFormatRFC1123(b *testing.B) {
t := Unix(1265346057, 0)
for i := 0; i < b.N; i++ {
t.Format("Mon, 02 Jan 2006 15:04:05 MST")
}
}

func BenchmarkFormatNow(b *testing.B) {
// Like BenchmarkFormat, but easier, because the time zone
// lookup cache is optimized for the present.
Expand Down