如何检查夏令时是否生效,如果是的话,偏移量是多少?

219

这是我的一段JS代码,需要使用它:

var secDiff = Math.abs(Math.round((utc_date-this.premiere_date)/1000));
this.years = this.calculateUnit(secDiff,(86400*365));
this.days = this.calculateUnit(secDiff-(this.years*(86400*365)),86400);
this.hours = this.calculateUnit((secDiff-(this.years*(86400*365))-(this.days*86400)),3600);
this.minutes = this.calculateUnit((secDiff-(this.years*(86400*365))-(this.days*86400)-(this.hours*3600)),60);
this.seconds = this.calculateUnit((secDiff-(this.years*(86400*365))-(this.days*86400)-(this.hours*3600)-(this.minutes*60)),1);

我想获得"ago"时间格式的日期时间,但如果正在使用夏令时(DST),则日期会偏移1小时。 我不知道如何检查是否正在使用夏令时。

我如何知道何时开始和结束夏令时?


zoned-date 是一个具有时区和夏令时支持的日期库。 - undefined
16个回答

2

更新: 在尝试在自定义datetime选择器中使用这些功能后,我注意到从三月到四月的切换预期地触发了时区切换,因为我的区域在三月份切换了DST。但出乎意料的是,它跳过了同一时区内的标准时间和夏令时之间的切换,却切换到了下一个时区。

原来是因为我的原始函数总是为当前时间或过去的任意固定时间创建new Date()。将其与三月和四月的相对时间进行比较,意味着它会逻辑上将DST切换检测为时区切换,而不是切换为同一时区内的标准时间和夏令时。

解决方法是将相对时间传递给实用函数,这样所有比较都是针对相对时间而不是现在或任意固定时间的。虽然失去了一些紧凑性,但现在逻辑按照需要工作。

工作流程更新:

  • t参数默认为new Date()
    • 对于固定时间,请传入现有的Date
    • 对于当前时间,请传入null或不传入任何内容
  • std()已更新为使用t.setMonth(v);来更改固定时间的月份
    • .getTimezoneOffset()不能链接到.setMonth(),因此我们需要从单行符号转换为使用闭包({})、终止符(;)和return
  • console.log()示例循环遍历每个月(011
    • 需要使用相同的时间戳克隆固定日期对象(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 : '');
  }
};

//current time only
console.log(ns.std(), ns.is_dst(), ns.utc(), ns.utc(null, 1));

//iterate each month
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));


1
你接近了答案但还有一点偏差。你不需要计算自己的时间,因为它是由你自己的时钟产生的结果。它可以检测你是否在使用夏令时,但不能对远程位置进行检测,因为偏移量不同。
newDateWithOffset = new Date(utc + (3600000*(offset)));

如果当前处于夏令时,这个做法会导致时间还是快了一个小时。需要判断远程时间是否在夏令时期间并进行相应的调整。可以计算出一个偏移量,然后设置时钟为 - 比如说 2015 年 2 月 1 日,并将时钟往回拨一个小时,就好像不在夏令时一样。然后再计算一个本来应该慢两小时的地方的偏移量,这会显示比这个两小时窗口提前了一个小时。你仍然需要考虑这个小时并进行调整。我用纽约和丹佛试过,但丹佛总是出现错误的时间(比实际时间早了一个小时)。


1

1
你是什么意思? - stackoverflow

1

假设夏令时在3月27日更改,以下代码可用于检测夏令时:

//utcT1: UTC time in [ms] before DST (DST has no impact on UTC!)
const utcT1 = (new Date(Date.UTC(2023, 2, 26))).getTime();
//locT1: local time in [ms] before setting DST
const locT1 = (new Date(2023, 2, 26)).getTime();
//tz1: time zone before DST
const tz1 = (locT1 - utcT1)/1000/60;
//expected output -60 [min] for e.g. Berlin

//utcT2: UTC time in [ms] after DST
const utcT2 = (new Date(Date.UTC(2023, 2, 27))).getTime();
//locT2: local time in [ms] after DST
const locT2 = (new Date(2023, 2, 27)).getTime();
//tz2: time zone after DST
const tz2 = (locT2 - utcT2)/1000/60;
//expected output -120 [min] for e.g. Berlin

//I.e. the DST for the local time zone is 60 [min]

//Similar result can be obtain by using the getTimezoneOffset
console.log((new Date(2023, 2, 26)).getTimezoneOffset());//-60
console.log((new Date(2023, 2, 27)).getTimezoneOffset());//-120


0

最近我需要创建一个带有UTC和DST的日期字符串,基于Sheldon的回答,我把它整理了一下:

Date.prototype.getTimezone = function(showDST) {
    var jan = new Date(this.getFullYear(), 0, 1);
    var jul = new Date(this.getFullYear(), 6, 1);

    var utcOffset = new Date().getTimezoneOffset() / 60 * -1;
    var dstOffset = (jan.getTimezoneOffset() - jul.getTimezoneOffset()) / 60;

    var utc = "UTC" + utcOffset.getSign() + (utcOffset * 100).preFixed(1000);
    var dst = "DST" + dstOffset.getSign() + (dstOffset * 100).preFixed(1000);

    if (showDST) {
        return utc + " (" + dst + ")";
    }

    return utc;
}
Number.prototype.preFixed = function (preCeiling) {
    var num = parseInt(this, 10);
    if (preCeiling && num < preCeiling) {
        num = Math.abs(num);
        var numLength   = num.toString().length;
        var preCeilingLength = preCeiling.toString().length;
        var preOffset   = preCeilingLength - numLength;
        for (var i = 0; i < preOffset; i++) {
            num = "0" + num;
        }
    }
    return num;
}
Number.prototype.getSign = function () {
    var num  = parseInt(this, 10);
    var sign = "+";
    if (num < 0) {
        sign = "-";
    }
    return sign;
}

document.body.innerHTML += new Date().getTimezone() + "<br>";
document.body.innerHTML += new Date().getTimezone(true);
<p>Output for Turkey (UTC+0200) and currently in DST: &nbsp; UTC+0300 (DST+0100)</p>
<hr>


0

使用Date.toString().indexOf('Daylight Time') > -1是否有问题?

"" + new Date()

Sat Jan 01 100050 00:00:00 GMT-0500 (东部标准时间)

"" + new Date(...)

Sun May 01 100033 00:00:00 GMT-0400 (东部夏令时)

这似乎与所有浏览器兼容。


5
是的,它并非全球通用。在欧洲的夏季,你会得到这样的日期时间格式:"Thu Jul 02 2020 14:07:01 GMT+0200 (Central European Summer Time)" - Tadej Krevh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接