Чи завжди об’єкт дати JavaScript завжди один вихідний день?


238

У моєму додатку Java Script у мене зберігається дата у такому форматі:

2011-09-24

Тепер, коли я намагаюся використовувати вказане вище значення для створення нового об'єкта дати (щоб я міг отримати дату в іншому форматі), дата завжди повертається на один вихідний день. Дивись нижче:

var doo = new Date("2011-09-24");
console.log(doo);

журнали:

Fri Sep 23 2011 20:00:00 GMT-0400 (Eastern Daylight Time)

11
Клас дати Javascript не представляє дату, він представляє часову позначку (те саме на Java). Щоб визначити дату, він використовує часовий пояс, і це причина вашої проблеми. Він аналізує його часовий пояс GMT / UTC (24 вересня 2011, 00 : 00 по Грінвічу) , а потім виводить його з іншим часовим поясом 4 годин (23 Вер 2011, 20 : 00 GMT-0400).
Кодо

2
Я отримую "недійсну дату". Замініть символи '-' символами '/' та повторіть спробу. Або розділіть дату на її біти і встановіть компоненти окремо (якщо це зробити, відніміть 1 від числа місяця).
RobG

@Codo - так, хороша відповідь. Застосовується ECMA-262 15.9.1.15. ОП повинна використовувати "2011-09-24T20: 00: 00-04: 00" або подібне.
RobG

1
Я виявив, що формат "24 вересня 2011 року" поверне належну дату. Дивіться тут для пояснення: stackoverflow.com/questions/2587345/javascript-date-parse
christurnerio

Відповіді:


98

Зауважте, що східний літній час є, -4 hoursі що години в дату, яку ви повертаєтесь, - це 20.

20h + 4h = 24h

що опівночі 2011-09-24. Дату було проаналізовано в UTC (GMT), оскільки ви вказали рядок для дати без будь-якого індикатора часового поясу. Якби ви навели рядок дати / часу без індикатора ( new Date("2011-09-24T00:00:00")), він би був проаналізований у вашому локальному часовому поясі. (Історично там були невідповідності, не в останню чергу тому, що специфікація змінювалася не раз, але сучасні браузери повинні бути добре; або ви завжди можете включити індикатор часового поясу.)

Ви отримуєте потрібну дату, ви просто ніколи не вказали правильний часовий пояс.

Якщо вам необхідно отримати доступ до значень дати, ви можете використовувати getUTCDate()або будь-який з інших getUTC*()функцій :

var d,
    days;
d = new Date('2011-09-24');
days = ['Sun', 'Mon', 'Tues', 'Wed', 'Thurs', 'Fri', 'Sat'];
console.log(days[d.getUTCDay()]);

24
Як ви "вказуєте правильний часовий пояс"? Конструктор дат завжди інтерпретує рядок дати як UTC, але потім коригує часовий пояс. Я навіть можу зробити "нову дату" ("2012-01-02 EDT") і все одно переміщую її назад до попереднього дня завдяки застосуванню компенсації для економії літнього дня, що не має сенсу, тому що якби я сказав вам, це дата відповідно до поточний локальний часовий пояс EDT, тоді не застосовуйте до нього додаткове зміщення. Я сказав йому, що часовий пояс був EDT, але він все ще застосовує додатковий зсув, переміщуючи його на один день назад.
AaronLS

1
@AaronLS, EDTє перехід на літній час (також відомий як літній час) , ESTце часовий пояс , який застосовується в січні.
zzzzBov

1
Я б хотів зробити це як просто UTC з рядка, тому я запитав "Як ти" вказати правильний часовий пояс "?" щодо місця, де ви заявили, "ви просто ніколи не вказали правильний часовий пояс". Якщо я new Date('2012-01-01 GMT')це зробити, він все ще застосовує зміщення, оскільки воно перетворює його на місцевий час дати користувача.
AaronLS

