Go中时间与时区问题的深入讲解

目录
  • 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") // 使用时区转化为对应国家的时间。小心格式化的时间,填自己想要的格式。

}

总结

到此这篇关于Go中时间与时区问题的文章就介绍到这了,更多相关Go时间与时区问题内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • golang时间、时区、格式的使用方法

    前几天,因为需要实现海外服务端定时停机,涉及到时区的概念.网上搜索了一下,大部分都是谈time.Format中的Layout,非常不成体系,这里就简单总结一下其中的时间初始化.时区转化及格式转换. 开发中,我们对时间的使用是比较多的,其应用场景,按照使用概率,从大到小,通常是: 获取当前或数据库中存储的时间 比较两个时间点的先后 显示打印时间 时区转换 对应到go,也就是几个基本定义: 时间点与时间段:Time,Duration.好比MVC中的M. 时 区:Location,在时间转换上,好比是

  • Go中时间与时区问题的深入讲解

    目录 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,格林威治时间,是

  • iOS中时间与时间戳的相互转化实例代码

    本人搜索了很多关于iOS中时间与时间戳的相互转化的资料,下面我来记录一下,有需要了解iOS中时间与时间戳的相互转化的朋友可参考.希望此文章对各位有所帮助. //获取当前系统时间的时间戳 #pragma mark - 获取当前时间的 时间戳 +(NSInteger)getNowTimestamp{ NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; [formatter setDateStyle:NSDateFormatterM

  • C++ 中时间与时间戳的转换实例详解

    C++ 中时间与时间戳的转换实例详解 // 设置时间显示格式: NSString *timeStr = @"2011-01-26 17:40:50"; NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; [formatter setDateStyle:NSDateFormatterMediumStyle]; [formatter setTimeStyle:NSDateFormatterShortStyle]; [fo

  • js中时间格式化的几种方法

    项目中时间返回值,很过时候为毫秒值,我们需要转换成 能够看懂的时间的格式: 例如: yyyy-MM-dd HH:mm:ss 2.处理方法(处理方法有多种,可以传值到前端处理,也可以后台可以好之后再传递到页面) 方法一:实体类中添加时间转换注解(注意时区问题) /** * 开始时间 */ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone="GMT+8") private Date startTime; 方法二:

  • python中时间、日期、时间戳的转换的实现方法

    1.简介 在编写代码时,往往涉及时间.日期.时间戳的相互转换. 2.示例 # 引入模块 import time, datetime 2.1 str类型的日期转换为时间戳 # 字符类型的时间 tss1 = '2013-10-10 23:40:00' # 转为时间数组 timeArray = time.strptime(tss1, "%Y-%m-%d %H:%M:%S") print timeArray # timeArray可以调用tm_year等 print timeArray.tm_

  • 详解Django 时间与时区设置问题

    再写入数据库对时间进行加减操作时候 django报告了错误 TypeError: can't subtract offset-naive and offset-aware datetimes 修改setting.py 文件 在Django的配置文件settings.py中,有两个配置参数是跟时间与时区有关的,分别是TIME_ZONE和USE_TZ 如果USE_TZ设置为True时,Django会使用系统默认设置的时区,即America/Chicago, 此时的TIME_ZONE不管有没有设置都不

  • 详解Django 中是否使用时区的区别

    起步 在 Django 的模型中新加了一个日期的字段: import datetime class Instance(models.Model): ... start_time = models.DateTimeField(default=datetime.datetime.now) 同步到数据库时 Django 报出了一个警告: django/db/models/fields/__init__.py:1423: RuntimeWarning: DateTimeField Instance.st

  • SpringBoot中时间格式化的五种方法汇总

    目录 前言 时间问题演示 1.前端时间格式化 JS 版时间格式化 2.SimpleDateFormat格式化 3.DateTimeFormatter格式化 4.全局时间格式化 实现原理分析 5.部分时间格式化 总结 参考 & 鸣谢 前言 在我们日常工作中,时间格式化是一件经常遇到的事儿,所以本文我们就来盘点一下 Spring Boot 中时间格式化的几种方法. 时间问题演示 为了方便演示,我写了一个简单 Spring Boot 项目,其中数据库中包含了一张 userinfo 表,它的组成结构和数

  • 浅谈Python3中datetime不同时区转换介绍与踩坑

    最近的项目需要根据用户所属时区制定一些特定策略,学习.应用了若干python3的时区转换相关知识,这里整理一部分记录下来. 下面涉及的几个概念及知识点: GMT时间:Greenwich Mean Time, 格林尼治平均时间 UTC时间:Universal Time Coordinated 世界协调时,可以认为是更精准的GMT时间,但两者误差极小,在1s以内,一般可视为等同 LMT:Local Mean Time, 当地标准时间 Python中的北京时间:Python的标准timezone中信息

  • 详解Java关于JDK中时间日期的API

    目录 JDK 8中关于日期和时间的API测试 JDK 8 之前日期和时间的API测试 //1.System类中的currentTimeMillis() public void test1(){ long time = System.currentTimeMillis(); //返回当前时间与1970年1月1日0时0分0秒之间以毫秒为时间为单位的时间差. //称为时间戳 System.out.println(time);//1628416744054 } /* java.util.Date类 |-

随机推荐