github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/pkg/util/tz.go (about)

     1  // Copyright 2020 PingCAP, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package util
    15  
    16  import (
    17  	"path/filepath"
    18  	"strings"
    19  	"time"
    20  
    21  	"github.com/pingcap/log"
    22  	tiTypes "github.com/pingcap/tidb/pkg/types"
    23  	"github.com/pingcap/tidb/pkg/util/timeutil"
    24  	cerror "github.com/pingcap/tiflow/pkg/errors"
    25  	"go.uber.org/zap"
    26  )
    27  
    28  // GetTimezone returns the timezone specified by the name
    29  func GetTimezone(name string) (tz *time.Location, err error) {
    30  	switch strings.ToLower(name) {
    31  	case "", "system", "local":
    32  		tz, err = GetLocalTimezone()
    33  		err = cerror.WrapError(cerror.ErrLoadTimezone, err)
    34  		if err == nil {
    35  			log.Info("Use the timezone of the TiCDC server machine",
    36  				zap.String("timezoneName", name),
    37  				zap.String("timezone", tz.String()))
    38  		}
    39  	default:
    40  		tz, err = time.LoadLocation(name)
    41  		err = cerror.WrapError(cerror.ErrLoadTimezone, err)
    42  		if err == nil {
    43  			log.Info("Load the timezone specified by the user",
    44  				zap.String("timezoneName", name),
    45  				zap.String("timezone", tz.String()))
    46  		}
    47  	}
    48  	return
    49  }
    50  
    51  // GetTimezoneFromZonefile read the timezone from file
    52  func GetTimezoneFromZonefile(zonefile string) (tz *time.Location, err error) {
    53  	// the linked path of `/etc/localtime` sample:
    54  	// MacOS: /var/db/timezone/zoneinfo/Asia/Shanghai
    55  	// Linux: /usr/share/zoneinfo/Asia/Shanghai
    56  	region := filepath.Base(filepath.Dir(zonefile))
    57  	zone := filepath.Base(zonefile)
    58  	var tzName string
    59  	if region == "zoneinfo" {
    60  		tzName = zone
    61  	} else {
    62  		tzName = filepath.Join(region, zone)
    63  	}
    64  	tz, err = time.LoadLocation(tzName)
    65  	err = cerror.WrapError(cerror.ErrLoadTimezone, err)
    66  	return
    67  }
    68  
    69  // GetLocalTimezone returns the timezone in local system
    70  func GetLocalTimezone() (*time.Location, error) {
    71  	if time.Local.String() != "Local" {
    72  		return time.Local, nil
    73  	}
    74  	str := timeutil.InferSystemTZ()
    75  	return GetTimezoneFromZonefile(str)
    76  }
    77  
    78  // GetTimeZoneName returns the timezone name in a time.Location.
    79  func GetTimeZoneName(tz *time.Location) string {
    80  	if tz == nil {
    81  		return ""
    82  	}
    83  	return tz.String()
    84  }
    85  
    86  // ConvertTimezone converts the timestamp to the specified timezone
    87  func ConvertTimezone(timestamp string, location string) (string, error) {
    88  	t, err := tiTypes.ParseTimestamp(tiTypes.StrictContext, timestamp)
    89  	if err != nil {
    90  		return "", err
    91  	}
    92  
    93  	var tz *time.Location
    94  	switch strings.ToLower(location) {
    95  	case "", "system", "local":
    96  		tz, err = GetLocalTimezone()
    97  	default:
    98  		tz, err = time.LoadLocation(location)
    99  	}
   100  	if err != nil {
   101  		log.Info("cannot load timezone location",
   102  			zap.String("location", location), zap.Error(err))
   103  		return "", err
   104  	}
   105  
   106  	err = t.ConvertTimeZone(tz, time.UTC)
   107  	if err != nil {
   108  		return "", err
   109  	}
   110  
   111  	return t.String(), nil
   112  }