Как проверить, действует ли 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);
Я хочу получить дату и время назад, но если летнее время используется, то даты отключаются на 1 час. Я не знаю, как проверить, используется ли DST или нет.
Как я могу узнать, когда летнее время начинается и заканчивается?
17 ответов
Этот код использует тот факт, что getTimezoneOffset
возвращает большее значение в течение стандартного времени по сравнению с летним временем (DST). Таким образом, он определяет ожидаемый результат в течение стандартного времени и сравнивает, является ли результат данной даты одинаковым (стандартным) или меньшим (летнее время).
Обратите внимание, что getTimezoneOffset
возвращает положительные числа минут для зон к западу от UTC, которые обычно указываются как отрицательные часы (так как они "позади" UTC). Например, Лос-Анджелес - это UTC-8h Standard, UTC-7h DST. getTimezoneOffset
возвращается 480
(положительные 480 минут) в декабре (зима, стандартное время), а не -480
, Возвращает отрицательные числа для Восточного полушария (например, -600
в Сиднее зимой, несмотря на то, что это "впереди" (UTC + 10ч).
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!");
}
Этот ответ очень похож на принятый ответ, но не отменяет Date
прототип, и использует только один вызов функции, чтобы проверить, действует ли переход на летнее время, а не два.
Идея заключается в том, что, поскольку ни одна страна не наблюдает летнее время, которое длится в течение 7 месяцев [1], в области, где наблюдается летнее время, смещение от времени UTC в январе будет отличаться от июля.
В то время как летнее время перемещает часы вперед, JavaScript всегда возвращает большее значение в течение стандартного времени. Поэтому, получая минимальное смещение между январем и июлем, получим смещение часового пояса во время летнего времени.
Затем мы проверяем, равен ли часовой пояс дат этому минимальному значению. Если это так, то мы находимся в летнее время; в противном случае мы не.
Следующая функция использует этот алгоритм. Требуется объект даты, 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.min(jan,jul) == d.getTimezoneOffset();
}
Создайте две даты: одну в июне, одну в январе. Сравните их значения getTimezoneOffset().
- если смещение января> смещение июня, клиент находится в северном полушарии
- если смещение в январе <смещение в июне, клиент находится в южном полушарии
- если нет разницы, клиентский часовой пояс не соблюдает летнее время
Теперь проверьте getTimezoneOffset() текущей даты.
- если оно равняется июню, северное полушарие, тогда текущий часовой пояс - летнее время (+1 час)
- если равняется январю, южное полушарие, тогда текущий часовой пояс - летнее время (+1 час)
Я столкнулся с этой же проблемой сегодня, но, поскольку наше летнее время начинается и заканчивается в разное время из США (по крайней мере, насколько я понимаю), я использовал немного другой маршрут..
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);
Затем вы просто сравниваете текущее смещение часового пояса с DST и nonDST, чтобы увидеть, какой из них соответствует.
getTimezoneOffset()
Метод в JavaScript, в браузере, возвращает количество минут смещения от часового пояса 00:00. Например, часовой пояс America/New_York в режиме летнего времени (DST) возвращает число 300. 300 минут - это 5-часовая разница от нуля. 300 минут, разделенные на 60 минут - это 5 часов. Каждый часовой пояс сравнивается с нулевым часовым поясом, +00:00 / Etc/GMT / время по Гринвичу.
Следующее, что вы должны знать, это то, что смещение имеет противоположный знак фактического часового пояса.
Информация о часовых поясах поддерживается Управлением по присвоению номеров в Интернете (iana)
Красиво отформатированная таблица часовых поясов предоставлена joda.org
+00: 00 или Etc / GMT - время по Гринвичу
Все часовые пояса смещены от +00:00 / "Etc/GMT" / время по Гринвичу
Летнее время всегда более раннее, чем "обычное" время летом. Вы устанавливаете свои часы назад в осенний сезон. (Лозунг "Отступить", чтобы вспомнить, что делать)
Так, в Америке / Нью-Йорке время в летнее время (зима) на один час раньше обычного времени. Так, например, то, что обычно было в 5 часов вечера в Нью-Йорке летом, сейчас в 4 часа дня по Америке / Нью-Йорк в летнее время. Название времени "America/New_York" является именем часового пояса "Long Format". Восточное побережье США обычно называет часовой пояс Восточное стандартное время (EST).
Если вы хотите сравнить текущее смещение часового пояса с смещением часового пояса какой-либо другой даты, вам нужно знать, что математический знак (+/- "Положительный / Отрицательный") смещения часового пояса является противоположностью часового пояса.
Посмотрите на таблицу часовых поясов на joda.org и найдите часовой пояс для "America/New_York". Он будет иметь отрицательный знак перед стандартным смещением.
Земля вращается против часовой стрелки вокруг своей оси. Человек, наблюдающий восход солнца в Гринвиче, видит восход солнца за 5 часов до того, как кто-то в Нью-Йорке увидит восход солнца. И кто-то на западном побережье США увидит восход солнца после того, как кто-то на восточном побережье США увидит восход солнца.
Есть причина, почему вам нужно знать все это. Так что вы сможете логически определить, правильно ли какой-то код JavaScript получает статус DST или нет, без необходимости тестировать каждый часовой пояс в разное время года.
Представьте, что в Нью-Йорке ноябрь, и часы были переведены на час назад. Летом в Нью-Йорке смещение составляет 240 минут или 4 часа.
Вы можете проверить это, создав дату в июле, а затем получив смещение.
var July_Date = new Date(2017, 6, 1);
var july_Timezone_OffSet = July_Date.getTimezoneOffset();
console.log('july_Timezone_OffSet: ' + july_Timezone_OffSet)
Что будет печататься в журнале консоли инструментов разработчика браузера?
Ответ: 240
Итак, теперь вы можете создать дату в январе и посмотреть, что ваш браузер возвращает для смещения часового пояса для зимнего сезона.
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)
Ответ: 300
Очевидно, 300 больше, чем 240. Итак, что это значит? Должны ли вы написать код, который проверяет, что зимнее смещение больше летнего? Или летнее смещение меньше зимнего? Если между летним и зимним смещением часового пояса есть разница, можно предположить, что для этого часового пояса используется летнее время. Но это не говорит вам, если сегодня используется DST для часового пояса браузера. Итак, вам нужно получить смещение часового пояса на сегодня.
var today = new Date();
var todaysTimeZone = today.getTimezoneOffset();
console.log('todaysTimeZone : ' + todaysTimeZone)
Ответ: - Зависит от времени года
Если текущее смещение часового пояса и смещение летнего часового пояса одинаковы, а смещения часового пояса летнего и зимнего периодов отличаются, то с помощью логического вычета сегодняшний день НЕ должен быть в летнее время.
Можете ли вы опустить сравнение смещений часового и летнего часовых поясов (чтобы узнать, используется ли DST для этого часового пояса) и просто сравнить сегодняшнее смещение часового пояса с летним смещением TZ и всегда получить правильный ответ?
today's TZ Offset !== Summer TZ Offset
Ну, сегодня зима или лето? Если бы вы знали это, вы могли бы применить следующую логику:
if ( it_is_winter && ( todays_TZ_Offset !== summer_TZ_Offset) {
var are_We_In_DST = true;
}
Но проблема в том, что вы не знаете, будет ли сегодняшняя дата зимой или летом. В каждом часовом поясе могут быть свои правила запуска и остановки DST. Вам нужно будет отслеживать правила каждого часового пояса для каждого часового пояса в мире. Так что, если есть лучший и более простой способ, вы можете сделать это лучше и проще.
Что нам осталось, так это то, что вам нужно знать, использует ли этот часовой пояс летнее время, а затем сравнить текущее смещение часового пояса с летним смещением часового пояса. Это всегда даст вам надежный ответ.
Последняя логика:
if ( DST_Is_Used_In_This_Time_Zone && ( todays_TZ_Offset !== summer_TZ_Offset) {
var are_We_In_DST = true;
}
Функция для определения, использует ли часовой пояс в браузере DST:
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
}
На основании комментария Мэтта Джохансона к решению, предоставленному Шелдоном Гриффином, я создал следующий код:
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();
};
Он пытается получить лучшее из всех миров, принимая во внимание все комментарии и ранее предложенные ответы, а именно:
1) Кэширует результат за год stdTimezoneOffset, так что вам не нужно пересчитывать его при тестировании нескольких дат в одном году.
2) Он не предполагает, что летнее время (если оно вообще существует) обязательно в июле, и будет работать, даже если оно будет в какой-то момент и в каком-то месте в любой месяц. Однако с точки зрения производительности он будет работать быстрее, если действительно июль (или около месяца) действительно является летним временем.
3) В худшем случае он будет сравнивать getTimezoneOffset первого числа каждого месяца. [и делайте это Один раз в течение проверенного года].
Предположение, которое он все еще делает, состоит в том, что, если есть период DST, больше чем один месяц.
Если кто-то хочет снять это предположение, он может изменить цикл на нечто более похожее на то, что содержится в солютине, предоставленном Аароном Коулом, - но я все равно прыгнул бы на пол года вперед и вырвался из цикла, когда будут найдены два разных смещения]
Перспективное решение, которое работает во всех часовых поясах
- Позволять
x
быть ожидаемым числом миллисекунд в интересующем году без учета перехода на летнее время. - Позволять
y
количество миллисекунд с начала эпохи с даты интереса. - Позволять
z
количество миллисекунд с начала эпохи полной даты и времени интереса - Позволять
t
быть вычитанием обоихx
а такжеy
изz
:z - y - x
, Это дает смещение из-за летнего времени. - Если
t
ноль, то DST не действует. Еслиt
не равно нулю, тогда действует DST.
(function(){"use strict";
function dstOffsetAtDate(dateInput) {
var fullYear = dateInput.getFullYear()|0;
// "Leap Years are any year that can be exactly divided by 4 (2012, 2016, etc)
// except if it can be exactly divided by 100, then it isn't (2100,2200,etc)
// except if it can be exactly divided by 400, then it is (2000, 2400)"
// (https://www.mathsisfun.com/leap-years.html).
var isLeapYear = ((fullYear & 3) | (fullYear/100 & 3)) === 0 ? 1 : 0;
// (fullYear & 3) = (fullYear % 4), but faster
//Alternative:var isLeapYear=(new Date(currentYear,1,29,12)).getDate()===29?1:0
var fullMonth = dateInput.getMonth()|0;
return (
// 1. We know what the time since the Epoch really is
(+dateInput) // same as the dateInput.getTime() method
// 2. We know what the time since the Epoch at the start of the year is
- (+new Date(fullYear, 0, 0)) // day defaults to 1 if not explicitly zeroed
// 3. Now, subtract what we would expect the time to be if daylight savings
// did not exist. This yields the time-offset due to daylight savings.
- ((
((
// Calculate the day of the year in the Gregorian calendar
// The code below works based upon the facts of signed right shifts
// • (x) >> n: shifts n and fills in the n highest bits with 0s
// • (-x) >> n: shifts n and fills in the n highest bits with 1s
// (This assumes that x is a positive integer)
(31 & ((-fullMonth) >> 4)) + // January // (-11)>>4 = -1
((28 + isLeapYear) & ((1-fullMonth) >> 4)) + // February
(31 & ((2-fullMonth) >> 4)) + // March
(30 & ((3-fullMonth) >> 4)) + // April
(31 & ((4-fullMonth) >> 4)) + // May
(30 & ((5-fullMonth) >> 4)) + // June
(31 & ((6-fullMonth) >> 4)) + // July
(31 & ((7-fullMonth) >> 4)) + // August
(30 & ((8-fullMonth) >> 4)) + // September
(31 & ((9-fullMonth) >> 4)) + // October
(30 & ((10-fullMonth) >> 4)) + // November
// There are no months past December: the year rolls into the next.
// Thus, fullMonth is 0-based, so it will never be 12 in Javascript
(dateInput.getDate()|0) // get day of the month
)&0xffff) * 24 * 60 // 24 hours in a day, 60 minutes in an hour
+ (dateInput.getHours()&0xff) * 60 // 60 minutes in an hour
+ (dateInput.getMinutes()&0xff)
)|0) * 60 * 1000 // 60 seconds in a minute * 1000 milliseconds in a second
- (dateInput.getSeconds()&0xff) * 1000 // 1000 milliseconds in a second
- dateInput.getMilliseconds()
);
}
// Demonstration:
var date = new Date(2100, 0, 1)
for (var i=0; i<12; i=i+1|0, date.setMonth(date.getMonth()+1|0))
console.log(date.getMonth()+":\t"+dstOffsetAtDate(date)/60/60/1000+"h\t"+date);
date = new Date(1900, 0, 1);
for (var i=0; i<12; i=i+1|0, date.setMonth(date.getMonth()+1|0))
console.log(date.getMonth()+":\t"+dstOffsetAtDate(date)/60/60/1000+"h\t"+date);
// Performance Benchmark:
console.time("Speed of processing 16384 dates");
for (var i=0,month=date.getMonth()|0; i<16384; i=i+1|0)
date.setMonth(month=month+1+(dstOffsetAtDate(date)|0)|0);
console.timeEnd("Speed of processing 16384 dates");
})();
Я считаю, что приведенный выше фрагмент кода превосходит все остальные ответы, размещенные здесь по многим причинам.
- Этот ответ работает во всех часовых поясах, даже Антарктида / Кейси.
- Летнее время очень подвержено изменениям. Может случиться, что через 20 лет в некоторой стране может быть 3 периода DST вместо обычных 2. Этот код обрабатывает этот случай, возвращая смещение DST в миллисекундах, а не только то, действует ли DST или нет.
- Размер месяцев года и то, как работают високосные годы, идеально вписывается в наше время на пути к солнцу. Черт возьми, это работает так прекрасно, что все, что мы когда-либо делаем, это просто настраиваем простые секунды здесь и там. Наша нынешняя система високосных лет действует с 24 февраля 1582 года и, вероятно, будет действовать в обозримом будущем.
- Этот код работает в часовых поясах, которые не используют DST.
- Этот код работает в исторические времена до того, как был внедрен DST (например, 1900-е годы).
- Этот код максимально оптимизирован для целых чисел и не должен вызывать проблем при вызове в узком цикле. Запустив приведенный выше фрагмент кода, прокрутите вниз до нижней части вывода, чтобы увидеть тест производительности. Мой компьютер может обрабатывать 16384 даты за ~97 мс в Chrome.
Однако, если вы не готовитесь к более чем 2 периодам летнего времени, то приведенный ниже код можно использовать, чтобы определить, действует ли DST как логическое значение.
function isDaylightSavingsInEffect(dateInput) {
// To satisfy the original question
return dstOffsetAtDate(dateInput) !== 0;
}
Библиотека moment.js предоставляет .isDst()
метод на свое время объекты.
момент #isDST проверяет, находится ли текущий момент в летнее время.
moment([2011, 2, 12]).isDST(); // false, March 12 2011 is not DST
moment([2011, 2, 14]).isDST(); // true, March 14 2011 is DST
Обновление: после попытки использовать эти функции в пользовательском
datetime
picker, я заметил, что при переключении с марта на апрель часовой пояс переключается, как и ожидалось, так как моя зона переключает летнее время в марте. Неожиданно он переключился на следующий часовой пояс вместо переключения между стандартным и дневным светом в том же часовом поясе.
Оказывается, это потому, что мои исходные функции всегда создавались для текущего времени или произвольного фиксированного времени в прошлом. Сравнение этого с относительным временем с марта и апреля означало, что он логически обнаружит переключение на летнее время как переключение часовых поясов.
Решением было передать относительное время в функции полезности, поэтому все мои сравнения были для относительного времени, а не сейчас или произвольного фиксированного времени. Потеряли часть компактности, но теперь логика работает как надо.
Обновления рабочего процесса:
-
t
параметр по умолчаниюnew Date()
- На фиксированное время передайте существующий
- На текущее время перейти
null
или ничего
-
std()
обновлен для использованияt.setMonth(v);
изменить месяц на фиксированное время-
.getTimezoneOffset()
не может цепляться за.setMonth()
, поэтому нам нужно отказаться от однострочной записи, чтобы использовать замыкания ({}
), терминаторы (;
), а такжеreturn
-
-
console.log()
пример проходит через каждый месяц (0
к11
)- Объект с фиксированной датой необходимо клонировать с использованием той же метки времени (
let ts = +t;
) - В
+
до того, как тип переведет его вnumber
с меткой времени Unix -
Date()
также принимает временные метки Unix для создания фиксированного времени - Если мы не клонируем его, каждый вызов будет проходить примерно так же
Date
объект с месяцами, установленными на6
, который побеждает цель - Хорошо, на самом деле мы не клонируем, а просто создаем новый объект с теми же настройками; такая же разница ;)
- Объект с фиксированной датой необходимо клонировать с использованием той же метки времени (
Я обнаружил, что использование библиотеки Moment.js с некоторыми из описанных здесь концепций (сравнение января и июня) работает очень хорошо.
Эта простая функция возвратит, наблюдает ли часовой пояс, в котором находится пользователь, переход на летнее время:
function HasDST() {
return moment([2017, 1, 1]).isDST() != moment([2017, 6, 1]).isDST();
}
Простой способ убедиться в том, что это работает (в Windows), - это изменить часовой пояс на зону, отличную от DST, например, Аризона вернет false, тогда как EST или PST вернет true.
С https://date-fns.org/v2.22.1/docs/Time-Zones можно решить одной строкой
новая дата ().getUTCHours() + getTimezoneOffset('Европа / Амстердам') / 1000/60/60;
Используйте Moment.js (https://momentjs.com/)
moment().isDST();
даст вам, если соблюдается дневная экономия.
Также у него есть вспомогательная функция для вычисления относительного времени. Вам не нужно производить расчеты вручную, напримерmoment("20200105", "YYYYMMDD").fromNow();
ES6 Стиль
Math.min(...[0, 6].map(v => new Date(95, v, 1).getTimezoneOffset() * -1));
Предполагая, что 27 марта будет изменено летнее время, для определения летнего времени можно использовать следующее:
Вы близко, но немного не в себе. Вам никогда не нужно рассчитывать свое время, так как оно является результатом ваших собственных часов. Он может определить, используете ли вы летнее время в вашем местоположении, но не для удаленного местоположения, созданного смещением:
newDateWithOffset = new Date(utc + (3600000*(offset)));
Это все равно будет неправильно и через час, если они находятся в летнее время. Вам нужна удаленная учетная запись времени, если они в настоящее время находятся внутри своего летнего времени или нет, и настройте их соответствующим образом. попробуйте рассчитать это и измените ваши часы на - скажем, 1/2/2015 и сбросьте часы на час назад, как если бы они находились вне летнего времени. Затем рассчитайте смещение для места, которое все еще должно отставать на 2 часа. Он покажет час впереди двухчасового окна. Вам все равно придется учесть час и отрегулировать. Я сделал это для Нью-Йорка и Денвера и всегда ошибаюсь (на час вперед) в Денвере.
Недавно мне нужно было создать строку даты с UTC и DST, и, основываясь на ответе Шелдона, я соединил это:
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: UTC+0300 (DST+0100)</p>
<hr>
Есть ли проблема с использованием Date.toString().indexOf('Daylight Time') > -1
"" + new Date()
Сб 01 января 100050 00:00:00 GMT-0500 (восточно-американское стандартное время)
"" + new Date(...)
Вс 1 мая 100033 00:00:00 GMT-0400 (восточное летнее время)
Кажется, это совместимо со всеми браузерами.