github.com/cockroachdb/cockroachdb-parser@v0.23.3-0.20240213214944-911057d40c9a/pkg/util/timeutil/pgdate/zone_cache.go (about) 1 // Copyright 2018 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package pgdate 12 13 import ( 14 "fmt" 15 "time" 16 17 "github.com/cockroachdb/cockroachdb-parser/pkg/util/syncutil" 18 "github.com/cockroachdb/cockroachdb-parser/pkg/util/timeutil" 19 ) 20 21 // zoneCache stores the results of resolving time.Location instances. 22 // 23 // timeutil.LoadLocation does not perform caching internally and requires 24 // many disk accesses to locate the named zoneinfo file. 25 // 26 // We also save time.FixedZone calls to avoid needing to regenerate 27 // the string representation. 28 type zoneCache struct { 29 mu struct { 30 syncutil.Mutex 31 named map[string]*zoneCacheEntry 32 fixed map[int]*time.Location 33 } 34 } 35 36 var zoneCacheInstance = zoneCache{} 37 38 func init() { 39 zoneCacheInstance.mu.named = make(map[string]*zoneCacheEntry) 40 zoneCacheInstance.mu.fixed = make(map[int]*time.Location) 41 } 42 43 type zoneCacheEntry struct { 44 loc *time.Location 45 // We don't expect the tzinfo database to change frequently, relative 46 // to restarts of the server. Errors are unlikely to ever resolve. 47 err error 48 } 49 50 // LoadLocation wraps timeutil.LoadLocation which does not perform 51 // caching internally and which requires many disk accesses to 52 // locate the named zoneinfo file. 53 func (z *zoneCache) LoadLocation(zone string) (*time.Location, error) { 54 z.mu.Lock() 55 entry, ok := z.mu.named[zone] 56 z.mu.Unlock() 57 58 if !ok { 59 loc, err := timeutil.LoadLocation(zone) 60 61 entry = &zoneCacheEntry{loc: loc, err: err} 62 z.mu.Lock() 63 z.mu.named[zone] = entry 64 z.mu.Unlock() 65 } 66 return entry.loc, entry.err 67 } 68 69 // FixedZone wraps time.FixedZone. 70 func (z *zoneCache) FixedZone(hours, minutes, seconds int) *time.Location { 71 offset := (hours*60+minutes)*60 + seconds 72 z.mu.Lock() 73 ret, ok := z.mu.fixed[offset] 74 z.mu.Unlock() 75 76 if !ok { 77 if minutes < 0 { 78 minutes *= -1 79 } 80 if seconds < 0 { 81 seconds *= -1 82 } 83 // Need a field width of 3 for the leading digit because we're 84 // forcing the sign to be printed and it counts as part of the field. 85 ret = time.FixedZone(fmt.Sprintf("%+03d%02d%02d", hours, minutes, seconds), offset) 86 z.mu.Lock() 87 z.mu.fixed[offset] = ret 88 z.mu.Unlock() 89 } 90 91 return ret 92 }