Czy obiekt JavaScript zawsze ma jeden dzień wolny?


238

W mojej aplikacji Java Script mam datę zapisaną w takim formacie:

2011-09-24

Teraz, gdy próbuję użyć powyższej wartości, aby utworzyć nowy obiekt Date (dzięki czemu mogę pobrać datę w innym formacie), data zawsze wraca o jeden dzień wolny. Patrz poniżej:

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

logi:

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

11
Klasa Date w Javascript nie reprezentuje daty, reprezentuje znacznik czasu (taki sam w Javie). Aby była to data, wykorzystuje strefę czasową i to jest przyczyną Twojego problemu. Analizuje go ze strefą czasową GMT / UTC (24 września 2011 r., 00 : 00 UTC), a następnie wysyła go z inną strefą czasową wynoszącą 4 godziny (23 września 2011 r., 20 : 00 GMT-0400).
Kod

2
Dostaję „nieprawidłową datę”. Zamień znaki „-” na znaki „/” i spróbuj ponownie. Lub podziel datę na części i ustaw składniki indywidualnie (jeśli to zrobisz, odejmij 1 od liczby miesięcy).
RobG

@Codo - tak, dobra odpowiedź. Obowiązuje ECMA-262 15.9.1.15. PO powinien używać „2011-09-24T20: 00: 00-04: 00” lub podobnego.
RobG

1
Odkryłem, że format „24 września 2011 r.” Zwróci właściwą datę. Zobacz tutaj wyjaśnienie: stackoverflow.com/questions/2587345/javascript-date-parse
christurnerio

Odpowiedzi:


98

Zwróć uwagę, że jest wschodni czas letni -4 hoursi że godziny w dniu powrotu są 20.

20h + 4h = 24h

czyli północ 24.09.2011. Data została przeanalizowana w UTC (GMT), ponieważ podałeś ciąg tylko daty bez wskaźnika strefy czasowej. Jeśli zamiast tego podałeś ciąg daty / godziny bez wskaźnika (new Date("2011-09-24T00:00:00") ), zostałby on przeanalizowany w lokalnej strefie czasowej. (Historycznie występowały tam niespójności, między innymi dlatego, że specyfikacja zmieniła się więcej niż jeden raz, ale nowoczesne przeglądarki powinny być w porządku; lub zawsze możesz dołączyć wskaźnik strefy czasowej).

Otrzymujesz właściwą datę, po prostu nigdy nie podałeś prawidłowej strefy czasowej.

Jeśli chcesz uzyskać dostęp do wartości daty, można użyć getUTCDate()lub którykolwiek z pozostałych getUTC*()funkcji :

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

24
Jak „określić prawidłową strefę czasową”? Konstruktor daty zawsze interpretuje ciąg daty jako UTC, ale następnie dostosowuje się do strefy czasowej. Mogę nawet zrobić „nową datę” („2012-01-02 EDT”) i nadal przenoszę ją na poprzedni dzień ze względu na zastosowanie przesunięcia w stosunku do czasu letniego, co nie ma sensu, ponieważ gdybym powiedział, że jest to data zgodna z bieżąca lokalna strefa czasowa EDT, a następnie nie stosuj do niej dodatkowego przesunięcia. Powiedziałem, że strefa czasowa to EDT, ale nadal stosuje dodatkowe przesunięcie, cofając ją o jeden dzień.
AaronLS,

1
@AaronLS, EDTto czas letni (znany również jako czas letni) , ESTto strefa czasowa, która obowiązuje w styczniu.
zzzzBov

1
Stworzenie go jako ciągu UTC z ciągu byłoby tym, co chciałbym zrobić, dlatego zapytałem „Jak to zrobić” określić prawidłową strefę czasową? odnośnie tego, gdzie napisałeś „po prostu nigdy nie podałeś prawidłowej strefy czasowej”. Jeśli to zrobię new Date('2012-01-01 GMT'), nadal stosuje przesunięcie, ponieważ konwertuje je na lokalną datę użytkownika.
AaronLS,

7
@AaronLS, jeśli jesteś zmuszony użyć get*metod i potrzebujesz go zwrócić poprawną datę / godzinę, w tym nieznane przesunięcie strefy czasowej, po prostu dodaj nieznane przesunięcie strefy czasowej: d = new Date('2013-01-08 GMT'); d.setMinutes(d.getMinutes() + d.getTimezoneOffset());To znormalizuje datę do ustawień regionalnych użytkownika, aby .get*metody powróciły oczekiwana wartość. Te .getUTC*metody będą następnie być błędne, więc należy być ostrożnym.
zzzzBov

