更新:
在尝试在自定义datetime
选择器中使用这些功能后,我注意到从三月到四月的切换预期地触发了时区切换,因为我的区域在三月份切换了DST。但出乎意料的是,它跳过了同一时区内的标准时间和夏令时之间的切换,却切换到了下一个时区。
原来是因为我的原始函数总是为当前时间或过去的任意固定时间创建new Date()
。将其与三月和四月的相对时间进行比较,意味着它会逻辑上将DST切换检测为时区切换,而不是切换为同一时区内的标准时间和夏令时。
解决方法是将相对时间传递给实用函数,这样所有比较都是针对相对时间而不是现在或任意固定时间的。虽然失去了一些紧凑性,但现在逻辑按照需要工作。
工作流程更新:
t
参数默认为new Date()
- 对于固定时间,请传入现有的
Date
- 对于当前时间,请传入
null
或不传入任何内容
std()
已更新为使用t.setMonth(v);
来更改固定时间的月份
.getTimezoneOffset()
不能链接到.setMonth()
,因此我们需要从单行符号转换为使用闭包({}
)、终止符(;
)和return
console.log()
示例循环遍历每个月(0
到11
)
- 需要使用相同的时间戳克隆固定日期对象(
let ts = +t;
)
Date
类型前面的+
将其强制转换为Unix时间戳的number
Date()
也接受Unix时间戳以创建固定时间
- 如果我们不克隆它,每次调用都会传递相同的
Date
对象,月份设置为6
,这就失去了目的
- 好吧,我们实际上并没有克隆,只是使用相同的设置创建了一个新对象;没什么区别 ;)
let ns = {
std: (t = new Date()) => Math.max(...[0, 6].map(v => {
t.setMonth(v);
return t.getTimezoneOffset();
})),
is_dst: (t = new Date()) => t.getTimezoneOffset() < ns.std(t),
utc: (t, std = 0) => {
t = t || new Date();
let z = std ? ns.std(t) : t.getTimezoneOffset(),
zm = z % 60;
return 'UTC' + (z > 0 ? '-' : '+') + (z / 60) + (zm ? ':' + zm : '');
}
};
console.log(ns.std(), ns.is_dst(), ns.utc(), ns.utc(null, 1));
let t = new Date(2021,0,1);
for (let i = 0; i < 12; i++) {
t.setMonth(i);
let ts = +t;
console.log(t.toDateString().split(" ")[1], ns.std(new Date(ts)), ns.is_dst(new Date(ts)), ns.utc(new Date(ts)), ns.utc(new Date(ts), 1));
}
在@nkitku提供的简洁易懂解决方案的基础上,进一步拓展为可复用函数集。
工作流程:
所有函数都在命名空间
ns
中定义,以避免与代码中可能具有相同名称的其他函数冲突。
命名空间还允许使用紧凑的函数符号;
std: ()=>Math.max()
等同于
function std(){ return Math.max(); }
。
std()
返回标准时间的时区偏移量。
[0, 6]
设置了一个没有DST的月份和一个有DST的月份的比较。
0
表示1月,因为
Date.setMonth()
是从零开始索引的。
6
表示7月。
显然,并非每个人的标准时间都在1月,因此我们必须同时检查1月和7月。
...[]
将月份的
Array
转换为
Set
,以便我们可以应用
map()
函数。
原始数组无法运行
map()
。
map()
在同一函数上运行一组变量,并返回结果数组。
使用年、月、日创建一个新的
Date
对象。
年份(例如示例中的
95
)是任意的,因为年份对于这个计算并不重要。
月份将我们的值
[0, 6]
作为变量
v
插入。
日期(例如示例中的
1
)也是任意的。
从逻辑上讲,我们可以创建一个
new Date()
,然后
.setMonth(v)
,但使用任意数字更紧凑、更快。
现在我们有了日期,
getTimezoneOffset()
返回每个月的偏移量,并将它们推送到结果数组中。
Math.max()
找到结果中的最大值,这将是标准时间偏移量。
is_dst()
检查当前是否为夏令时。
new Date().getTimezoneOffset()
获取当前偏移量,无论是否使用DST。
ns.std()
获取标准时间的偏移量。
如果当前偏移量较低,则为DST。
utc()
以UTC符号返回字符串。
std
参数默认为关闭状态。
z = std ? ns.std() : new Date().getTimezoneOffset()
根据标志设置时间为DST或标准时间。
zm = z % 60
捕获分钟,因为某些区域使用30分钟。
(z > 0 ? '-' : '+')
根据UTC符号分配正确的符号;正偏移值在符号中显示为负偏移。
(z / 60)
捕获小时,以单个数字格式显示,因此不需要
.toString().padStart(2,'0')
进行双位数字格式。
(zm ? ':' + zm : '')
如果时区存在,则附加分钟。
由于这个版本旨在紧凑,您可以通过去除多余的空格来节省更多的空间。不过这真的是缩小文件的工作。
std:()=>Math.max(...[0,6].map(v=>new Date(95,v,1).getTimezoneOffset())),
const ns = {
std: () => Math.max(...[0, 6].map(v => new Date(95, v, 1).getTimezoneOffset())),
is_dst: () => new Date().getTimezoneOffset() < ns.std(),
utc: (std = 0) => {
let z = std ? ns.std() : new Date().getTimezoneOffset(),
zm = z % 60;
return 'UTC' + (z > 0 ? '-' : '+') + (z / 60) + (zm ? ':' + zm : '');
}
};
console.log(ns.std(), ns.is_dst(), ns.utc(), ns.utc(1));