@@ -5,6 +5,7 @@ package date
55
66import (
77 "bufio"
8+ "bytes"
89 "errors"
910 "fmt"
1011 "io"
@@ -218,6 +219,10 @@ func tzFromTZifFooter(data []byte) (string, error) {
218219
219220// posixToIANA maps common POSIX TZ strings (from TZif footers) to IANA names.
220221// This covers the most common timezones; the walk fallback handles the rest.
222+ // Note: some mappings are ambiguous (e.g., "CST-8" matches Asia/Shanghai,
223+ // Asia/Taipei, and Asia/Hong_Kong). We pick a representative zone for each
224+ // POSIX string. This is a best-effort fast path — the walk fallback will
225+ // find the exact match if the footer mapping is wrong for a given system.
221226var posixToIANA = map [string ]string {
222227 "EST5EDT,M3.2.0,M11.1.0" : "America/New_York" ,
223228 "CST6CDT,M3.2.0,M11.1.0" : "America/Chicago" ,
@@ -276,17 +281,17 @@ func matchLocaltimeByCommonPaths(fs afero.Fs, localtime []byte) (string, error)
276281 if err != nil {
277282 continue
278283 }
279- if len (candidate ) == len ( localtime ) && string ( candidate ) == string ( localtime ) {
284+ if bytes . Equal (candidate , localtime ) {
280285 return tz , nil
281286 }
282287 }
283288 }
284289 return "" , fmt .Errorf ("no common timezone matched" )
285290}
286291
287- // errMatchFound is a sentinel error used to stop walking the zoneinfo tree
288- // once a matching timezone file has been found .
289- var errMatchFound = errors .New ("match found " )
292+ // errWalkDone is a sentinel error used to stop walking the zoneinfo tree
293+ // early, either because a match was found or the file count limit was reached .
294+ var errWalkDone = errors .New ("walk done " )
290295
291296// maxZoneinfoFiles limits the number of files compared during a full zoneinfo
292297// tree walk. This prevents pathological performance on tar-backed filesystems
@@ -318,24 +323,24 @@ func findMatchingZoneinfo(fs afero.Fs, base string, localtime []byte) (string, e
318323
319324 filesChecked ++
320325 if filesChecked > maxZoneinfoFiles {
321- return errMatchFound // bail out, we've checked enough
326+ return errWalkDone // bail out, we've checked enough
322327 }
323328
324329 candidate , err := afero .ReadFile (fs , path )
325330 if err != nil {
326331 return nil // skip unreadable files
327332 }
328- if len (candidate ) == len ( localtime ) && string ( candidate ) == string ( localtime ) {
333+ if bytes . Equal (candidate , localtime ) {
329334 rel := strings .TrimPrefix (path , base + "/" )
330335 // Validate it looks like an IANA name (contains a slash, e.g. "America/New_York")
331336 if strings .Contains (rel , "/" ) {
332337 match = rel
333- return errMatchFound
338+ return errWalkDone
334339 }
335340 }
336341 return nil
337342 })
338- if err != nil && ! errors .Is (err , errMatchFound ) {
343+ if err != nil && ! errors .Is (err , errWalkDone ) {
339344 return "" , err
340345 }
341346 if match == "" {
0 commit comments