时间、时区详解
来源参考:
- 对RFC 3339的时间、时区格式详解(2022-01-01T07:00:00.52Z):https://www.jianshu.com/p/f50005a2410c
- 互联网上的日期和时间:https://zhuanlan.zhihu.com/p/31829454
- MDN/Date/cn:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Date
- MDN/Date/us:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date
- 计算机从哪里获取时间:https://dotat.at/@/2023-05-26-whence-time.html
一、RFC 3339/Date ISO 8601
国际标准组织(ISO)制定了ISO 8601,我们的中华人民共和国国家标准GB/T 7408-2005《数据元和交换格式 信息交换 日期和时间表示法》与 ISO 8601:2000
等效采用。
上面两个标准,对开发者而言,太过于严肃。其实有一份更加简单易读的文件 RFC3339 Date and Time on the Internet: Timestamps。
本文之后的讨论,都基于 RFC3339
。
RFC3339
和 ISO 8601
差异
- RFC 3339遵循ISO 8601 DateTime格式
ISO
允许24
点,而RFC 3339
为了减少混淆,限制小时必须在0至23之间。23:59
过1
分钟,是第二天的00:00
。ISO
必须使用T
,RFC 3399
可以使用空格代替T
# This is acceptable in ISO 8601 and RFC 3339(with T)
2019-10-12T07:20:50.52Z
# This is only accepted in RFC 3339(without T)
2019-10-12 07:20:50.52Z
阅读 RFC3339
,我主要明确下面三个定义:
- 时间戳
- 本地时间
- 标准时间
1.1、时间戳
概述
时间戳是一个**数字**,定义为格林威治时间 1970年01月01日00时00分00秒
(北京时间1970年01月01日08时00分00秒
)起至现在的总秒数。<br />
注意,同一时刻,不同时区获得的时间戳是相同的。<br />
以前很多用来记录时间的字段,在数据库中往往不会存储为 Datetime
类型,而是直接存储为无符号整形,存放时间戳的值。
使用 javascript
获取当前时间的时间戳
// 获取当前时间的时间戳
new Date().getTime(); // 1682485305804
精度
说明:13位位毫秒级别;10位为秒级别
JavaScript
时间戳的精度:毫秒,共13位。new Date().getTime()
JAVA
时间戳的精度:毫秒,共13位。System.out.println(System.currentTimeMillis())
Python
默认float
类型,通过转换可以得到13位。<br />time.time()``<br />int(round(time.time() * 1000))
- PHP默认10位,秒级
时间戳-特性应用
不重复、签名、文件命名
不同时区的简称
**GMT:**Greenwich Mean Time<br />
格林威治标准时间:英国伦敦格林威治定为0°经线开始的地方,地球每15°经度 被分为一个时区,共分为24个时区,相邻时区相差一小时;例: 中国北京位于东八区,GMT时间比北京时间慢8小时。**UTC**: Coordinated Universal Time
**世界协调时间;经严谨计算得到的时间,精确到秒,误差在0.9s以内, 是比GMT更为精确的世界时间。<br />
UTC就是0时区的时间**DST**: Daylight Saving Time<br />
夏季节约时间,即夏令时;是为了利用夏天充足的光照而将时间调早一个小时,北美、欧洲的许多国家实行夏令时;- **CST
<br />
**四个不同时区的缩写:- Central Standard Time (USA) UT-6:00 美国标准时间
- Central Standard Time (Australia) UT+9:30 澳大利亚标准时间
- China Standard Time UT+8:00 中国标准时间
- Cuba Standard Time UT-4:00 古巴标准时间
1.2、本地时间
当前时区的本地时间
new Date().toLocaleString(); // '2023/4/26 13:09:37'
1.3、标准时间
本地时间只包括当前的时间,不包含任何时区信息。<br />
同一时刻,东八区的本地时间比零时区的本地时间快了8个小时。<br />
在不同时区之间交换时间数据,除了**用纯数字的时间戳**,还有一种更方便人类阅读的表示方式:标准时间的偏移量表示方法。
RFC3339
详细定义了互联网上日期/时间的偏移量表示:
2017-12-08T00:00:00.00Z
# 这个代表了UTC时间的2017年12月08日零时
2017-12-08T08:00:00.00+08:00
# 这个代表了同一时刻的,东八区北京时间(CST)表示的方法
上面两个时间的时间戳是等价的。<br />
两个的区别:在本地时间后面增加了时区信息。<br />
Z表示零时区。+08:00表示UTC时间增加8小时。
这种表示方式容易让人疑惑的点是从标准时间换算UTC时间。以CST转换UTC为例,没有看文档的情况下,根据 +08:00 的结尾,很容易根据直觉在本地时间再加上8小时。正确的计算方法是本地时间减去多增加的8小时。+08:00
减去 8
小时才是 UTC
时间,-08:00
加上8小时才是 UTC
时间。
二、开发相关
2.1、客户端&服务端时间选择
- 服务端入库时间以时间戳为标准时间入库
- 前端根据业务所在时区以及业务对时间的要求,对行时间格式进行本地化转换
2.2、服务器时区设置
Docker 设置时区(使用北京时间)
很多Docker镜像都是UTC时区,在上海使用镜像启用容器,容器里面获取的本地时间都会慢8个小时。可以在Dockerfile里面设置时区。参考这个问题的回答
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
Ubuntu设置时区(使用北京时间)
Docker里面大部分镜像是基于Debian的,Docker这么解决,Ubuntu上思路类似,直接执行命令就好了。
TZ=Asia/Shanghai
ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
可以执行 date
命令查看时间,CST是中国标准时间。
date
# Fri Apri 26 16:00:50 CST 2023
2.3、javascript常用时间插件推荐
2.3.1、Day.js
官网:https://dayjs.gitee.io/zh-CN/
2.3.2、moment.js
github:https://github.com/moment/moment/
2.3.3、date-fns(推荐/模块函数)
官网:https://date-fns.org/docs/Getting-Started/<br />
github:https://github.com/date-fns/date-fns<br />
注意:locale的使用和引入
// 引入通用方法
import { parseISO, format, formatDistance, setDefaultOptions } from 'date-fns'
// 推荐:引入中文语言包,该方法可正常构建
import zhCN from 'date-fns/locale/zh-CN/index';
// 不推荐: 该方法使用vite构建时,可能导致无法识别es模块
import { zhCN } from 'date-fns/locale/';
// 推荐:设置全局默认语言包
setDefaultOptions({locale: zhCN})
// 格式化指定时间字符
format(parseISO('2023-10-24'), 'yyyy-MM-dd')
2.4、nodeJS
中 new Date()
的注意项
v6
之后的版本new Date()
相当于同调用了toISOString()方法,即new Date() 使用的是UTC 0时区
时间
2.5、javascript: new Date(dateString)
时间戳问题
前置条件:本地时区为北京时间
new Date('2023-01-17').getTime()
new Date('2023-01-17').getTime()
// 1、返回值:1673913600000
// 2、识别为标准格式:"2023-01-17"转为"2023-01-17T00:00:00Z"为UTC时间
// 3、使用GMT格式显示为本地时区时间
// 4、控制台输出GMT时间:Tue Jan 17 2023 08:00:00 GMT+0800 (中国标准时间)
// 4、返回的时间戳是指2023年1月17日00:00:00的本地时刻(北京时间)
new Date('2023-01-17 00:00:00).getTime()
new Date('2023-01-17 00:00:00').getTime()
// 1、返回值:1673884800000
// 2、识别为非标准格式:将"2023-01-17 00:00:00" 识别为本地时间
// 3、使用本地时区进行计算并转换为UTC时间"2023-01-16T16:00:00Z"
// 4、使用GMT格式显示为本地时区时间
// 5、控制台输出GMT时间:Tue Jan 17 2023 00:00:00 GMT+0800 (中国标准时间)
// 6、获取的时间戳为指定时间的时间戳,没有做本地时区转换
实例分析
- 以上两个例子使用的是new Date(dateString) 规范,如参数的
dateString
含有"空格",可能被识别转换为"RFC 3339/tc39
"要求的标准格式 - 当使用时区相关方法时,例如:
getTime()
,toISOString()
,toLocaleString()
,getUTCDate()
- 如
dateString
可识别为标准格式
,则直接使用。<br />
例如:2023-01-07
将转为标准格式2023-01-07T00:00:00Z
- 如
dateString
不可转转为标准格式,则将dateString识别为本地时区的时间,并使用本地时区为基础,转为UTC时间后,再进行计算。<br />
例如:2023-01-07 00:00:00
为非标准格式,本地时区为北京时间,转为UTC时间后,2023-01-16T16:00:00Z
- 如
- 当使用时区无关的方法时,例如:
getFullYear()
,getDate()
,getMonth()
,getDate()
- 如
new Date('2023-01-17').getDate()
和new Date('2023-01-17 00:00:00').getDate()
得到的值都为17
- 如
三、常用时间单位及换算关系
1秒(s) =1000毫秒(ms)<br />
1毫秒(ms)=1000微秒 (us)<br />
1微秒(us)=1000纳秒 (ns)<br />
1纳秒(ns)=1000皮秒 (ps)
皮秒,符号ps(英语:picosecond )
<br />
1皮秒等于一万亿分之一秒<br />
1,000 皮秒 = 1纳秒<br />
1,000,000 皮秒 = 1微秒<br />
1,000,000,000 皮秒 = 1毫秒<br />
1,000,000,000,000 皮秒 = 1秒
纳秒,符号ns(英语:nanosecond )
1纳秒等于十亿分之一秒<br />
1 纳秒 = 1000皮秒<br />
1,000 纳秒 = 1微秒<br />
1,000,000 纳秒 = 1毫秒<br />
1,000,000,000 纳秒 = 1秒
微秒,符号μs(英语:microsecond )
1微秒等于一百万分之一秒<br />
0.000 001 微秒 = 1皮秒<br />
0.001 微秒 = 1纳秒<br />
1,000 微秒 = 1毫秒<br />
1,000,000 微秒 = 1秒
毫秒,符号ms(英语:millisecond )
1毫秒等于一千分之一秒<br />
0.000 000 001 毫秒 = 1皮秒<br />
0.000 001 毫秒 = 1纳秒<br />
0.001 毫秒 = 1微秒<br />
1000 毫秒 = 1秒
四、其他
- 国际航班机票上的[出发/到达]时间,使用的时区为对应[出发地/目的地]的时区,如需计算航班行程,需使用同一时区进行计算