github.com/wangyougui/gf/v2@v2.6.5/os/gtime/gtime_time_zone.go (about)

     1  // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the MIT License.
     4  // If a copy of the MIT was not distributed with this file,
     5  // You can obtain one at https://github.com/wangyougui/gf.
     6  
     7  package gtime
     8  
     9  import (
    10  	"os"
    11  	"strings"
    12  	"sync"
    13  	"time"
    14  
    15  	"github.com/wangyougui/gf/v2/errors/gcode"
    16  	"github.com/wangyougui/gf/v2/errors/gerror"
    17  )
    18  
    19  var (
    20  	setTimeZoneMu   sync.Mutex
    21  	setTimeZoneName string
    22  	zoneMap         = make(map[string]*time.Location)
    23  	zoneMu          sync.RWMutex
    24  )
    25  
    26  // SetTimeZone sets the time zone for current whole process.
    27  // The parameter `zone` is an area string specifying corresponding time zone,
    28  // eg: Asia/Shanghai.
    29  //
    30  // PLEASE VERY NOTE THAT:
    31  // 1. This should be called before package "time" import.
    32  // 2. This function should be called once.
    33  // 3. Please refer to issue: https://github.com/golang/go/issues/34814
    34  func SetTimeZone(zone string) (err error) {
    35  	setTimeZoneMu.Lock()
    36  	defer setTimeZoneMu.Unlock()
    37  	if setTimeZoneName != "" && !strings.EqualFold(zone, setTimeZoneName) {
    38  		return gerror.NewCodef(
    39  			gcode.CodeInvalidOperation,
    40  			`process timezone already set using "%s"`,
    41  			setTimeZoneName,
    42  		)
    43  	}
    44  	defer func() {
    45  		if err == nil {
    46  			setTimeZoneName = zone
    47  		}
    48  	}()
    49  
    50  	// It is already set to time.Local.
    51  	if strings.EqualFold(zone, time.Local.String()) {
    52  		return
    53  	}
    54  
    55  	// Load zone info from specified name.
    56  	location, err := time.LoadLocation(zone)
    57  	if err != nil {
    58  		err = gerror.WrapCodef(gcode.CodeInvalidParameter, err, `time.LoadLocation failed for zone "%s"`, zone)
    59  		return err
    60  	}
    61  
    62  	// Update the time.Local for once.
    63  	time.Local = location
    64  
    65  	// Update the timezone environment for *nix systems.
    66  	var (
    67  		envKey   = "TZ"
    68  		envValue = location.String()
    69  	)
    70  	if err = os.Setenv(envKey, envValue); err != nil {
    71  		err = gerror.WrapCodef(
    72  			gcode.CodeUnknown,
    73  			err,
    74  			`set environment failed with key "%s", value "%s"`,
    75  			envKey, envValue,
    76  		)
    77  	}
    78  	return
    79  }
    80  
    81  // ToLocation converts current time to specified location.
    82  func (t *Time) ToLocation(location *time.Location) *Time {
    83  	newTime := t.Clone()
    84  	newTime.Time = newTime.Time.In(location)
    85  	return newTime
    86  }
    87  
    88  // ToZone converts current time to specified zone like: Asia/Shanghai.
    89  func (t *Time) ToZone(zone string) (*Time, error) {
    90  	if location, err := t.getLocationByZoneName(zone); err == nil {
    91  		return t.ToLocation(location), nil
    92  	} else {
    93  		return nil, err
    94  	}
    95  }
    96  
    97  func (t *Time) getLocationByZoneName(name string) (location *time.Location, err error) {
    98  	zoneMu.RLock()
    99  	location = zoneMap[name]
   100  	zoneMu.RUnlock()
   101  	if location == nil {
   102  		location, err = time.LoadLocation(name)
   103  		if err != nil {
   104  			err = gerror.Wrapf(err, `time.LoadLocation failed for name "%s"`, name)
   105  		}
   106  		if location != nil {
   107  			zoneMu.Lock()
   108  			zoneMap[name] = location
   109  			zoneMu.Unlock()
   110  		}
   111  	}
   112  	return
   113  }
   114  
   115  // Local converts the time to local timezone.
   116  func (t *Time) Local() *Time {
   117  	newTime := t.Clone()
   118  	newTime.Time = newTime.Time.Local()
   119  	return newTime
   120  }