1
Istnieje różnica między zdefiniowaniem zachowania w celu zapewnienia referencji a użyciem go jako ucieczki, gdy implementatory nie wdrożyły czegoś poprawnie. I to nie ta sama reprezentacja, jeśli dane nie są zgodne z oczekiwaniami.
Dissident Rage

252

Istnieje kilka szalonych rzeczy, które mają miejsce w przypadku obiektu JS DATE, który konwertuje ciągi znaków, na przykład rozważ podaną przez ciebie datę

Uwaga: Poniższe przykłady mogą lub nie mogą być jeden dzień wolny w zależności od TWOJEJ strefę czasową i aktualny czas.

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

Jeśli jednak zmienimy format ciągu na Miesiąc-Dzień-Rok ...

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

Kolejny dziwny

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.

Możemy łatwo zmienić myślniki w dacie „2011-09-24” podczas tworzenia nowej daty

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

Co gdybyśmy mieli ciąg daty, taki jak „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.

Teraz zmień myślnik na ukośnik tak jak poprzednio; co się dzieje?

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

Zwykle muszę zarządzać formatem daty 2011-09-24T00: 00: 00, więc to właśnie robię.

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

AKTUALIZACJA

Jeśli podasz osobne argumenty konstruktorowi daty, możesz uzyskać inne przydatne dane wyjściowe, jak opisano poniżej

Uwaga: argumenty mogą być typu Number lub String. Pokażę przykłady z mieszanymi wartościami.

Uzyskaj pierwszy miesiąc i dzień danego roku

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

Uzyskaj ostatni miesiąc i dzień roku

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)

Przykład argumentów Number, String. Zauważ, że miesiącem jest marzec, ponieważ ponownie miesiącami są zero.

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

Jeśli zrobimy to samo, ale z zerowym dniem, otrzymamy coś innego.

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)

Dodanie dnia zero do dowolnego argumentu roku i miesiąca spowoduje uzyskanie ostatniego dnia poprzedniego miesiąca. Jeśli będziesz kontynuować z liczbami ujemnymi, możesz kontynuować cofanie o kolejny dzień

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

12
To naprawdę pomogło mi najlepiej. Właśnie dodałem .replace (/ - / g, '\ /'). Replace (/ T. + /, '') Na końcu moich danych, kiedy utworzyłem nową datę. Super łatwe!
Devin Prejean

64
Wow - javascript jest strasznie niespójny.
psparrow

2
Wywołania .replace () bardzo pomogły dziś wieczorem.
używaj

2
tak, przetestowałem to wszystko i to naprawdę dziwne.
chintan adatiya

26
Wszystko to wynika z zachowania bazowej funkcji Date.parse (), która stara się przestrzegać normy ISO 8601. Gdy łańcuch daty ma format rrrr-mm-dd, przyjmuje się, że jest to ISO 8601 z niejawnym kodem UTC 00:00. Kiedy łańcuch odbiega od formatu (np. Mm-dd-rrrr lub ukośnik zamiast łącznika), wraca do luźniejszego parsera zgodnie z RFC 2822, który wykorzystuje czas lokalny, gdy strefa czasowa jest nieobecna. Trzeba przyznać, że dla przeciętnego człowieka wszystko to będzie dość tajemnicze.
Mizstik

71

Aby znormalizować datę i wyeliminować niepożądane przesunięcie (testowane tutaj: 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)

Osiąga to również to samo i uznanie dla @tpartee (testowane tutaj: https://jsfiddle.net/7xp1xL5m/1/ ):

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

Zrobiło to dla mnie - musiałem pracować z datami z API, a następnie sortować / porównywać je, więc po prostu dodanie przesunięcia strefy czasowej działa najlepiej.
czakeda

To działało dla mnie, tyle że musiałem odjąć timeZoneOffset, nie dodawać go
ErikAGriffin

@ErikAGriffin Czy jesteś w dodatniej strefie czasowej, IE GMT + 0X00 zamiast GMT-0X00?
AaronLS,

Zrobiłem coś podobnego:doo.setMinutes(doo.getMinutes() + doo.getTimezoneOffset())
Dissident Rage

2
@AaronLS Byłeś na dobrej drodze, ale nieco zła logika. Aby skompensować przesunięcie TZ, poprawną logiką jest: console.log (nowa data (doo.getTime () - doo.getTimezoneOffset () * -60000)); - Znak przesunięcia jest ważny i nie można go absolutyzować, ale do poprawienia potrzebny jest również znak odwrotny, więc mnożymy przesunięcie przez -60000, aby zastosować znak odwrotny.
tpartee

28

Jeśli chcesz uzyskać godzinę 0 jakiejś daty w lokalnej strefie czasowej, przekaż poszczególne części daty Datekonstruktorowi.

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

26

Chcę tylko dodać, że najwyraźniej dodanie spacji na końcu ciągu spowoduje użycie UTC do utworzenia.

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)

