本文可能比较水,不再介绍dayjs一些基础的用法,各位巨佬们肯定都用得很6了,本文只是基于本人在工作中的一些关于时区的经验和困惑,特此留念,不喜勿喷。
首先摆明一个概念,时间戳没有时区,全世界在某一个时刻的时间戳都是一样的,这个观点应该没人反对吧。
先上第一张图,显示世界上一些城市的当前时间,参照时间为北京时间:2025-12-12 12:11:38
以下内容是关于dayjs时区的一些用法、经验、困惑,欢迎各位解惑
// 虽然默认时区是Asia/Bangkok,但是这里还是会显示Asia/Shanghai的时区时间
console.log(dayjs().format('YYYY-MM-DD HH:mm:ss')) // 2025-12-09 16:02:37
// 只有设置了时区,才会根据当前时区显示对应的时间(tz没有传对应的时区,但是设置过默认时区,所以会显示默认时区的时间)
console.log(dayjs().tz().format('YYYY-MM-DD HH:mm:ss')) // 2025-12-09 15:02:37
// dayjs.tz(undefined, true) 当传递第二个参数为 true 时,只更新时区 (和偏移量),本地时间将保持不变。所以还是显示本地时间
console.log(dayjs().tz(undefined, true).format('YYYY-MM-DD HH:mm:ss')) // 2025-12-09 16:03:42
// dayjs.tz('Asia/Tokyo', true) 当传递第二个参数为 true 时,虽然传了时区,但是只会更新时区 (和偏移量),本地时间将保持不变。所以还是显示本地时间
console.log(dayjs().tz('Asia/Tokyo', true).format('YYYY-MM-DD HH:mm:ss')) // 2025-12-09 16:03:42
// dayjs().tz(undefined)第一个参数传undefined代表使用默认时区
console.log(dayjs().tz(undefined).format('YYYY-MM-DD HH:mm:ss')) // 2025-12-09 15:07:35
// dayjs.tz('Asia/Tokyo', true) 当传递第二个参数为 true 时,虽然传了时区,但是只会更新时区 (和偏移量),本地时间将保持不变。所以还是显示本地时间
console.log(dayjs().tz('Asia/Shanghai', true).format('YYYY-MM-DD HH:mm:ss')) // 2025-12-09 16:30:48
// dayjs.tz('Asia/Tokyo') 当传递第二个参数不传时,会显示当前时区时间
console.log(dayjs().tz('Asia/Tokyo').format('YYYY-MM-DD HH:mm:ss')) // 2025-12-09 17:30:48
// 特定时间,没有设置时区,使用本地时间
console.log(dayjs('2025-08-27').format('YYYY-MM-DD HH:mm:ss')) // 2025-08-27 00:00:00
// 特定时间,设置时区,使用时区时间
console.log(dayjs('2025-08-27').tz().format('YYYY-MM-DD HH:mm:ss')) // 2025-08-26 23:00:00
// 特定时间,设置时区,使用时区时间
console.log(
dayjs('2025-08-27').tz('Asia/Tokyo').format('YYYY-MM-DD HH:mm:ss'),
) // 2025-08-27 01:00:00
// 不管什么时区,返回的时间戳都一样,时间戳没有时区的概念
console.log(dayjs().valueOf()) // 1765269971719
// 不管什么时区,返回的时间戳都一样,时间戳没有时区的概念
console.log(dayjs().tz().valueOf()) // 1765269971719
// 不管什么时区,返回的时间戳都一样,时间戳没有时区的概念
console.log(dayjs().tz('Asia/Tokyo').valueOf()) // 1765269971719
// 但是获取开始时间,会根据时区进行计算(这点暂时没想通为什么?求大佬们指点)
console.log(dayjs().startOf('d').valueOf()) // 1765209600000
console.log(dayjs().tz().startOf('d').valueOf()) // 1765213200000
console.log(dayjs().tz('Asia/Tokyo').startOf('d').valueOf()) // 1765206000000
// 一个特定的时间,不管什么时区时间戳都是一样的,再次证明时间戳没有时区的概念
console.log(dayjs('2025-08-27 10:00:00').valueOf()) // 1756260000000
console.log(dayjs('2025-08-27 10:00:00').tz().valueOf()) // 1756260000000
console.log(dayjs('2025-08-27 10:00:00').tz('Asia/Tokyo').valueOf()) // 1756260000000
// 但是获取开始时间,同样会根据时区进行计算
console.log(dayjs('2025-08-27 00:00:00').startOf('d').valueOf()) // 1756224000000
console.log(dayjs('2025-08-27 00:00:00').tz().startOf('d').valueOf()) // 1756141200000
console.log(
dayjs('2025-08-27 00:00:00').tz('Asia/Tokyo').startOf('d').valueOf(),
) // 1756220400000
// dayjs.tz()是dayjs 的静态方法,第一个参数是时间,第二个参数是时区,返回一个dayjs对象,这个对象调用时创建指定时区的时间,需要传入时间和时区作为参数,主要用于直接创建特定时区的时间对象
// dayjs.tz的第一个参数不传,代表当前时间,全世界当前时间的时间戳都一样
console.log(dayjs.tz().valueOf()) // 1765276466531
console.log(dayjs.tz(undefined, 'Asia/Shanghai').valueOf()) // 1765276466531
console.log(dayjs.tz(undefined, 'Asia/Tokyo').valueOf()) // 1765276466531
// 同样是第一个参数不传,设置不同的时区,但是加上了获取开始时间,获取到的时间戳就不一样了。(想了一下,不同时区的0点0分0秒时, 时间戳是不一样的,大概是这个原因吧)
console.log(dayjs.tz().startOf('d').valueOf()) // 1765213200000
console.log(dayjs.tz(undefined, 'Asia/Shanghai').startOf('d').valueOf()) // 1765209600000
console.log(dayjs.tz(undefined, 'Asia/Tokyo').startOf('d').valueOf()) // 1765206000000
// 特定时间格式化,不带时区,显示本地时间
console.log(dayjs('2025-08-27').format('YYYY-MM-DD HH:mm:ss')) // 2025-08-27 00:00:00
// 特定时间格式化,当传递第二个参数为 true 时,虽然传了时区,但是只会更新时区 (和偏移量),本地时间将保持不变。所以还是显示本地时间
console.log(
dayjs('2025-08-27').tz(undefined, true).format('YYYY-MM-DD HH:mm:ss'),
) // 2025-08-27 00:00:00
// 特定时间格式化, 当传递第二个参数为 true 时,虽然传了时区,但是只会更新时区 (和偏移量),本地时间将保持不变。所以还是显示本地时间
console.log(
dayjs('2025-08-27').tz('Asia/Tokyo', true).format('YYYY-MM-DD HH:mm:ss'),
) // 2025-08-27 00:00:00
// 特定时间格式化, 当传递第二个参数不传时,会显示当前时区时间
console.log(
dayjs('2025-08-27').tz('Asia/Tokyo').format('YYYY-MM-DD HH:mm:ss'),
) // 2025-08-27 10:00:00
// 注意:一个特定的时间,tz传了时区,
// 显示本地时间戳
console.log(dayjs('2025-08-27').valueOf()) // 1756224000000
// 第二个参数是true, 会更新时区 (和偏移量), 显示时区时间戳, 得出来这个时间戳是指当前时区是2025-08-27 00:00:00时的时间戳,因为默认时区是Asia/Bangkok比本地时区晚一个小时,所以当Asia/Bangkok时区是2025-08-27 00:00:00时,时间戳要比本地时区时间戳多了1小时
console.log(dayjs('2025-08-27').tz(undefined, true).valueOf()) // 1756227600000
// 第二个参数是true, 会更新时区 (和偏移量), 显示时区时间戳, 得出来这个时间戳是指当前时区是2025-08-27 00:00:00时的时间戳,因为默认时区是Asia/Bangkok比本地时区晚一个小时,所以当Asia/Bangkok时区是2025-08-27 00:00:00时,时间戳要比本地时区时间戳多了1小时
console.log(dayjs('2025-08-27').tz('Asia/Tokyo', true).valueOf()) // 1756220400000
// 第二个参数是false, 不会更新时区 (和偏移量), 显示本地时间戳
console.log(dayjs('2025-08-27').tz(undefined).valueOf()) // 1756224000000
// 第二个参数是false, 不会更新时区 (和偏移量), 显示本地时间戳
console.log(dayjs('2025-08-27').tz('Asia/Tokyo').valueOf()) // 1756224000000
// 显示本地时间戳
console.log(dayjs().valueOf()) // 1765508371883
// 第二个参数是true, 会更新时区 (和偏移量), 显示时区时间戳
console.log(dayjs().tz(undefined, true).valueOf()) // 1765511971884
// 第二个参数是false, 不会更新时区 (和偏移量), 显示本地时间戳
console.log(dayjs().tz().valueOf()) // 1765508371884
// 获取今天0点时,当前时区应该的时间戳,因为是一个特定时间,所以不同时区到今天的0点,时间戳应该不同
console.log(dayjs.tz(undefined, 'Asia/Tokyo').startOf('d').valueOf()) // 1765465200000
console.log(dayjs().tz('Asia/Tokyo').startOf('d').valueOf()) // 1765465200000
console.log(dayjs().tz('Asia/Tokyo', true).startOf('d').valueOf()) // 1765465200000
console.log(dayjs.tz(undefined).startOf('d').valueOf()) // 1765472400000
console.log(dayjs().tz().startOf('d').valueOf()) // 1765472400000
console.log(dayjs().tz(undefined, true).startOf('d').valueOf()) // 1765472400000
console.log(dayjs.tz(undefined, 'Asia/Shanghai').startOf('d').valueOf()) // 1765468800000
console.log(dayjs().tz('Asia/Shanghai').startOf('d').valueOf()) // 1765468800000
console.log(dayjs().tz('Asia/Shanghai', true).startOf('d').valueOf()) // 1765468800000
// 获取本地时间和utc时间的时间差
console.log(dayjs().utcOffset()) // 480
// 获取当前时区和utc时间的时间差
console.log(dayjs().tz().utcOffset()) // 420
// 获取当前时区和utc时间的时间差
console.log(dayjs().tz('Asia/Tokyo').utcOffset()) // 540
console.log(dayjs().tz('Asia/Tokyo', true).utcOffset()) // 540
// 获取时间戳在当前时区下的时间
console.log(dayjs(1756369695752).tz().format('YYYY-MM-DD HH:mm:ss')) // 2025-08-28 15:28:15
// 获取时间戳的本地时间
console.log(dayjs(1756369695752).format('YYYY-MM-DD HH:mm:ss')) // 2025-08-28 16:28:15
// 特定时间格式化为本地时间
console.log(dayjs('2025-08-27').format('YYYY-MM-DD HH:mm:ss')) // 2025-08-27 00:00:00
// 特定时间格式化为时区时间,因为时间定了,所以在该时区下还是这个时间
console.log(
dayjs.tz('2025-08-27', 'Asia/Bangkok').format('YYYY-MM-DD HH:mm:ss'), // 2025-08-27 00:00:00
)
// dayjs(xxxx)和dayjs.tz(xxx, xxx)代表的意义是不一样的,dayjs(xxxx)时间已经定好了,不同时区在这个时间下还是这个时间,而dayjs.tz(xxx, xxx)时间没有定,代表某个时间在某个时区下,所以不同时区下时间会不同
// 特定时间,显示本地时间戳
console.log(dayjs('2025-08-27').valueOf()) // 1756224000000
// 显示这个时间在该时区下的时间戳
console.log(dayjs.tz('2025-08-27', 'Asia/Bangkok').valueOf()) // 1756227600000
// 特定时间,时间已经定好了,所有时区下都是这个时间,时间戳一样
console.log(dayjs('2025-08-27').tz('Asia/Bangkok').valueOf()) // 1756224000000
// 特定时间,时间已经定好了,所有时区下都是这个时间,时间戳一样
console.log(dayjs('2025-08-27').tz('Asia/Tokyo').valueOf()) // 1756224000000
// 这个得出的时间是2025-08-26 01:00:00,暂时有点没理解为什么 求大佬们指点
console.log(dayjs('2025-08-27').tz('Asia/Bangkok').startOf('d').valueOf()) // 1756141200000
// 该时间戳的本地时间
console.log(dayjs(1756369695752).format('YYYY-MM-DD HH:mm:ss')) // 2025-08-28 16:28:15
// 该时间戳的时区时间
console.log(dayjs.tz(1756369695752).format('YYYY-MM-DD HH:mm:ss')) // 2025-08-28 15:28:15
// 该时间戳的时区时间
console.log(dayjs(1756369695752).tz().format('YYYY-MM-DD HH:mm:ss')) // 2025-08-28 15:28:15
// 获取utc时间,传入 true 将只改变 UTC 模式而不改变本地时间
console.log(dayjs().utc(true).format('YYYY-MM-DD HH:mm:ss')) // 2025-12-12 12:01:05
// 获取utc时间,传入 true 将只改变 UTC 模式而不改变本地时间,但是这里转换了时区,所以显示该时区的本地时间
console.log(dayjs().tz().utc(true).format('YYYY-MM-DD HH:mm:ss')) // 2025-12-12 11:02:02
// 获取utc时间(标准时间)
console.log(dayjs().utc().format('YYYY-MM-DD HH:mm:ss')) // 2025-12-12 04:01:23
// 获取当前时区
console.log(dayjs.tz.guess()) // Asia/Shanghai
下面是两个基于antd DatePicker关于时间选择的自定义组件
MyDatePicker
import { useControllableValue } from 'ahooks'
import { DatePicker, DatePickerProps } from 'antd'
import dayjs from 'dayjs'
import { useMemo } from 'react'
const defaultShortcuts = [
{
label: '今天',
value: dayjs(),
},
{
label: '昨天',
value: dayjs().subtract(1, 'day'),
},
{
label: '三天前',
value: dayjs().subtract(3, 'days'),
},
{
label: '一周前',
value: dayjs().subtract(1, 'week'),
},
{
label: '15天前',
value: dayjs().subtract(15, 'days'),
},
{
label: '一个月前',
value: dayjs().subtract(1, 'month'),
},
]
// 时间选择器组件的属性
export interface MyDatePickerProps extends DatePickerProps {
shortcuts?: number[] // 快捷选项
shortcutsMap?: Record<number, string> // 快捷选项的映射
shortcutsRender?: (shortcuts?: number[]) => DatePickerProps['presets']
showPresets?: boolean // 是否显示快捷选项
}
// 非受控/受控时间选择器组件
export default function MyDatePicker(props: MyDatePickerProps) {
const {
shortcuts,
shortcutsMap,
showPresets = true,
shortcutsRender,
...rests
} = props
const [values, setValues] =
useControllableValue<DatePickerProps['value']>(props)
const presets = useMemo(() => {
if (!showPresets) return undefined
if (shortcutsRender && shortcuts?.length) {
return shortcutsRender(shortcuts)
}
if (shortcuts?.length) {
return shortcuts.map((shortcut) => {
return {
label: shortcutsMap?.[shortcut] || `近${shortcut}天`,
value: dayjs().subtract(shortcut, 'days'),
}
})
}
return defaultShortcuts
}, [shortcuts, shortcutsMap, showPresets, shortcutsRender])
return (
<DatePicker
presets={presets}
{...rests}
value={values}
onChange={setValues}
/>
)
}
MyTzDatePicker
import { useControllableValue } from 'ahooks'
import { DatePickerProps } from 'antd'
import dayjs from 'dayjs'
import { useCallback, useEffect, useMemo } from 'react'
import MyDatePicker, { MyDatePickerProps } from '../MyDatePicker/Index'
// 带时区时间选择器组件的属性
export interface MyTzDatePickerProps extends MyDatePickerProps {
tzDiff: number // 时区差秒数
}
// 非受控/受控时间带时区选择器组件
export default function MyTzDatePicker(props: MyTzDatePickerProps) {
const { tzDiff = 0, defaultValue, ...rests } = props
const [values, setValues] =
useControllableValue<DatePickerProps['value']>(props)
const valuesWithTz = useMemo(() => {
if (!values) return values
return dayjs(values).subtract(tzDiff, 'seconds')
}, [values, tzDiff])
const setValuesWithTz = useCallback(() => {
return (value: DatePickerProps['value']) => {
if (!value) return setValues(value)
setValues(dayjs(value).add(tzDiff, 'seconds'))
}
}, [setValues, tzDiff])
useEffect(() => {
if (defaultValue) {
setValues(dayjs(defaultValue).add(tzDiff, 'seconds'))
}
}, [defaultValue, setValues, setValuesWithTz, tzDiff])
return (
<MyDatePicker {...rests} value={valuesWithTz} onChange={setValuesWithTz} />
)
}