目录
  • 1. 时间与时区
    • 1.1 时间标准
    • 1.2 时区划分
    • 1.3 Local 时间
  • 2. Go 中的时间及序列化
    • 2.1 Go 如何初始化时区
    • 2.2 Go 时间字段的序列化
    • 2.3 Go 结构体中的时间字段序列化
  • 3. 各种环境下设置时区
    • 3.1 在 Linux 中
    • 3.1 在 Docker 中
    • 3.2 在 Kubernetes 中
  • 4. 参考
    • 5.golang时区处理
      • 总结

        1. 时间与时区

        1.1 时间标准

        UTC,世界标准时间,是现在的时间标准,以原子时计时。

        GMT,格林威治时间,是以前的时间标准,规定太阳每天经过位于英国伦敦郊区的皇家格林威治天文台的时间为中午 12 点。

        UTC 时间更加准确,但如果对精度要求不高,可以视两种标准等同。

        1.2 时区划分

        从格林威治本初子午线起,经度每向东或者向西间隔 15°,就划分一个时区,因此一共有 24 个时区,东、西个 12 个。

        但为了行政上的方便,通常会将一个国家或者一个省份划分在一起。下面是几个 UTC 表示的时间:

        • UTC-6(CST — 北美中部标准时间)
        • UTC+9(JST — 日本标准时间)
        • UTC+8(CT/CST — 中原标准时间)
        • UTC+5:30(IST — 印度标准时间)
        • UTC+3(MSK — 莫斯科时区)

        1.3 Local 时间

        Local 时间为当前系统的带时区时间,可以通过 /etc/localtime 获取。实际上 /etc/localtime 是指向 zoneinfo 目录下的某个时区。下面是 MacOS 上的执行结果,Linux 上的路径会不一样:

        ls -al  /etc/localtime
        
        lrwxr-xr-x  1 root  wheel  39 Apr 26  2021 /etc/localtime -> /var/db/timezone/zoneinfo/Asia/Shanghai
        

        2. Go 中的时间及序列化

        2.1 Go 如何初始化时区

        1. 查找 TZ 变量获取时区
        2. 如果没有 TZ,那么使用 /etc/localtime
        3. 如果 TZ=””,那么使用 UTC
        4. 当 TZ=“foo” 或者 TZ=”:foo”时,如果 foo 指向的文件将被用于初始化时区,否则使用 /usr/share/zoneinfo/foo

        下面是 Go 实现的源码:

        tz, ok := syscall.Getenv("TZ")
        
        switch {
        
        case !ok:
        
        	z, err := loadLocation("localtime", []string{"/etc"})
        
        	if err == nil {
        
        		localLoc = *z
        
        		localLoc.name = "Local"
        
        		return
        
        	}
        
        case tz != "":
        
        	if tz[0] == ':' {
        
        		tz = tz[1:]
        
        	}
        
        	if tz != "" && tz[0] == '/' {
        
        		if z, err := loadLocation(tz, []string{""}); err == nil {
        
        			localLoc = *z
        
        			if tz == "/etc/localtime" {
        
        				localLoc.name = "Local"
        
        			} else {
        
        				localLoc.name = tz
        
        			}
        
        			return
        
        		}
        
        	} else if tz != "" && tz != "UTC" {
        
        		if z, err := loadLocation(tz, zoneSources); err == nil {
        
        			localLoc = *z
        
        			return
        
        		}
        
        	}
        
        }

        2.2 Go 时间字段的序列化

        在 Go 使用 “encoding/json” 可以对 Time 字段进行序列化,使用 Format 可以对时间格式进行自定义。如下示例:

        package main
        
        import (
        
        	"encoding/json"
        
        	"fmt"
        
        	"time"
        
        )
        
        func main(){
        
        	fmt.Println(time.Now())
        
        	var a, _ := json.Marshal(time.Now())
        
        	fmt.Println(string(a))
        
        	a, _ = json.Marshal(time.Now().Format(time.RFC1123))
        
        	fmt.Println(string(a))
        
        	a, _ = json.Marshal(time.Now().Format("06-01-02"))
        
        	fmt.Println(string(a))
        
        }

        输出结果:

        2021-12-07 16:44:44.874809 +0800 CST m=+0.000070010

        “2021-12-07T16:44:44.874937+08:00”

        “Tue, 07 Dec 2021 16:44:44 CST”

        “00-120-74 16:44:07”

        “21-12-07”

        2.3 Go 结构体中的时间字段序列化

        在结构体中,如果直接使用 “encoding/json” 对结构体进行序列化,得到的将会是这样的时间格式: 2021-12-07T17:31:08.811045+08:00。无法使用 Format 函数对时间格式进行控制。

        那么,如何控制结构体中的时间格式呢?请看如下示例:

        package main
        
        import (
        
        	"fmt"
        
        	"strings"
        
        	"time"
        
        	"unsafe"
        
        	"encoding/json"
        
        	jsoniter "github.com/json-iterator/go"
        
        )
        
        func main() {
        
        	var json2 = NewJsonTime()
        
        	var d = struct {
        
        		Title string `json:"title"`
        
        		StartedAt time.Time `json:"time"`
        
        	}{
        
        		Title: "this is title",
        
        		StartedAt: time.Now(),
        
        	}
        
        	t1, _ := json.Marshal(d)
        
        	fmt.Println(string(t1))
        
        	t2, _ := json2.Marshal(d)
        
        	fmt.Println(string(t2))
        
        }
        
        func NewJsonTime() jsoniter.API {
        
        	var jt = jsoniter.ConfigCompatibleWithStandardLibrary
        
        	jt.RegisterExtension(&CustomTimeExtension{})
        
        	return jt
        
        }
        
        type CustomTimeExtension struct {
        
        	jsoniter.DummyExtension
        
        }
        
        func (extension *CustomTimeExtension) UpdateStructDescriptor(structDescriptor *jsoniter.StructDescriptor) {
        
        	for _, binding := range structDescriptor.Fields {
        
        		var typeErr error
        
        		var isPtr bool
        
        		name := strings.ToLower(binding.Field.Name())
        
        		if name == "startedat" {
        
        			isPtr = false
        
        		} else if name == "finishedat" {
        
        			isPtr = true
        
        		} else {
        
        			continue
        
        		}
        
        		timeFormat := time.RFC1123Z
        
        		locale, _ := time.LoadLocation("Asia/Shanghai")
        
        		binding.Encoder = &funcEncoder{fun: func(ptr unsafe.Pointer, stream *jsoniter.Stream) {
        
        			if typeErr != nil {
        
        				stream.Error = typeErr
        
        				return
        
        			}
        
        			var tp *time.Time
        
        			if isPtr {
        
        				tpp := (**time.Time)(ptr)
        
        				tp = *(tpp)
        
        			} else {
        
        				tp = (*time.Time)(ptr)
        
        			}
        
        			if tp != nil {
        
        				lt := tp.In(locale)
        
        				str := lt.Format(timeFormat)
        
        				stream.WriteString(str)
        
        			} else {
        
        				stream.Write([]byte("null"))
        
        			}
        
        		}}
        
        		binding.Decoder = &funcDecoder{fun: func(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
        
        			if typeErr != nil {
        
        				iter.Error = typeErr
        
        				return
        
        			}
        
        			str := iter.ReadString()
        
        			var t *time.Time
        
        			if str != "" {
        
        				var err error
        
        				tmp, err := time.ParseInLocation(timeFormat, str, locale)
        
        				if err != nil {
        
        					iter.Error = err
        
        					return
        
        				}
        
        				t = &tmp
        
        			} else {
        
        				t = nil
        
        			}
        
        			if isPtr {
        
        				tpp := (**time.Time)(ptr)
        
        				*tpp = t
        
        			} else {
        
        				tp := (*time.Time)(ptr)
        
        				if tp != nil && t != nil {
        
        					*tp = *t
        
        				}
        
        			}
        
        		}}
        
        	}
        
        }
        
        type funcDecoder struct {
        
        	fun jsoniter.DecoderFunc
        
        }
        
        func (decoder *funcDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
        
        	decoder.fun(ptr, iter)
        
        }
        
        type funcEncoder struct {
        
        	fun         jsoniter.EncoderFunc
        
        	isEmptyFunc func(ptr unsafe.Pointer) bool
        
        }
        
        func (encoder *funcEncoder) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) {
        
        	encoder.fun(ptr, stream)
        
        }
        
        func (encoder *funcEncoder) IsEmpty(ptr unsafe.Pointer) bool {
        
        	if encoder.isEmptyFunc == nil {
        
        		return false
        
        	}
        
        	return encoder.isEmptyFunc(ptr)
        
        }

        输出结果:

        {“title”:”this is title”,”time”:”2021-12-07T17:31:08.811045+08:00″}

        {“title”:”this is title”,”time”:”Tue, 07 Dec 2021 17:31:08 +0800″}

        这里主要是使用 “github.com/json-iterator/go” 包控制 Go 对时间字段的序列化,通过其提供的扩展指定 key 为 startedat、finishedat 的时间字段,指定序列化时使用 timeFormat := time.RFC1123Z 格式和 locale, _ := time.LoadLocation(“Asia/Shanghai”) 时区。

        3. 各种环境下设置时区

        3.1 在 Linux 中

        执行命令:

        timedatectl set-timezone Asia/Shanghai

        或者设置 TZ 环境变量:

        TZ='Asia/Shanghai'
        
        export TZ

        都可以设置时区。

        3.1 在 Docker 中

        在制作镜像时,直接在 Dockerfile 设置 TZ 变量,可能会碰到问题:

        FROM alpine
        
        ENV TZ='Asia/Shanghai'
        
        COPY ./time.go .

        报错: panic: time: missing Location in call to Time.In

        原因: 我们常用的 Linux 系统,例如 Ubuntu、CentOS,在 /usr/share/zoneinfo/ 目录下存放了各个时区而 alpine 镜像没有。

        因此 alpine 镜像需要安装一些额外的包。

        FROM alpine
        
         
        
        RUN apk add tzdata && \
        
        
            cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
        
        
            echo "Asia/Shanghai" > /etc/timezone

        在运行容器时,可以直接挂载主机的时区描述文件:

        docker run -it --rm -v /etc/localtime:/etc/localtime:ro nginx

        3.2 在 Kubernetes 中

        apiVersion: v1
        
        kind: Pod
        
        metadata:
        
          name: test
        
          namespace: default
        
        spec:
        
          restartPolicy: OnFailure
        
          containers:
        
          - name: nginx
        
            image: nginx-test
        
            imagePullPolicy: IfNotPresent
        
            volumeMounts:
        
            - name: date-config
        
              mountPath: /etc/localtime
        
            command: ["sleep", "60000"]
        
          volumes:
        
          - name: date-config
        
            hostPath:
        
              path: /etc/localtime

        这里将主机上的时区文件挂载到 Pod 中。

        4. 参考

        https://github.com/json-iterator/go 

        5.golang时区处理

        如果要设定时区,那么在使用时间函数之前,就要设定时区。

        那么问题就来了,打个比喻说。我想在墨西哥5月6号12点45分时开始促销。而我在中国,那么你要设定了个什么样的数字呢?

        墨西哥是西5时区-5,中国是+8时区,相差13个时区,也就是在中国今天是5.6号,那么墨西哥是5.5号

        也就是说,我今天要设置5.7号的时间吗?

        。。。。。。。。。。。。。

        其实我觉得,是不是直接设定5.6号就行了。因为设定了,那么墨西哥是5.6号做的促销,你只要在5.7号跟进就行了。

        如果你想要看交易数据(按照中国的时间来看),那样才要做转换。也就是中国时间5.7号,墨西哥卖出了多少货。

        好了,不扯蛋了。下面是有需要转时区的写法。

               var cstZone = time.FixedZone("CST", -7*3600) //设定要转换的时区<br>                            <br>                h,:=time.ParseDuration("-1h") //中国的时间是+8区
        
        // element
        
        t,err:=time.Parse("2006-01-02 15:04:05",item.SaleStartTime)//要处理的时间格式,使用入的字符串要跟格式化的一致
        
        var tString string
        
        if err!=nil{
        
               tString=time.Now().In(cstZone).Format("2006-01-02T15:04:05-0700") // 这时有个坑,不需要的自己想加法解决
        
        }else{<br>                       t=t.Add(8*h) //要减去+8区
        
               tString=t.In(cstZone).Format("2006-01-02T15:04:05-0700") // 使用时区转化为对应国家的时间。小心格式化的时间,填自己想要的格式。
        
        }

        总结

        声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。