Edycja: To nie jest zalecane rozwiązanie, tylko alternatywna odpowiedź. Proszę nie stosować tego podejścia, ponieważ nie jest jasne, co się dzieje. Istnieje wiele sposobów, w jakie ktoś może przezwyciężyć to przypadkowo, powodując błąd.


Na przykład ze spacją na końcu, konsola zwraca „Nieprawidłowa data = 2 $”
Brian Risk

1
działa bardzo dobrze! nowa data (data.Date + „”) .toLocaleDateString („en-US”)
Nakres

Szczerze mówiąc, była to tylko alternatywna odpowiedź i nie jest zalecanym rozwiązaniem.
Kyle Shrader,

25

Uważam, że ma to związek z dostosowaniem strefy czasowej. Data, którą utworzyłeś, to GMT, a domyślną godziną jest północ, ale twoja strefa czasowa to EDT, więc odejmuje 4 godziny. Spróbuj tego, aby zweryfikować:

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

3
To najlepsza odpowiedź tutaj. + 1 milion za korzystanie z wbudowanych ciągów lokalizacji strefy czasowej zamiast konwertowania programowego.
blearn 10.04.17

2
Ten pomaga. Działa to tak: $ scope.dat = new Date (datestring + 'EDT'). Zwróć jednak uwagę na różnicę między EDT i EST: link
Weihui Guo,

Nie wiem, co dzieje się za kulisami, ale działa idealnie.
the_haystacker

Powiedziałbym, że jest to najprostsza z odpowiedzi i mniej formatowania kodu.
Michael

Dzięki, uważam najlepszą drogę do mnie.
Janderson Constantino

11

Twój problem dotyczy konkretnie strefy czasowej. Uwaga część GMT-0400- czyli 4 godziny za GMT. Jeśli dodasz 4 godziny do wyświetlanej daty / godziny, otrzymasz dokładnie północ 24/09/24. toUTCString()Zamiast tego użyj metody, aby uzyskać ciąg GMT:

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

7

To prawdopodobnie nie jest dobra odpowiedź, ale chcę tylko podzielić się swoimi doświadczeniami z tym problemem.

Moja aplikacja globalnie używa daty utc w formacie „RRRR-MM-DD”, podczas gdy wtyczka datepicker, której używam, akceptuje tylko datę js, trudno mi rozważyć zarówno utc, jak i js. Więc kiedy chcę przekazać datę w formacie „RRRR-MM-DD” do mojego datepickera, najpierw przekonwertuję ją na format „MM / DD / RRRR” za pomocą moment.js lub cokolwiek innego, a data wyświetlana w datepicker jest teraz poprawny. Na twój przykład

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

Najwyraźniej d1 jest tym, czego chcę. Mam nadzieję, że byłoby to pomocne dla niektórych osób.


1
Po prostu zmiana formatów tak jak ty, działała dla mnie tak samo. Bardzo dziwne.
jAC

5

To przeze mnie na pętlę, +1 w odpowiedzi zzzBov. Oto pełna konwersja daty, która działała dla mnie przy użyciu metod 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

To znaczy 2011-09-24 00:00:00 GMT, a skoro już jesteś GMT -4, będzie to 20:00dzień wcześniej.

Osobiście rozumiem 2011-09-24 02:00:00, ponieważ mieszkam w GMT +2.


3

Chociaż w przypadku OP strefą czasową jest EDT, nie ma gwarancji, że użytkownik wykonujący skrypt będzie w strefie czasowej EDT, więc kodowanie przesunięcia na stałe niekoniecznie będzie działać. Rozwiązanie, które znalazłem, dzieli ciąg daty i używa osobnych wartości w konstruktorze daty.

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

Pamiętaj, że musisz wziąć pod uwagę kolejną dziwność JS: miesiąc jest zerowy.


2

Dokładnie ten problem napotkałem, gdy mój klient pracował w Atlantic Standard Time. Wartość daty, którą pobierał klient, to „2018-11-23”, a kiedy kod przekazał go do new Date("2018-11-23")danych wyjściowych dla klienta, był z poprzedniego dnia. Utworzyłem funkcję narzędzia pokazaną we fragmencie, która znormalizowała datę, podając klientowi oczekiwaną datę.

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

jeśli chcesz tylko upewnić się, że poszczególne części daty pozostaną takie same do celów wyświetlania, * wydaje się, że działa, nawet po zmianie strefy czasowej:

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

po prostu dodaj tam zera.

W moim kodzie robię to:

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

