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  }