7
@AaronLS, якщо ви змушені використовувати get*методи і вам потрібні, щоб повернути правильну дату / час, включаючи невідоме зміщення часового поясу, просто додайте до невідомого зміщення часового поясу: d = new Date('2013-01-08 GMT'); d.setMinutes(d.getMinutes() + d.getTimezoneOffset());це нормалізує дату в місцевості користувача, щоб .get*методи поверталися очікуване значення. Тоді .getUTC*методи будуть невірними, тому будьте обережні.
zzzzBov

1
Існує відмінність між визначенням поведінки для надання посилання та використанням її як виходу, коли виконавці не змогли щось правильно виконати. І це не те саме представлення, якщо дані не виправдовують очікування.
Дисидентська лють

252

Є кілька божевільних речей, які трапляються з об’єктом JS DATE, який перетворює рядки, наприклад, враховуйте наступну дату, яку ви вказали

Примітка . Наступні приклади можуть бути або не бути ОДНИМИ ВИМКНЕННЯМ залежно від ВАШОГО часового поясу та поточного часу.

new Date("2011-09-24"); // Year-Month-Day
// => Fri Sep 23 2011 17:00:00 GMT-0700 (MST) - ONE DAY OFF.

Однак, якщо ми переставимо формат рядка на місяць-день-рік ...

new Date("09-24-2011");
=> // Sat Sep 24 2011 00:00:00 GMT-0700 (MST) - CORRECT DATE.

Ще один дивний

new Date("2011-09-24");
// => Fri Sep 23 2011 17:00:00 GMT-0700 (MST) - ONE DAY OFF AS BEFORE.

new Date("2011/09/24"); // change from "-" to "/".
=> // Sat Sep 24 2011 00:00:00 GMT-0700 (MST) - CORRECT DATE.

Ми можемо легко змінити дефіси у вашу дату "2011-09-24" під час створення нової дати

new Date("2011-09-24".replace(/-/g, '\/')); // => "2011/09/24".
=> // Sat Sep 24 2011 00:00:00 GMT-0700 (MST) - CORRECT DATE.

Що робити, якщо у нас був рядок дати типу "2011-09-24T00: 00: 00"

new Date("2011-09-24T00:00:00");
// => Fri Sep 23 2011 17:00:00 GMT-0700 (MST) - ONE DAY OFF.

Тепер поміняйте дефіс на косу рису, як і раніше; Що станеться?

new Date("2011/09/24T00:00:00");
// => Invalid Date

Як правило, я повинен керувати форматом дати 2011-09-24T00: 00: 00, тому я це роблю.

new Date("2011-09-24T00:00:00".replace(/-/g, '\/').replace(/T.+/, ''));
// => Sat Sep 24 2011 00:00:00 GMT-0700 (MST) - CORRECT DATE.

ОНОВЛЕННЯ

Якщо ви надаєте окремі аргументи конструктору Date, ви можете отримати інші корисні результати, як описано нижче

Примітка: аргументи можуть бути типу Number або String. Я покажу приклади зі змішаними значеннями.

Отримайте перший місяць та день заданого року

new Date(2011, 0); // Normal behavior as months in this case are zero based.
=> // Sat Jan 01 2011 00:00:00 GMT-0700 (MST)

Отримайте останній місяць та день року

new Date((2011 + 1), 0, 0); // The second zero roles back one day into the previous month's last day.
=> // Sat Dec 31 2011 00:00:00 GMT-0700 (MST)

Приклад аргументів числа, рядків. Зауважте, що місяць березень, оскільки знову нульовий місяць

new Date(2011, "02"); 
=> // Tue Mar 01 2011 00:00:00 GMT-0700 (MST)

Якщо ми робимо те саме, але з нульовим днем, ми отримуємо щось інше.

new Date(2011, "02", 0); // again the zero roles back from March to the last day of February.
=> // Mon Feb 28 2011 00:00:00 GMT-0700 (MST)

