Как инициализировать дату JavaScript для определенного часового пояса
У меня есть дата в определенном часовом поясе в виде строки, и я хочу преобразовать ее в местное время. Но я не знаю, как установить часовой пояс в объекте Date.
Например, у меня есть Feb 28 2013 7:00 PM ET,
тогда я могу
var mydate = new Date();
mydate.setFullYear(2013);
mydate.setMonth(02);
mydate.setDate(28);
mydate.setHours(7);
mydate.setMinutes(00);
Насколько я знаю, я могу установить время UTC или местное время. Но как мне установить время в другом часовом поясе?
Я пытался использовать сложение / вычитание смещения от UTC, но я не знаю, как противостоять переходу на летнее время. Я не уверен, что направляюсь в правильном направлении.
Как я могу конвертировать время из другого часового пояса в местное время в JavaScript?
23 ответа
Фон
в JavaScript Date
объект отслеживает время в UTC внутри, но обычно принимает ввод и вывод по местному времени компьютера, на котором он работает. Он не имеет никаких средств для работы со временем в других часовых поясах. Вы можете анализировать и выводить даты в формате UTC или Local, но вы не можете напрямую работать с другими часовыми поясами.
Чтобы быть абсолютно точным, внутреннее представление Date
Объект представляет собой одно число, представляющее количество миллисекунд, прошедших с момента 1970-01-01 00:00:00 UTC
без учета високосных секунд. В самом объекте Date нет часового пояса или формата строки. Когда различные функции Date
объект используется, локальный часовой пояс компьютера применяется к внутреннему представлению. Если функция создает строку, то информация о локали компьютера может быть принята во внимание, чтобы определить, как создать эту строку. Детали варьируются в зависимости от функции, а некоторые зависят от реализации.
Библиотеки
К счастью, есть библиотеки, которые могут это сделать. Они реализуют стандартную базу данных часовых поясов Olson/IANA на JavaScript. Некоторые из них имеют накладные расходы, если вы работаете в веб-браузере, так как база данных может стать немного большой, если вы хотите всего этого. К счастью, многие из этих библиотек позволяют вам выборочно выбирать зоны, которые вы хотите поддерживать, делая размер данных намного более приемлемым. Также некоторые используют современные функции для получения данных о часовых поясах из Intl
API вместо того, чтобы отправлять его самостоятельно.
Для этого есть несколько библиотек:
- Luxon (преемник Moment.js)
- момент-часовой пояс (расширение для Moment.js)
- js-joda (js-порт Java ThreeTen)
- date-fns-tz / date-fns-timezone (расширения для date-fns)
- BigEasy / TimeZone
WallTime-js(прекращено)- TimeZoneJS
- tz.js
Luxon - это, пожалуй, самая безопасная ставка для всего современного использования, и это самый легкий вес, поскольку он использует Intl
API для данных о часовых поясах.
Moment-timezone является расширением для moment.js и предоставляет собственные данные о часовых поясах.
js-joda - это реализация JavaScript Joda-Time API (из Java), включающая поддержку часовых поясов через отдельный модуль.
date-fns-tz - это расширение для date-fns 2.x. date-fns-timezone является расширением для date-fns 1.x.
BigEasy / TimeZone также, кажется, находится на правильном пути.
У WallTime-js закончился срок эксплуатации, и владельцы мигрируют в зону времени и времени.
TimeZoneJS был самым длинным, но, как известно, имеет некоторые давние ошибки, особенно при переходах на летнее время. Надеюсь, они будут исправлены в будущем.
tz.js также был в течение некоторого времени, но не очень хорошо задокументирован, IMHO.
Вы должны оценить эти библиотеки, чтобы увидеть, какие из них будут соответствовать вашим потребностям. Если вы не уверены, перейдите с моментом / моментом-часовым поясом.
Встроенная поддержка в современных браузерах
Если вы можете ограничить свое использование современными веб-браузерами, теперь вы можете делать следующее без каких-либо специальных библиотек:
new Date().toLocaleString("en-US", {timeZone: "America/New_York"})
Это не всеобъемлющее решение, но оно работает для многих сценариев, которые требуют только преобразования выходных данных (из UTC или местного времени в определенный часовой пояс, но не в другом направлении). Это часть API интернационализации ECMAScript (ECMA-402). Смотрите этот пост для более подробной информации. Эта таблица совместимости отслеживает, какие версии поддерживаются. Это Intl
API упомянул выше, что некоторые библиотеки сейчас используются внутри компании.
Будущие предложения
Целью временного предложения TC39 является предоставление нового набора стандартных объектов для работы с датами и временем на самом языке JavaScript. Это будет включать поддержку объекта, осведомленного о часовом поясе.
Как сказал Мэтт Джонсон
Если вы можете ограничить свое использование современными веб-браузерами, теперь вы можете делать следующее без каких-либо специальных библиотек:
new Date().toLocaleString("en-US", {timeZone: "America/New_York"})
Это не комплексное решение, но оно работает для многих сценариев, которые требуют только преобразования выходных данных (из UTC или местного времени в определенный часовой пояс, но не в другом направлении).
Таким образом, хотя браузер не может считывать часовые пояса IANA при создании даты или имеет какие-либо методы для изменения часовых поясов в существующем объекте Date, кажется, что есть взлом:
function changeTimezone(date,ianatz) {
// suppose the date is 12:00 UTC
var invdate = new Date(date.toLocaleString('en-US', {
timeZone: ianatz
}));
// then invdate will be 07:00 in Toronto
// and the diff is 5 hours
var diff = date.getTime()-invdate.getTime();
// so 12:00 in Toronto is 17:00 UTC
return new Date(date.getTime()+diff);
}
Это должно решить вашу проблему, не стесняйтесь предлагать исправления. Этот метод также будет учитывать переход на летнее время для данной даты.
dateWithTimeZone = (timeZone, year, month, day, hour, minute, second) => {
let date = new Date(Date.UTC(year, month, day, hour, minute, second));
let utcDate = new Date(date.toLocaleString('en-US', { timeZone: "UTC" }));
let tzDate = new Date(date.toLocaleString('en-US', { timeZone: timeZone }));
let offset = utcDate.getTime() - tzDate.getTime();
date.setTime( date.getTime() + offset );
return date;
};
Как использовать с часовым поясом и местным временем:
dateWithTimeZone("America/Los_Angeles",2019,8,8,0,0,0)
Вы можете указать смещение часового пояса на new Date()
, например:
new Date('Feb 28 2013 19:00:00 EST')
или же
new Date('Feb 28 2013 19:00:00 GMT-0500')
поскольку Date
хранить время UTC (т.е. getTime
возвращается в UTC), javascript преобразует время в UTC, а когда вы вызываете такие вещи, как toString
javascript преобразует время UTC в местный часовой пояс браузера и возвращает строку в местном часовом поясе, т.е. если я использую UTC+8
:
> new Date('Feb 28 2013 19:00:00 GMT-0500').toString()
< "Fri Mar 01 2013 08:00:00 GMT+0800 (CST)"
Также вы можете использовать обычный getHours/Minute/Second
метод:
> new Date('Feb 28 2013 19:00:00 GMT-0500').getHours()
< 8
(Это 8
значит, после того, как время переведено в мое местное время - UTC+8
номер часов 8
,)
Я нашел наиболее поддерживаемый способ сделать это, не беспокоясь о сторонней библиотеке, используя getTimezoneOffset
чтобы рассчитать соответствующую временную метку или обновить время, затем используйте обычные методы для получения необходимой даты и времени.
var mydate = new Date();
mydate.setFullYear(2013);
mydate.setMonth(02);
mydate.setDate(28);
mydate.setHours(7);
mydate.setMinutes(00);
// ET timezone offset in hours.
var timezone = -5;
// Timezone offset in minutes + the desired offset in minutes, converted to ms.
// This offset should be the same for ALL date calculations, so you should only need to calculate it once.
var offset = (mydate.getTimezoneOffset() + (timezone * 60)) * 60 * 1000;
// Use the timestamp and offset as necessary to calculate min/sec etc, i.e. for countdowns.
var timestamp = mydate.getTime() + timezone_offset,
seconds = Math.floor(timestamp / 1000) % 60,
minutes = Math.floor(timestamp / 1000 / 60) % 60,
hours = Math.floor(timestamp / 1000 / 60 / 60);
// Or update the timestamp to reflect the timezone offset.
mydate.setTime(mydate.getTime() + timezone_offset);
// Then Output dates and times using the normal methods.
var date = mydate.getDate(),
hour = mydate.getHours();
РЕДАКТИРОВАТЬ
Я ранее использовал UTC
методы при выполнении преобразований даты, что было неверно. С добавлением смещения ко времени, используя местный get
функции вернут желаемые результаты.
Для пользователей Ionic я был хрен с этим, потому что .toISOString()
должен использоваться с шаблоном html.
Это захватит текущую дату, но, конечно, может быть добавлен к предыдущим ответам для выбранной даты.
Я исправил это с помощью этого:
date = new Date();
public currentDate: any = new Date(this.date.getTime() - this.date.getTimezoneOffset()*60000).toISOString();
*60000 указывает UTC -6, который является CST, поэтому, какой бы часовой пояс ни потребовался, число и разницу можно изменить.
Я столкнулся с этой проблемой при работе с облачной функцией GCP. Конечно, это работает на локальной машине, но работа в облаке делает ОС по умолчанию (локальной) для
new Date()
не имеющий отношения. В моем случае для вызова API из облака требовалось восточное стандартное время в формате ISO (без «Z») со смещением «-0500» или «-0400» в зависимости от летнего времени, например:
2021-12-01T00:00:00.000-0500
Опять же, это не проблема форматирования браузера, поэтому я вынужден использовать этот формат, чтобы вызов API работал правильно.
Используя код @chickens в качестве начала, вот что сработало:
var date = new Date();
var now_utc = Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(),
date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds());
var dt = new Date(now_utc);
let utcDate = new Date(dt.toLocaleString('en-US', { timeZone: "UTC" }));
let tzDate = new Date(dt.toLocaleString('en-US', { timeZone: "America/New_York" }));
let offset1 = utcDate.getTime() - tzDate.getTime();
let offset2 = offset1/60000;
let o1 = Math.abs(offset2);
console.log(offset2)
var offsetValue1 = (offset2 < 0 ? "+" : "-") + ("00" + Math.floor(o1 / 60)).slice(-2) + ("00" + (o1 % 60)).slice(-2);
console.log(offsetValue1)
dt.setTime(dt.getTime() - offset1);
console.log(dt.toISOString());
console.log(dt.toISOString().slice(0,-1)+offsetValue1);
Я столкнулся с аналогичной проблемой с модульными тестами (особенно в шутку, когда модульные тесты запускаются локально для создания снимков, а затем сервер CI запускается (потенциально) в другом часовом поясе, что приводит к сбою сравнения снимков). Я издевался над нашими Date
и некоторые из вспомогательных методов, например, так:
describe('...', () => {
let originalDate;
beforeEach(() => {
originalDate = Date;
Date = jest.fn(
(d) => {
let newD;
if (d) {
newD = (new originalDate(d));
} else {
newD = (new originalDate('2017-05-29T10:00:00z'));
}
newD.toLocaleString = () => {
return (new originalDate(newD.valueOf())).toLocaleString("en-US", {timeZone: "America/New_York"});
};
newD.toLocaleDateString = () => {
return (new originalDate(newD.valueOf())).toLocaleDateString("en-US", {timeZone: "America/New_York"});
};
newD.toLocaleTimeString = () => {
return (new originalDate(newD.valueOf())).toLocaleTimeString("en-US", {timeZone: "America/New_York"});
};
return newD;
}
);
Date.now = () => { return (Date()); };
});
afterEach(() => {
Date = originalDate;
});
});
У меня была та же проблема , но мы можем использовать часовой пояс , мы хотим ,
мы используем
.toLocaleDateString()
например:
Я знаю, что это на 3 года слишком поздно, но, возможно, это может помочь кому-то еще, потому что я не нашел ничего подобного, за исключением библиотеки моментов и часовых поясов, которая не совсем совпадает с тем, что он просит здесь.
Я сделал нечто подобное для немецкого часового пояса, это немного сложно из-за летнего времени и високосных лет, когда у вас есть 366 дней.
может потребоваться небольшая работа с функцией "isDaylightSavingTimeInGermany", в то время как разные часовые пояса меняются в разное время летнего времени.
в любом случае, проверьте эту страницу: https://github.com/zerkotin/german-timezone-converter/wiki
Основными методами являются: convertLocalDateToGermanTimezone convertGermanDateToLocalTimezone
Я приложил усилия к его документированию, так что это не будет так запутанно.
Попробуйте использовать ctoc из npm. https://www.npmjs.com/package/ctoc_timezone
Он имеет простую функциональность для изменения часовых поясов (большинство часовых поясов около 400) и всех пользовательских форматов, которые вы хотите отображать.
Основываясь на приведенных выше ответах, я использую этот собственный лайнер для преобразования строки с длинным часовым поясом в трехбуквенную строку:
var longTz = 'America/Los_Angeles';
var shortTz = new Date().
toLocaleString("en", {timeZoneName: "short", timeZone: longTz}).
split(' ').
pop();
Это даст вам PDT или PST в зависимости от указанной даты. В моем конкретном случае использования, разрабатываемого в Salesforce (Aura/Lightning), мы можем получить часовой пояс пользователя в длинном формате из бэкэнда.
Я не уверен, почему все эти ответы такие сложные. Просто используйтеYYYY-MM-DD ZZZ
при создании даты только для даты в местном/желаемом часовом поясе.
Создайте локальную дату:
var myDate = new Date('2022-11-29 CST')
Дата будет храниться в хранилище как UTC, отлично.
Получите дату из хранилища и отобразите ее как локальную:
myDate.toLocaleDateString()
29.11.2022
чтобы решить аналогичную проблему, я написал функцию, которая принимает дату типа String, например
2021-09-21T19:00:00.000+00:00
как вход.
Попробуйте: date-from-timezone, он разрешает ожидаемую дату с помощью нативно доступного Intl.DateTimeFormat
,
Я использовал этот метод в одном из своих проектов уже несколько лет, но теперь я решил опубликовать его как небольшой проект ОС:)
Здесь есть несколько рабочих ответов, но каким-то образом многие из них, похоже, привели вас к строке, но не к объекту даты, с которого вы начали, поэтому вот мой простой нефункциональный подход к тому, как изменить часовой пояс на дату JS:
var TZ='Australia/Brisbane'; //Target timezone from server
var date = new Date(); //Init this to a time if you don't want current time
date=new Date(Date.parse(date.toLocaleString("en-US", {timeZone: TZ})));
//Just a clarification on what happens
// 1) First new Date() gives you a Date object at current time in the clients browser local timezone
// 2) .toLocaleString takes that time, and returns a string if time in the target timezone
// 3) Date.parse converts that new string to a Unix epoch number
// 4) new Date() converts the Unix epoch into a Date object in the new TimeZone.
// Now I can use my usual getHours and other Date functions as required.
Надеюсь, что это поможет другим (если вы дойдете до этого нижнего ответа!)
Может это тебе поможет
/**
* Shift any Date timezone.
* @param {Date} date - Date to update.
* @param {string} timezone - Timezone as `-03:00`.
*/
function timezoneShifter(date, timezone) {
let isBehindGTM = false;
if (timezone.startsWith("-")) {
timezone = timezone.substr(1);
isBehindGTM = true;
}
const [hDiff, mDiff] = timezone.split(":").map((t) => parseInt(t));
const diff = hDiff * 60 + mDiff * (isBehindGTM ? 1 : -1);
const currentDiff = new Date().getTimezoneOffset();
return new Date(date.valueOf() + (currentDiff - diff) * 60 * 1000);
}
const _now = new Date()
console.log(
[
"Here: " + _now.toLocaleString(),
"Greenwich: " + timezoneShifter(_now, "00:00").toLocaleString(),
"New York: " + timezoneShifter(_now, "-04:00").toLocaleString(),
"Tokyo: " + timezoneShifter(_now, "+09:00").toLocaleString(),
"Buenos Aires: " + timezoneShifter(_now, "-03:00").toLocaleString(),
].join('\n')
);
Попробуйте что-то вроде этого,
public static getTimezoneOffset(timeZone: string, date = new Date()): number {
const localDate = date.toLocaleString('fr', { timeZone, timeZoneName: 'long' });
const tz = localDate.split(' ');
const TZ = localDate.replace(tz[0], '').replace(tz[1], '').replace(' ', '');
const dateString = date.toString();
const offset = (Date.parse(`${dateString} UTC`) - Date.parse(`${dateString}${TZ}`)) / (3600 * 1000);
return offset;
}
Если у вас возникли проблемы с
toISOString()
попробуйте вот так, используя'UTC'
вnew Date()
метод:
let start = new Date();
let event = new Date(`${start} UTC`);
event.setUTCHours(0, 0, 0, 0);
console.log(event.toISOString()); //2023-10-30T00:00:00.000Z
Просто с поддержкой Node.JS
Передайте количество часов, в течение которых ваш часовой пояс смещен от UTC.
function initDateInTimezone(offsetHours) {
const timezoneOffsetInMS = offsetHours * 60 * 60000;
let d = new Date().getTimezoneOffset() * 60000 + timezoneOffsetInMS;
const date = new Date(new Date().getTime() - d);
return date
}
//For mumbai time diffrence is 5.5 hrs so
// city_time_diff=5.5 (change according to your city)
let timee= Date.now()
timee=timee+(3600000*city_time_diff); //Add our city time (in msec)
let new_date=new Date(timee)
console.log("My city time is: ",new_date);
У меня была та же проблема, что и у ОП, которая привела меня к этой теме и многим другим. Ни один из ответов не смог «инициализировать дату JavaScript для определенного часового пояса».
В моем сценарии пользователь выбирал дату и часовой пояс (для встречи), поэтому, будь то отметка времени Unix, UTC и т. д., необходимо, чтобы этот dateTime находился в заданном часовом поясе, не зная смещения от местного времени пользователя. Пример пользователя находится в ? locale необходимо назначить встречу на 10:30 по центральному стандартному времени, не зная смещения от местного времени. Удивительно сложно установить определенную дату и время из другой даты.
Следующее решение создаст экземпляр объекта dateTime в нелокальном часовом поясе или преобразует объект dateTime в другой часовой пояс, сохраняя при этом время, в которое он был первоначально установлен. Он совместим со всеми современными браузерами (и довольно прилично), не делает ничего сомнительного с API браузера и принимает строки дат IANA.
const setDateInAnotherTimeZone=(inputDateTime, timeZone)=>{
/**
* This funciton will instantiate a dateTime object in a non local timezone
* or
* Converts a dateTime object to a different timezone, while maintining the time
* Parameters
** inputDateTime, a date object with the desired time and day selected (in the local/non-desired timezone)
*** Example: new Date(2023, 10, 10, 10, 30) 10:30 (localalized to user timezone) on 10/30/2023
** timeZone, the desired timezone you want to set the date in.
*** Example: var timeZone = 'US/Central'
*** Requires a 'tz database time zone' name aka an IANA timezone name
*** Usefull list here https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
*** Date.prototype.toLocaleString() usng IANA time zone names is widely supported
*** https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleString#browser_compatibility
*/
console.log('desired Time Zone',timeZone)
console.log('desired Time ', inputDateTime.toString().split(" ").slice(4,5).join(' ').toString())
console.log('inputDateTime', inputDateTime)
const dateTZShifted = inputDateTime.toLocaleString("en-US", {timeZone : timeZone , dateStyle: 'long', timeStyle: 'long'})
let timeTZShifted = dateTZShifted.split(" ").slice(4,5).join(' ').toString().split(':')
var originalTime = inputDateTime.toString().split(" ").slice(4,5).join(' ').toString().split(':')
let newLocalTime = [
Number(originalTime[0]) - Number(timeTZShifted[0]) + Number(originalTime[0]) ,
Number(originalTime[1]) - Number(timeTZShifted[1]) + Number(originalTime[1]) ,
Number(originalTime[2]) -Number(timeTZShifted[2]) + Number(originalTime[2])
]
let outputDate = new Date(inputDateTime)
outputDate.setHours(Number(newLocalTime[0]), Number(newLocalTime[1]),Number(newLocalTime[2]))
console.log('outputDateTime ', outputDate.toLocaleString("en-US", {timeZone : timeZone , dateStyle: 'long', timeStyle: 'long'}) )
return(outputDate)
}
//Usage
let dateInAnotherTimeZone = setDateInAnotherTimeZone(new Date(2023, 7, 10, 10, 30),'US/Central')
let dateInAnotherTimeZone1 = setDateInAnotherTimeZone(DateObject,'America/New_York')
Ожидаемый результат
желаемый часовой пояс США/Центральный
желаемое время 10:30:00
вводдатавремя
Четверг, 10 августа 2023 г., 10:30:00 GMT-0400 (восточное летнее время)
выходДатаВремя
10 августа 2023 г., 10:30:00 CDT
или
Четверг, 10 августа 2023 г., 11:30:00 GMT-0400 (восточное летнее время)
Столкнулся с той же проблемой, использовал этот
Console.log(Date.parse("13 июня 2018 10:50:39 GMT+1"));
Он вернет миллисекунды, на которые вы можете проверить, если +100 к Тимзоне инициализировать британское время. Надеюсь, это поможет!!