Przełączam strefę czasową na komputerze, a data pozostaje taka sama, jak ciąg daty rrrr-mm-dd, który otrzymuję z interfejsu API.

Ale czy coś mi umknęło / czy to zły pomysł?

* przynajmniej w chromie. To nie działa w Safari! od tego pisania


Kiedy to zrobisz .toISOString(), cofnie się o 1 dzień.
vivek_23,

new Date('2019/11/18 05:30:00').toISOString();pracował dla mnie
vivek_23

1

Najlepszy sposób, aby sobie z tym poradzić bez użycia większej liczby metod konwersji,

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

Teraz po prostu dodaj GMT do swojej daty lub możesz ją dołączyć.

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

Na żywo: https://jsfiddle.net/gajender/2kop9vrk/1/


1

Napotkałem taki problem. Ale mój problem był wyłączony podczas pobierania daty z bazy danych.

jest ukryty w bazie danych i ma format UTC.

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

Więc kiedy dostanę się z bazy danych i sprawdzę datę, dodaje to przesunięcie i odsyła z powrotem do javascript.

wprowadź opis zdjęcia tutaj

Dodaje +05: 00, ponieważ to jest strefa czasowa mojego serwera. Mój klient jest w innej strefie czasowej +07: 00.

2019-03-28T19: 00: 00 + 05: 00 // to dostaję w javascript.

Oto moje rozwiązanie dotyczące tego problemu.

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);

Więc kiedy data pochodzi z serwera i ma przesunięcie serwera, dzielę datę i usuwam przesunięcie serwera, a następnie przekształcam na datę. To rozwiązuje mój problem.



0

Używasz formatu ciągu daty ISO, który zgodnie z tą stroną powoduje, że data jest konstruowana przy użyciu strefy czasowej UTC:

Uwaga: parsowanie ciągów daty za pomocą konstruktora Date (i Date.parse, są one równoważne) jest zdecydowanie odradzane z powodu różnic w przeglądarkach i niespójności. Obsługa ciągów formatu RFC 2822 odbywa się wyłącznie na podstawie konwencji. Obsługa formatów ISO 8601 różni się tym, że ciągi tylko daty (np. „1970-01-01”) są traktowane jako UTC, a nie lokalne.

Jeśli sformatujesz tekst inaczej, na przykład "Jan 01 1970", wtedy (przynajmniej na moim komputerze) użyje on lokalnej strefy czasowej.


0

Próbuję dodać moje 2 centy do tego wątku (opracowując odpowiedź @ paul-wintz).

Wydaje mi się, że gdy konstruktor daty otrzymuje ciąg znaków pasujący do pierwszej części formatu ISO 8601 (część daty), dokonuje dokładnej konwersji daty w strefie czasowej UTC z czasem 0. Po przekształceniu tej daty na czas lokalny może nastąpić przesunięcie daty, jeśli północ UTC jest wcześniejszą datą w lokalnej strefie czasowej.

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

Jeśli ciąg daty ma inny „luźniejszy” format (używa „/” lub data / miesiąc nie jest uzupełniona zerem), tworzy datę w lokalnej strefie czasowej, więc nie ma problemu z przesunięciem daty.

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)

Zatem jedną z szybkich poprawek, jak wspomniano powyżej, jest zastąpienie „-” ciągiem „/” w ciągu tylko daty sformatowanym w formacie ISO.

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

0

Przechowując yyyy-mm-ddw formacie MySql Date, musisz wykonać następujące czynności:

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

-1

Twój dziennik generuje GMT, więc chcesz określić swoją strefę czasową:

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

3
To pytanie ma już wiele odpowiedzi. Jeśli i tak chcesz odpowiedzieć na pytanie, powinieneś wspomnieć o czymś nowym, czego inne odpowiedzi nie. Najlepsza odpowiedź już bardziej szczegółowo wyjaśnia problem ze strefą czasową.
Calvin Godfrey,

Ta odpowiedź jest prawie dokładnie taka sama jak stackoverflow.com/a/7556642/8828658
kamienny pajęczak

Kogo to obchodzi, jeśli jego wyjaśnienie jest bardziej szczegółowe ... jego kod wciąż różni się od mojego. Mój jest o wiele prostszy. I tak, kamienny pajęczaku, masz rację. Mój błąd.
bmacx7

-3

nieważne, nie zauważyłem GMT -0400, co powoduje, że data jest wczoraj

Możesz spróbować ustawić domyślny „czas” na 12:00:00


Tak, byłem trochę szybki z moją odpowiedzią, przepraszam za to. Poprawiłem moją odpowiedź.
ChrisH

-3

Poniższe działało dla mnie -

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

Nadal nie będzie działać, dopóki nie będziesz majstrować w strefie czasowej.
Gar
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.