如何检查DST(夏令时)是否有效以及它是否是偏移量?

Jo *_*Smo 143 javascript dst

这是我需要的一些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);
Run Code Online (Sandbox Code Playgroud)

我想在之前获得日期时间,但是如果正在使用DST,则日期将关闭1小时.我不知道如何检查DST是否正在使用中.

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

小智 290

此代码使用在标准时间与夏令时(DST)期间getTimezoneOffset返回更大值的事实.因此,它确定标准时间内的预期输出,并比较给定日期的输出是否相同(标准)或更低(DST).

请注意,getTimezoneOffset返回UTC 以西区域的分钟数,通常表示为小时(因为它们"落后于"UTC).例如,洛杉矶是UTC-8h标准,UTC-7h DST.12月(冬季,标准时间)返回(正480分钟),而不是.它返回东半球的负数(冬季为悉尼,尽管这是"未来"(UTC + 10h).getTimezoneOffset480-480-600

Date.prototype.stdTimezoneOffset = function () {
    var jan = new Date(this.getFullYear(), 0, 1);
    var jul = new Date(this.getFullYear(), 6, 1);
    return Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset());
}

Date.prototype.isDstObserved = function () {
    return this.getTimezoneOffset() < this.stdTimezoneOffset();
}

var today = new Date();
if (today.isDstObserved()) { 
    alert ("Daylight saving time!");
}
Run Code Online (Sandbox Code Playgroud)

  • 我可以证实这在国际上有效.目前没有时区使用任何形式的夏令时,其中1月1日和7月1日都在夏令时期间或两者之内.此外,在TZDB的所有时区([有一个普通的例外](http://en.wikipedia.org/wiki/Antarctica/Cas​​ey))两个偏移中较大的一个是DST偏移.由于JavaScript的`getTimezoneOffset`返回反向值,因此`Math.max`确实返回*standard*offset.代码是正确的. (27认同)
  • 这在一般情况下不起作用,例如有些国家在某些年份没有观察到夏令时,也有一些国家在斋月期间恢复夏令时.接下来,Date的ECMAScript定义被破坏,并且在某些实现中也破坏了TZ环境变量的处理.所有这些结合使得这种方法不可靠.你最好使用不使用Date的库,例如timezonecomplete (9认同)
  • 对于使用以 UTC 作为本地时区的 AWS Lambda 或 Node 的任何人来说,这将不起作用。getTimezoneOffset() 在 1 月和 7 月都会返回 0。 (8认同)
  • 但是,如果任何时区都改变了它的定义,使得1月1日和7月1日都在DST中,或者两者都在DST中不*(并且DST仍然适用),则此代码在该区域中不起作用. (7认同)
  • 此代码在不遵守DST的国家/地区不起作用,例如南非或冰岛; 这意味着如果您将其用于与这些国家/地区的其他时区进行比较,则无法在此处显示正确的时间.建议一直使用UTC,并手动检查现在的时间是否在某个DST范围内.然后,只需将正常时间UTC偏移量改变+1即可获得DST. (5认同)
  • 该代码存在严重缺陷,并非在所有情况下都有效,因此切勿使用。例如,America/New_York 时区的偏移量通常为 240,在夏令时偏移量为 300。此代码的工作方式是,它获取 1 月和 7 月偏移量中较大的数字。返回 300。然后“dst”函数将当前偏移量与 jan 和 jul 偏移量中较大的一个进行比较。因此,在 11 月,偏移量将为 300,而 jan 和 jul 中的较大者为 300。300 不小于 300。即使 11 月处于夏令时,也会失败。 (3认同)
  • @AlanWells 关于您的评论“DST 不正常”(正确),“DST 是在冬天”(错误)。“所以,‘正常’是在夏天,夏令时不会人为地改变时间”(假)。DST 实际上是夏令时。冬天是“正常”的。请参阅 https://time.is/New_York,其中“当前东部标准时间 (EST),UTC -5”和“夏令时(东部夏令时间 (EDT),UTC -4)从 2020 年 3 月 8 日开始”。 (2认同)

Jon*_*der 20

创建两个日期:一个在六月,一个在一月.比较它们的getTimezoneOffset()值.

  • 如果1月抵消> 6月抵消,客户在北半球
  • 如果1月抵消<6月抵消,客户在南半球
  • 如果没有差异,客户端时区不会观察到DST

现在检查当前日期的getTimezoneOffset().

  • 如果等于六月,北半球,则当前时区为夏令时(+1小时)
  • 如果等于1月,南半球,则当前时区为夏令时(+1小时)


Toa*_*gma 14

这个答案与接受的答案非常相似,但不会覆盖Date原型,只使用一个函数调用来检查夏令时是否有效,而不是两个.


这个想法是,因为没有国家观察持续7个月的DST [1],在观察夏令时的区域中,1月份与UTC时间的偏差将与7月份的偏差不同.

虽然夏令时时钟移动向前,JavaScript的总是返回一个更大的标准时间值.因此,在1月到7月之间获得最小偏移将在DST期间获得时区偏移.

然后我们检查日期时区是否等于该最小值.如果是,那么我们在DST; 否则我们不是.

以下函数使用此算法.它需要一个日期对象,d并且true如果夏令时在该日期生效,false则返回,如果不是:

function isDST(d) {
    let jan = new Date(d.getFullYear(), 0, 1).getTimezoneOffset();
    let jul = new Date(d.getFullYear(), 6, 1).getTimezoneOffset();
    return Math.max(jan, jul) != d.getTimezoneOffset(); 
}
Run Code Online (Sandbox Code Playgroud)

  • 这有效,但如果当前时区中没有 DST,那么它也会导致 true,这是不正确的。如果您将其切换为`Math.max(...) != d.get...()`,如果在给定的时区中观察到 DST 并且日期当前在 DST 中,它将返回 true。如果未遵守 DST 或日期与标准偏移量匹配,它将返回 false。 (5认同)
  • 是的,这是 @GreySage 的一个很棒的地方,我在他们评论后很快编辑了答案以将其合并。 (4认同)
  • 似乎答案已被编辑以适应上面评论中的修复。@Toastrackenigma 你能确认并添加说明是这种情况吗? (3认同)

Aar*_*ole 12

我今天遇到了同样的问题,但由于我们的夏令时开始和停止在与美国不同的时间(至少从我的理解),我使用了一个稍微不同的路线..

var arr = [];
for (var i = 0; i < 365; i++) {
 var d = new Date();
 d.setDate(i);
 newoffset = d.getTimezoneOffset();
 arr.push(newoffset);
}
DST = Math.min.apply(null, arr);
nonDST = Math.max.apply(null, arr);
Run Code Online (Sandbox Code Playgroud)

然后,您只需将当前时区偏移与DST和nonDST进行比较,以查看哪一个匹配.

  • Rob - 如果你不知道在哪里搜索,你怎么能通过二分搜索来做到这一点(即你要找的地方是你的测试点之上还是之下?) (2认同)

epe*_*leg 9

基于Matt Johanson对Sheldon Griffin提供的解决方案的评论,我创建了以下代码:

    Date.prototype.stdTimezoneOffset = function() {
        var fy=this.getFullYear();
        if (!Date.prototype.stdTimezoneOffset.cache.hasOwnProperty(fy)) {

            var maxOffset = new Date(fy, 0, 1).getTimezoneOffset();
            var monthsTestOrder=[6,7,5,8,4,9,3,10,2,11,1];

            for(var mi=0;mi<12;mi++) {
                var offset=new Date(fy, monthsTestOrder[mi], 1).getTimezoneOffset();
                if (offset!=maxOffset) { 
                    maxOffset=Math.max(maxOffset,offset);
                    break;
                }
            }
            Date.prototype.stdTimezoneOffset.cache[fy]=maxOffset;
        }
        return Date.prototype.stdTimezoneOffset.cache[fy];
    };

    Date.prototype.stdTimezoneOffset.cache={};

    Date.prototype.isDST = function() {
        return this.getTimezoneOffset() < this.stdTimezoneOffset(); 
    };
Run Code Online (Sandbox Code Playgroud)

它试图在考虑所有评论和以前建议的答案的情况下,充分利用所有世界,特别是:

1)缓存每年stdTimezoneOffset的结果,以便在同一年测试多个日期时不需要重新计算它.

2)它不假设DST(如果它存在的话)必然在7月份,并且即使它在某个时间点和某个地方是任何月份也会起作用.然而,如果确实是7月(或接近几个月)确实是DST,那么在性能方面它将更快地工作.

