new Date(1640966400000)
new Date(1640966400000)
从这两个例子我们可以看到,
当时间字符串相同时,在不同的地区获得的时间戳是不同的
当时间戳相同时,在不同的地区获取的时间字符串是不同的
看到这里,你似乎已经对时间、时区、时间字符串有了一个初步的印象和理解。
关于时间和时区
时间是什么?
这是个很深刻的问题了,关于时间的定义,很复杂。但是简单来说,指的是某一瞬间。
时间戳、UTC、GMT这些都是什么?
看完这篇文章 日期与时间(UTC、GMT、时间戳、时区) 相信你会得到答案。
但归根在本质上,它们就是时间,是时间在计算机上的记录。
时间戳需要理解时区概念吗?
时间戳是不需要考虑时区概念的。
为什么这样说,我们假设 1657535893504(东八区 2022-07-11 18:39:22) 这个时间戳,无论在哪个国家哪个地区,它都代表着某一个时刻时间,它是时间本身的记录。
那时间字符串有时区概念呢?
当然有,因为在不同的时区下,同一个时间字符串代表的是不同的时间本身。 时间字符串只是当地时间的一个表达。
时间字符串和 UTC 、 GMT有什么区别,不都是字符串吗?
很好,盲点你已经发现了华生。 '2022-01-01 00:00:00' 和 'Sat Jan 01 2022 00:00:00 GMT+0800 (中国标准时间)' 虽然同样都是字符串,但是前者没有时区,所以在不同的时区下,它代表着不同的时间,后者带上了时区,它永久地指向了一个特定的时间。
如何理解dayjs对象?
我们应该知道dayjs对象本质上就是时间,在理解 时区、本地字符串、时间的关系和转换时,我们需要把dayjs对象当成时间当成时间戳去处理。
总结: 我们可以看到,时间字符串需要考虑时区,是因为它本身不携带时区信息,它在不同的时区时指向的是不同的时间;UTC、时间戳、GMT本身就代表着时间,所以说在处理它们的时候,我们不需要考虑时区的问题。
Dayjs的使用
从上面我们知道, 时间字符串 + 时区 = 时间。 那我们再来了解一下dayjs的api
dayjs()
这是最基础的一个api。调用时,时区都是机器时区,无论你是否设置了tz的默认时区。
从下面可以看到,当我们传入一个普通的时间字符串时,它是指我们本地的2022-01-01
dayjs().tz() // tz(timezone?: string, keepLocalTime?: boolean): Dayjs
interface Dayjs {
tz(timezone?: string, keepLocalTime?: boolean): Dayjs
可以注意到,此时的tz是dayjs实例上的共有函数,它的作用是转换时区。记住我们上面的等式, 时间字符串 + 时区 = 时间。所以,时区改变了,要么时间字符串改变,要么时间改变,这里是通过第二个参数去控制的,不传第二个函数默认是false。
keepLocalTime为false, 时间不变,时间字符串改变了
keepLocalTime 为 true , 时间字符串没变,时间变了
interface DayjsTimezone {
(date: ConfigType, timezone?: string): Dayjs
(date: ConfigType, format: string, timezone?: string): Dayjs
const tz: DayjsTimezone
首先和第二个函数不同,它是挂载在dayjs上的静态函数,它的作用和第一个函数 dayjs() 类似,都是用来构造dayjs对象的,但是不同的是,你可以指定你的目标时区,如果第二个参数没传,会使用tz.setDefault指定的时区
仍然是那个等式, 时间字符串 + 时区 = 时间,所以如果第一个参数传入了时间字符串,那么就是 时间字符串 + 指定时区, 如果传入的是时间,则format的时候是结果会不同
传入时间字符串
传入dayjs
在上面我们说过,dayjs对象需要当成时间来理解,所以传入dayjs 等同于 传入时间。那么当我们需要保持时间字符串不变时,那我们就需要先format一下。
可能有的同学已经发现了,既然传入的是dayjs,为啥不直接调用第二个例子, tz这个实例方法呢? 这里有两个理由:
如果是时间选择器出来的时间,这个dayjs可能是没有tz这个实例方法的,因为没有继承过timezone
dayjs.extend(utc)
dayjs.extend(timezone)
通常来说,都是用dayjs.tz.setDefault 设置一个默认的目标时区,而tz这个实例方法,又不能绕开第一个参数去设置第二个参数
Dayjs VS Moment
Dayjs和moment都支持处理时区,且API相似,但他们有着一个区别,
那就是 moment在设置时区后,不再保有原有时区,而dayjs仅影响.tz的对象
关于这个区别,可以看这个issue github.com/iamkun/dayj…
具体的业务场景
时间选择器组件onValueChange出来的的时间要怎么理解?
首先我们可以理解,时间选择器组件上假设选择了 2022-01-01, 那它其实背后是我们机器时区下的 2022-01-01。所以第一个,我们是需要将它转换至目标时区,其实是我们的场景A, 转换的过程中是需要改变时间戳。
时间选择器组件的disabled出来的时间要怎么理解?
参考上面的例子,我们知道时间选择器出来的都是机器时区下需要做一次转换。 因为在实际的业务过程中,例如说禁用今天,这个所谓的今天,是我们目标时区下的今天,而不是我们机器时区下的今天,所以同理需要做一次转换。
后端传回来的时间要怎么理解?
通常来说,后端传回来的都会是时间戳、 UTC、GMT等时间的一种表达方式,在上面我们说了,这些都是和时区无关的,就是时间本质。 要怎么理解这个时间本质呢,通俗的讲,你需要的是ID 2022-01-01 零点,它是一个准确的时间,那么后端返回的时间就精准地指向了这一刻。
dayjs.tz(timestamp)
要传给后端的时间要怎么理解
参考上面的例子,要传给后端的也会是一个准确的时间。
date.format('YYYY-MM-DD')
// or
date.valueOf()
单纯的时间处理函数怎么理解和处理呢?
对于这种函数,我们应该直接限制它的入参为准确的时间,鉴于ISO、GMT等均为字符串,可以直接限制为时间戳,或者是Dayjs类型,尽量避免参数可以为string,这会很容易造成混淆。
type processTime = (d: Dayjs | number, region: Region) => void
如果说参数确实需要有一个是字符串,我们需要一个额外的参数,去标识这个时间的时区,通常来说,是标识它到底是机器时区还是目标时区的时间
type processTime = (d: string, region: Region, isLocal: boolean) => void
if (isLocal) {
const date = dayjs(d).tz();
} else {
const date = dayjs.tz(d);