Якщо додати день нуля до будь-якого аргументу року та місяця, ви отримаєте останній день попереднього місяця. Якщо ви продовжите негативні цифри, ви можете продовжити відкат ще один день

new Date(2011, "02", -1);
=> // Sun Feb 27 2011 00:00:00 GMT-0700 (MST)

12
Це насправді допомогло мені найкращим чином. Щойно я додав .replace (/ - / g, '\ /'). Substitute (/ T. + /, '') До кінця своїх даних, коли я вносив нову дату. Супер легко!
Девін Преджан

64
Нічого собі - javascript дуже жахливий.
psparrow

2
Сьогодні ввечері дзвінки .replace () дійсно допомогли.
скасувати

2
так, я перевірив це все, і це дійсно дивно.
chintan adatiya

26
Все це пов’язано з поведінкою базового Date.parse (), що намагається дотримуватися ISO 8601. Коли рядок дат відповідає формату "yyyy-mm-dd", передбачається, що це ISO 8601 з неявним UTC 00:00. Коли рядок відхиляється від формату (наприклад, mm-dd-yyyy або косою рискою замість дефісу), вона повертається до слабшого аналізатора згідно з RFC 2822, який використовує місцевий час, коли часовий пояс відсутній. Справді, все це буде досить нецензурно для пересічної людини.
Мізстик

71