3)更糟糕的情况是它会比较每月第一天的getTimezoneOffset.[并且每个测试年份执行一次].

它仍然做出的假设是,如果有DST期间大于一个月.

如果有人想要删除这个假设,他可以将循环变成更像是Aaron Cole提供的解决方案中的东西 - 但我仍然会提前半年跳出来并在发现两个不同的偏移时突破循环]


Jac*_*fin 8

适用于所有时区的面向未来的解决方案

\n
    \n
  1. x为感兴趣年份的预期毫秒数(不考虑夏令时)。
  2. \n
  3. 设为自纪元以来从感兴趣日期的年份开始算起y的毫秒数。
  4. \n
  5. 设为自感兴趣的完整日期和时间的纪元z以来的毫秒数
  6. \n
  7. 让是和的t减法:。这会产生 DST 造成的偏移。xyzz - y - x
  8. \n
  9. 如果t为零,则 DST 无效。如果t不为零,则 DST 生效。
  10. \n
\n

\r\n
\r\n
"use strict";\nfunction dstOffsetAtDate(dateInput) {\n    var fullYear = dateInput.getFullYear()|0;\n    // "Leap Years are any year that can be exactly divided by 4 (2012, 2016, etc)\n    //   except if it can be exactly divided by 100, then it isn\'t (2100,2200,etc)\n    //    except if it can be exactly divided by 400, then it is (2000, 2400)"\n    // (https://www.mathsisfun.com/leap-years.html).\n    var isLeapYear = ((fullYear & 3) | (fullYear/100 & 3)) === 0 ? 1 : 0;\n    // (fullYear & 3) = (fullYear % 4), but faster\n    //Alternative:var isLeapYear=(new Date(currentYear,1,29,12)).getDate()===29?1:0\n    var fullMonth = dateInput.getMonth()|0;\n    return (\n        // 1. We know what the time since the Epoch really is\n        (+dateInput) // same as the dateInput.getTime() method\n        // 2. We know what the time since the Epoch at the start of the year is\n        - (+new Date(fullYear, 0)) // day defaults to 1 if not explicitly zeroed\n        // 3. Now, subtract what we would expect the time to be if daylight savings\n        //      did not exist. This yields the time-offset due to daylight savings.\n        - ((\n            ((\n                // Calculate the day of the year in the Gregorian calendar\n                // The code below works based upon the facts of signed right shifts\n                //    \xe2\x80\xa2 (x) >> n: shifts n and fills in the n highest bits with 0s \n                //    \xe2\x80\xa2 (-x) >> n: shifts n and fills in the n highest bits with 1s\n                // (This assumes that x is a positive integer)\n                -1 + // first day in the year is day 1\n                (31 & ((-fullMonth) >> 4)) + // January // (-11)>>4 = -1\n                ((28 + isLeapYear) & ((1-fullMonth) >> 4)) + // February\n                (31 & ((2-fullMonth) >> 4)) + // March\n                (30 & ((3-fullMonth) >> 4)) + // April\n                (31 & ((4-fullMonth) >> 4)) + // May\n                (30 & ((5-fullMonth) >> 4)) + // June\n                (31 & ((6-fullMonth) >> 4)) + // July\n                (31 & ((7-fullMonth) >> 4)) + // August\n                (30 & ((8-fullMonth) >> 4)) + // September\n                (31 & ((9-fullMonth) >> 4)) + // October\n                (30 & ((10-fullMonth) >> 4)) + // November\n                // There are no months past December: the year rolls into the next.\n                // Thus, fullMonth is 0-based, so it will never be 12 in Javascript\n                \n                (dateInput.getDate()|0) // get day of the month\n                \n            )&0xffff) * 24 * 60 // 24 hours in a day, 60 minutes in an hour\n            + (dateInput.getHours()&0xff) * 60 // 60 minutes in an hour\n            + (dateInput.getMinutes()&0xff)\n        )|0) * 60 * 1000 // 60 seconds in a minute * 1000 milliseconds in a second\n        - (dateInput.getSeconds()&0xff) * 1000 // 1000 milliseconds in a second\n        - dateInput.getMilliseconds()\n    );\n}\n\n// Demonstration:\nvar date = new Date(2100, 0, 1)\nfor (var i=0; i<12; i=i+1|0, date.setMonth(date.getMonth()+1|0))\n    console.log(date.getMonth()+":\\t"+dstOffsetAtDate(date)/60/60/1000+"h\\t"+date);\ndate = new Date(1900, 0, 1);\nfor (var i=0; i<12; i=i+1|0, date.setMonth(date.getMonth()+1|0))\n    console.log(date.getMonth()+":\\t"+dstOffsetAtDate(date)/60/60/1000+"h\\t"+date);\n\n// Performance Benchmark:\nconsole.time("Speed of processing 16384 dates");\nfor (var i=0,month=date.getMonth()|0; i<16384; i=i+1|0)\n    date.setMonth(month=month+1+(dstOffsetAtDate(date)|0)|0);\nconsole.timeEnd("Speed of processing 16384 dates");
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n

我相信,出于多种原因,上面的代码片段优于此处发布的所有其他答案。

\n
    \n
  • 这个答案适用于所有时区,甚至包括南极洲/凯西
  • \n
  • 夏令时很容易发生变化。20 年后,某些国家/地区可能有 3 个 DST 周期,而不是正常的 2 个。此代码通过返回以毫秒为单位的 DST 偏移量来处理这种情况,而不仅仅是 DST 是否有效。
  • \n
  • 一年中月份的大小和闰年的工作方式非常适合让我们的时间与太阳保持一致。哎呀,它工作得如此完美,我们所做的只是在这里或那里调整几秒钟我们现行的闰年制度自1582 年 2 月 24 日起生效,并且在可预见的未来可能会继续有效。
  • \n
  • 此代码适用于不使用 DST 的时区。
  • \n
  • 此代码适用于实施 DST 之前的历史时期(例如 1900 年代)。
  • \n
  • 该代码是最大整数优化的,如果在紧密循环中调用,应该不会给您带来任何问题。运行上面的代码片段后,向下滚动到输出底部以查看性能基准。我的计算机在 FireFox 上能够在 29 毫秒内处理 16384 个日期。
  • \n
\n

但是,如果您没有准备超过 2 个 DST 周期,则可以使用以下代码来确定 DST 是否作为布尔值有效。

\n
function isDaylightSavingsInEffect(dateInput) {\n    // To satisfy the original question\n    return dstOffsetAtDate(dateInput) !== 0;\n}\n
Run Code Online (Sandbox Code Playgroud)\n


Ala*_*lls 7

getTimezoneOffset()JavaScript 中的方法在浏览器中返回从 00:00 时区偏移的分钟数。例如,夏令时 (DST) 中的 America/New_York 时区返回数字 300。300 分钟与零相差 5 小时。300 分钟除以 60 分钟是 5 小时。每个时区都与零时区进行比较,+00:00 / Etc/GMT / 格林威治时间。

MDN 网络文档

接下来您必须知道的是,偏移量与实际时区的符号相反。

有关时区的信息由 Internet Assigned Numbers Authority (iana) 维护

伊安娜时区

joda.org 提供了一个格式良好的时区表

joda-time 时区

+00:00 或 Etc/GMT 是格林威治时间

所有时区都偏离 +00:00 / "Etc/GMT" / 格林威治时间

夏令时总是比夏季的“常规”时间早。您在秋季将时钟调回。(“Fall Back”口号记住该做什么)

因此,夏令时(冬季)中的 America/New_York 时间比常规时间早一小时。因此,例如,夏季纽约市通常下午 5 点,现在是夏令时美国/纽约时间下午 4 点。名称“America/New_York”时间是“长格式”时区名称。美国东海岸通常称他们的时区为东部标准时间 (EST)

如果要将今天的时区偏移量与其他日期的时区偏移量进行比较,则需要知道时区偏移量的数学符号(+/-“正/负”)与时区相反。

查看 joda.org 上的时区表,找到“America/New_York”的时区,它的标准偏移量前面会有一个负号。

地球在它的轴上逆时针旋转。在格林威治看日出的人比纽约市看日出的人早 5 小时。在美国东海岸有人看到日出之后,美国西海岸有人会看到日出。

您需要了解所有这些是有原因的。这样您就可以从逻辑上确定某些 JavaScript 代码是否正确获得了 DST 状态,而无需在一年中的不同时间测试每个时区。

想象一下,现在是纽约市的 11 月,时钟已经倒退了一个小时。在纽约市的夏季,偏移量为 240 分钟或 4 小时。

您可以通过创建一个 7 月的日期然后获取偏移量来测试这一点。

var July_Date = new Date(2017, 6, 1);
var july_Timezone_OffSet = July_Date.getTimezoneOffset();

console.log('july_Timezone_OffSet: ' + july_Timezone_OffSet)
Run Code Online (Sandbox Code Playgroud)

什么会打印到浏览器的开发者工具控制台日志?

答案是:240

因此,现在您可以在 1 月创建一个日期,并查看您的浏览器针对冬季的时区偏移返回的内容。

var Jan_Date = new Date(2017, 0, 1);//Month is zero indexed - Jan is zero
var jan_Timezone_OffSet = Jan_Date.getTimezoneOffset();

console.log('jan_Timezone_OffSet: ' + jan_Timezone_OffSet)
Run Code Online (Sandbox Code Playgroud)

答案是:300

显然 300 比 240 大。那么,这是什么意思呢?您是否应该编写测试冬季偏移量大于夏季偏移量的代码?或者夏季偏移量小于冬季偏移量?如果夏季和冬季时区偏移量之间存在差异,则您可以假设该时区使用的是 DST。但这并不能告诉您今天是否对浏览器时区使用 DST。因此,您需要获取今天的时区偏移量。

var today = new Date();
var todaysTimeZone = today.getTimezoneOffset();

console.log('todaysTimeZone : ' + todaysTimeZone)
Run Code Online (Sandbox Code Playgroud)

答案是: ? - 取决于一年中的时间

如果今天的时区偏移量和夏令时区偏移量是相同的,夏季和冬季的时区偏移量是不同的,然后通过逻辑推演,今天必须不能在DST。

您是否可以省略比较夏季和冬季时区偏移量(要知道该时区是否使用 DST)而仅将今天的时区偏移量与夏季 TZ 偏移量进行比较,并始终得到正确的答案?

today's TZ Offset !== Summer TZ Offset
Run Code Online (Sandbox Code Playgroud)

那么,今天是冬天还是夏天?如果您知道,那么您可以应用以下逻辑:

if ( it_is_winter && ( todays_TZ_Offset !== summer_TZ_Offset) {
  var are_We_In_DST = true;
}
Run Code Online (Sandbox Code Playgroud)

但问题是,您不知道今天的日期是冬天还是夏天。每个时区都可以有自己的 DST 开始和停止时间规则。您需要跟踪世界上每个时区的每个时区规则。所以,如果有更好、更简单的方法,那么你不妨用更好、更简单的方法来做。

我们剩下的是,您需要知道这个时区是否使用 DST,然后将今天的时区偏移量与夏季时区偏移量进行比较。那总会给你一个可靠的答案。

最后的逻辑是:

if ( DST_Is_Used_In_This_Time_Zone && ( todays_TZ_Offset !== summer_TZ_Offset) {
  var are_We_In_DST = true;
}
Run Code Online (Sandbox Code Playgroud)

判断浏览器时区是否使用夏令时的函数:

function is_DST_Used_In_This_TimeZone() {
  var Jan_Date, jan_Timezone_OffSet, July_Date, july_Timezone_OffSet 
      offsetsNotEqual, thisYear, today;

  today = new Date();//Create a date object that is now
  thisYear = today.getFullYear();//Get the year as a number

  Jan_Date = new Date(thisYear, 0, 1);//Month is zero indexed - Jan is zero
  jan_Timezone_OffSet = Jan_Date.getTimezoneOffset();

  console.log('jan_Timezone_OffSet: ' + jan_Timezone_OffSet)

  July_Date = new Date(thisYear, 6, 1);
  july_Timezone_OffSet = July_Date.getTimezoneOffset();

  console.log('july_Timezone_OffSet: ' + july_Timezone_OffSet)

  offsetsNotEqual = july_Timezone_OffSet !== jan_Timezone_OffSet;//True if not equal

  console.log('offsetsNotEqual: ' + offsetsNotEqual);

  return offsetsNotEqual;//If the offsets are not equal for summer and
       //winter then the only possible reason is that DST is used for
       //this time zone
}
Run Code Online (Sandbox Code Playgroud)

  • [根据 dateandtime.com](https://www.timeanddate.com/worldclock/usa/new-york) 2019 年 DST 于 3 月 10 日开始,因此是在夏季,而不是冬季,纽约的 DST 偏移量为 - 4,不是-5。 (2认同)