github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/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/cockroach/pkg/util/syncutil"
    18  )
    19  
    20  // zoneCache stores the results of resolving time.Location instances.
    21  //
    22  // time.LoadLocation does not perform caching internally and requires
    23  // many disk accesses to locate the named zoneinfo file.
    24  //
    25  // We also save time.FixedZone calls to avoid needing to regenerate
    26  // the string representation.
    27  type zoneCache struct {
    28  	mu struct {
    29  		syncutil.Mutex
    30  		named map[string]*zoneCacheEntry
    31  		fixed map[int]*time.Location
    32  	}
    33  }
    34  
    35  var zoneCacheInstance = zoneCache{}
    36  
    37  func init() {
    38  	zoneCacheInstance.mu.named = make(map[string]*zoneCacheEntry)
    39  	zoneCacheInstance.mu.fixed = make(map[int]*time.Location)
    40  }
    41  
    42  type zoneCacheEntry struct {
    43  	loc *time.Location
    44  	// We don't expect the tzinfo database to change frequently, relative
    45  	// to restarts of the server. Errors are unlikely to ever resolve.
    46  	err error
    47  }
    48  
    49  // LoadLocation wraps time.LoadLocation which does not perform
    50  // caching internally and which requires many disk accesses to
    51  // locate the named zoneinfo file.
    52  func (z *zoneCache) LoadLocation(zone string) (*time.Location, error) {
    53  	z.mu.Lock()
    54  	entry, ok := z.mu.named[zone]
    55  	z.mu.Unlock()
    56  
    57  	if !ok {
    58  		loc, err := time.LoadLocation(zone)
    59  
    60  		entry = &zoneCacheEntry{loc: loc, err: err}
    61  		z.mu.Lock()
    62  		z.mu.named[zone] = entry
    63  		z.mu.Unlock()
    64  	}
    65  	return entry.loc, entry.err
    66  }
    67  
    68  // FixedZone wraps time.FixedZone.
    69  func (z *zoneCache) FixedZone(hours, minutes, seconds int) *time.Location {
    70  	offset := (hours*60+minutes)*60 + seconds
    71  	z.mu.Lock()
    72  	ret, ok := z.mu.fixed[offset]
    73  	z.mu.Unlock()
    74  
    75  	if !ok {
    76  		if minutes < 0 {
    77  			minutes *= -1
    78  		}
    79  		if seconds < 0 {
    80  			seconds *= -1
    81  		}
    82  		// Need a field width of 3 for the leading digit because we're
    83  		// forcing the sign to be printed and it counts as part of the field.
    84  		ret = time.FixedZone(fmt.Sprintf("%+03d%02d%02d", hours, minutes, seconds), offset)
    85  		z.mu.Lock()
    86  		z.mu.fixed[offset] = ret
    87  		z.mu.Unlock()
    88  	}
    89  
    90  	return ret
    91  }