Для нормалізації дати та усунення небажаного зміщення (перевірено тут: https://jsfiddle.net/7xp1xL5m/ ):

var doo = new Date("2011-09-24");
console.log(  new Date( doo.getTime() + Math.abs(doo.getTimezoneOffset()*60000) )  );
// Output: Sat Sep 24 2011 00:00:00 GMT-0400 (Eastern Daylight Time)

Це також забезпечує те ж саме і кредит @tpartee (перевірено тут: https://jsfiddle.net/7xp1xL5m/1/ ):

var doo = new Date("2011-09-24");
console.log( new Date( doo.getTime() - doo.getTimezoneOffset() * -60000 )  );

Це зробило це для мене - мені довелося працювати з датами API, потім сортувати / порівнювати їх, тому просто додавання зміщення часового поясу найкраще працює.
чакеда

Це працювало для мене, за винятком того, що мені довелося відняти timeZoneOffset, а не додавати його
ErikAGriffin

@ErikAGriffin Ви знаходитесь у позитивному часовому поясі, IE GMT + 0X00 замість GMT-0X00?
AaronLS

Я зробив щось подібне:doo.setMinutes(doo.getMinutes() + doo.getTimezoneOffset())
Людина дисидентів

2
@AaronLS Ви були на правильному шляху, але трохи неправильна логіка. Для компенсації зміщення TZ правильною логікою є: console.log (нова дата (doo.getTime () - doo.getTimezoneOffset () * -60000)); - Знак зміщення важливий і не може бути усунутий, але вам також потрібен зворотний знак для виправлення, тому ми помножимо зміщення на -60000, щоб застосувати зворотний знак.
tpartee

28

Якщо ви хочете отримати годину 0 певної дати в локальному часовому поясі, передайте окремі частини дати Dateконструктору.

new Date(2011,08,24); // month value is 0 based, others are 1 based.

26

Просто хочу додати, що, мабуть, додавання пробілу в кінці рядка буде використовувати UTC для створення.

new Date("2016-07-06")
> Tue Jul 05 2016 17:00:00 GMT-0700 (Pacific Daylight Time)

new Date("2016-07-06 ")
> Wed Jul 06 2016 00:00:00 GMT-0700 (Pacific Daylight Time)

Редагувати: це не рекомендоване рішення, а альтернативна відповідь. Будь ласка, не використовуйте цей підхід, оскільки дуже незрозуміло, що відбувається. Існує декілька способів, коли хтось може змінити це випадково, викликаючи помилку.


Для прикладу з пробілом в кінці, консоль повертає "Недійсна дата = $ 2"
Brian Risk

1
працює дуже добре! нова дата (data.Date + "") .toLocaleDateString ("en-US")
Nakres

Чесно кажучи, це була лише альтернативна відповідь і не є рекомендованим рішенням.
Кайл Шрадер

25

Я вважаю, що це стосується коригування часового поясу. Дата, яку ви створили, є в GMT, а час за замовчуванням - півночі, але ваш часовий пояс - EDT, тому вона віднімає 4 години. Спробуйте це підтвердити:

var doo = new Date("2011-09-25 EDT");

3
Тут найкраща відповідь. + 1 мільйон на використання вбудованих рядків локалізації часових поясів замість програмного перетворення.
blearn

2
Цей допомагає. Він працює так: $ range.dat = нова дата (datestring + 'EDT'). Зверніть увагу на різницю між EDT та EST: посилання
Weihui Guo

Я не знаю, що відбувається за лаштунками, але це працює чудово.
the_haystacker

Це, я б сказав, є найбільш спрощеним з відповідей і менш переформатуванням коду.
Майкл

Дякую, я вважаю найкращий спосіб для мене.
Джандерсон Константіно

11

Ваша проблема спеціально з часовим поясом. Зверніть увагу на частину GMT-0400- це на 4 години відставання від GMT. Якщо додати до відображеної дати / часу 4 години, ви отримаєте рівно півночі 2011/09/24. Використовуйте toUTCString()метод замість цього, щоб отримати рядок GMT:

var doo = new Date("2011-09-24");
console.log(doo.toUTCString());

7

Це, мабуть, не є гарною відповіддю, але я просто хочу поділитися своїм досвідом з цим питанням.

У моєму додатку використовується глобальна дата у форматі "YYYY-MM-DD", тоді як плагін datapicker я використовую лише js дата, мені важко розглянути як utc, так і js. Тож, коли я хочу передати дату, відформатовану "РРРР-ММ-ДД", вперше конвертую її у формат "ММ / ДД / РРРР", використовуючи moment.js або все, що завгодно, і дата відображається на виборі дат зараз правильно. Для вашого прикладу

var d = new Date('2011-09-24'); // d will be 'Fri Sep 23 2011 20:00:00 GMT-0400 (EDT)' for my lacale
var d1 = new Date('09/24/2011'); // d1 will be 'Sat Sep 24 2011 00:00:00 GMT-0400 (EDT)' for my lacale

Мабуть, d1 - це те, чого я хочу. Сподіваюся, що це буде корисним для деяких людей.


1
Просто перемикання форматів, як ви, працювало так само і для мене. Так дивно.
jAC

5

Це через мене для циклу, +1 на відповідь zzzBov. Ось повна конверсія дати, яка працювала на мене за допомогою методів UTC:

//myMeeting.MeetingDate = '2015-01-30T00:00:00'

var myDate = new Date(myMeeting.MeetingDate);
//convert to JavaScript date format
//returns date of 'Thu Jan 29 2015 19:00:00 GMT-0500 (Eastern Standard Time)' <-- One Day Off!

myDate = new Date(myDate.getUTCFullYear(), myDate.getUTCMonth(), myDate.getUTCDate());
//returns date of 'Fri Jan 30 2015 00:00:00 GMT-0500 (Eastern Standard Time)' <-- Correct Date!

4

Це означає 2011-09-24 00:00:00 GMT, і оскільки ви вдома GMT -4, це буде 20:00попередній день.

Особисто я отримую 2011-09-24 02:00:00, бо живу в GMT +2.


3

Хоча у випадку ОП часовим поясом є EDT, немає гарантії, що користувач, який виконує ваш сценарій, буде в часовому поясі EDT, тому жорстке кодування зміщення не обов'язково спрацює. Я знайшов рішення, розбиває рядок дати та використовує окремі значення в конструкторі дат.

var dateString = "2011-09-24";
var dateParts = dateString.split("-");
var date = new Date(dateParts[0], dateParts[1] - 1, dateParts[2]);

Зауважте, що вам доводиться рахувати ще одну частину дивацтва JS: місяць базується на нулі.


2

З цією проблемою я стикався, коли мій клієнт був у Атлантичний стандартний час. Значення дати, яку отримав клієнт, було "2018-11-23", і коли код передав його у new Date("2018-11-23")вихід для клієнта, був за попередній день. Я створив функцію утиліти, як показано в фрагменті, який нормалізував дату, давши клієнтові очікувану дату.

date.setMinutes(date.getMinutes() + date.getTimezoneOffset());

var normalizeDate = function(date) {
  date.setMinutes(date.getMinutes() + date.getTimezoneOffset());
  return date;
};

var date = new Date("2018-11-23");

document.getElementById("default").textContent = date;
document.getElementById("normalized").textContent = normalizeDate(date);
<h2>Calling new Date("2018-11-23")</h2>
<div>
  <label><b>Default</b> : </label>
  <span id="default"></span>
</div>
<hr>
<div>
  <label><b>Normalized</b> : </label>
  <span id="normalized"></span>
</div>


2

якщо ви просто хочете переконатися, що окремі частини дати залишаються однаковими для цілей відображення, * це, здається, працює, навіть коли я змінюю часовий пояс:

var doo = new Date("2011-09-24 00:00:00")

просто додайте нулі туди.

У своєму коді я роблю це:

let dateForDisplayToUser = 
  new Date( `${YYYYMMDDdateStringSeparatedByHyphensFromAPI} 00:00:00` )
  .toLocaleDateString( 
    'en-GB', 
    { day: 'numeric', month: 'short', year: 'numeric' }
  )

І я перемикаю свій часовий пояс на своєму комп’ютері, і дата залишається такою ж, як рядок дати yyyy-mm-dd, що я отримую від API.

Але я щось пропускаю / це погана ідея?

* принаймні в хромі. Це не працює в Safari! станом на це написання


Коли ви це зробите .toISOString(), він проходить 1 день назад.
vivek_23

new Date('2019/11/18 05:30:00').toISOString();працював на мене
vivek_23

1

Найкращий спосіб впоратися з цим, не використовуючи більше методів перетворення,

 var mydate='2016,3,3';
 var utcDate = Date.parse(mydate);
 console.log(" You're getting back are 20.  20h + 4h = 24h :: "+utcDate);

Тепер просто додайте GMT ​​у свою дату, або ви можете додати її.

 var  mydateNew='2016,3,3'+ 'GMT';
 var utcDateNew = Date.parse(mydateNew);
 console.log("the right time that you want:"+utcDateNew)

Наживо: https://jsfiddle.net/gajender/2kop9vrk/1/


1

Я стикався з таким питанням, як це. Але мою проблему було знято під час отримання дати з бази даних.

про це йдеться у базі даних, і це у форматі UTC.

2019-03-29 19: 00: 00.0000000 +00: 00

Тож, коли я отримую з бази даних і перевіряю дату, вона додає зсув з неї і повертаюсь назад до JavaScript.

введіть тут опис зображення

Він додає +05: 00, оскільки це часовий пояс мого сервера. Мій клієнт перебуває в іншому часовому поясі +07: 00.

2019-03-28T19: 00: 00 + 05: 00 // це те, що я отримую в JavaScript.

Тож ось моє рішення, що я роблю з цим питанням.

var dates = price.deliveryDate.split(/-|T|:/);
var expDate = new Date(dates[0], dates[1] - 1, dates[2], dates[3], dates[4]);
var expirationDate = new Date(expDate);

Отже, коли дата надходить із сервера і має зміщення сервера, то я розділяю дату і видаляю зсув сервера, а потім перетворюю на дату. Це вирішує моє питання.



0

Ви використовуєте формат рядка рядків дати ISO, який, згідно з цією сторінкою , спричиняє побудову дати за часовим поясом UTC:

Примітка: синтаксичний аналіз рядків дат із конструктором дат (і Date.parse, вони еквівалентні) сильно не рекомендується через різниці та невідповідності браузера. Підтримка рядків формату RFC 2822 здійснюється лише за умовами. Підтримка форматів ISO 8601 відрізняється тим, що рядки лише для дати (наприклад, "1970-01-01") трактуються як UTC, а не локальні.

Якщо ви форматуєте текст інакше, наприклад "Jan 01 1970", тоді (принаймні, на моїй машині) він використовує ваш локальний часовий пояс.


0

Намагаюся додати свої 2 центи до цього потоку (детальніше про відповідь @ paul-wintz).

Мені здається, що коли конструктор Date отримує рядок, що відповідає першій частині формату ISO 8601 (частина дати), він робить точне перетворення дати у часовому поясі UTC за 0 разів. Коли ця дата перетворена на місцевий час, можливий зсув дати, якщо опівночі UTC є більш ранньою датою в місцевому часовому поясі.

new Date('2020-05-07')
Wed May 06 2020 20:00:00 GMT-0400 (Eastern Daylight Time)

Якщо рядок дати має будь-який інший формат "звільненіший" (використовує "/" або дата / місяць не зафіксований нулем), він створює дату в локальному часовому поясі, таким чином, жодних проблем зі зміною дати не виникає.

new Date('2020/05/07')
Thu May 07 2020 00:00:00 GMT-0400 (Eastern Daylight Time)
new Date('2020-5-07')
Thu May 07 2020 00:00:00 GMT-0400 (Eastern Daylight Time)
new Date('2020-5-7')
Thu May 07 2020 00:00:00 GMT-0400 (Eastern Daylight Time)
new Date('2020-05-7')
Thu May 07 2020 00:00:00 GMT-0400 (Eastern Daylight Time)

Тоді одним із швидких виправлень, як згадувалося вище, є заміна "-" на "/" у вашому рядку "Дата" лише у форматі ISO.

new Date('2020-05-07'.replace('-','/'))
Thu May 07 2020 00:00:00 GMT-0400 (Eastern Daylight Time)

0

Зберігаючи yyyy-mm-ddу форматі дати MySql, ви повинні зробити наступне:

const newDate = new Date( yourDate.getTime() + Math.abs(yourDate.getTimezoneOffset()*60000) );
console.log(newDate.toJSON().slice(0, 10)); // yyyy-mm-dd

-1

Ваш журнал виводить GMT, щоб ви хочете вказати свій часовий пояс:

var doo = new Date("2011-09-24 EST");

3
На це запитання вже є багато відповідей. Якщо ви все одно хочете відповісти на запитання, вам слід згадати щось нове, чого не відповідають інші відповіді. Верхня відповідь вже пояснює проблему з часовим поясом значно детальніше.
Келвін Годфрі

Ця відповідь майже точно така ж, як stackoverflow.com/a/7556642/8828658
кам'яний павукоподібний

Кому байдуже, чи пояснення його більш глибоке ... його код все ще відрізняється від мого. Шахта набагато простіша. І так, кам'яний павукоподібний ти прав. Моє ліжко.
bmacx7

-3

ніколи не пам’ятаю, не помітив GMT -0400, що спричиняє дату вчора

Ви можете спробувати встановити за замовчуванням "час", який буде 12:00:00


Так, я трохи поспішив зі своєю відповіддю, вибач за це. Переглянув мою відповідь.
ChrisH

-3

Наступне працювало для мене -

    var doo = new Date("2011-09-24").format("m/d/yyyy");

Це все одно не працюватиме, доки ви не поспішаєте з часовим поясом.
Гар
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.