Niebezpieczny parser dat w JavaScript: jak uniknąć błędów przy pracy ze stringami
Konstruktor new Date(string) w JavaScript próbuje wyodrębnić datę z dowolnego ciągu znaków, co prowadzi do nieoczekiwanych wyników. To nie błąd — to cecha implementacji silników V8 i SpiderMonkey, gdzie parser agresywnie interpretuje tekst.
Przykłady pokazują problem:
new Date("2020-01-23")
// Wed Jan 22 2020 19:00:00 GMT-0500 (interpretacja UTC)
new Date("Today is 2020-01-23")
// Thu Jan 23 2020 00:00:00 GMT-0500 (czas lokalny)
new Date("Route 66")
// Sat Jan 01 1966 00:00:00 GMT-0500
new Date("Beverly Hills, 90210")
// Mon Jan 01 90210 00:00:00 GMT-0500
Takie przekształcenia tworzą poprawne obiekty Date, ale z nieprawidłowymi wartościami, co utrudnia debugowanie. Parser Catch 22 zwraca Invalid Date — rzadki przypadek, gdy logika działa poprawnie.
Mechanizm parsowania w V8
Specyfikacja ECMAScript wymaga parsowania tylko podzbioru ISO 8601. Reszta zależy od implementacji.
Algorytm V8:
- Sprawdzenie formatu ISO 8601: ciąg musi zaczynać się od czterocyfrowego roku lub znaku.
- Przejście do legacy-parsera: ignorowanie słów przed pierwszą liczbą.
- Przetwarzanie liczb w
DayComposer:
- 1–12: miesiąc.
- 13–31: niejednoznaczne, często odrzucane.
- 32+: rok (50–99 +1900).
- Domyślnie: 1 stycznia.
To wyjaśnia, dlaczego "Route 66" to rok 1966, a "90210" to rok 90210.
Problemy z strefami czasowymi
Ciągi ISO takie jak "2020-01-23" są parsowane jako UTC, legacy — jako czas lokalny. Różnica wynika z prefiksów:
- Czysta ISO: UTC.
- Z tekstem: legacy, czas lokalny.
Spacje na początku lub końcu ciągu zmieniają parsera, przesuwając wynik o dobę w zależności od strefy czasowej.
Safari (JavaScriptCore) jest bardziej rygorystyczny: odrzuca przykłady legacy oraz ciągi z końcowymi spacjami.
Praktyczne błędy i rozwiązania
W projektach rzeczywistych fallback na new Date() dla danych użytkownika zamienia "Route 66" na datę, psując tabele. Rozwiązanie: polegaj wyłącznie na własnych parserach z walidacją regex.
Zalecenia dla programistów średniozaawansowanych i seniorów:
- Waliduj format za pomocą regex przed użyciem
new Date(). - Używaj
date-fns parseISO()do ścisłego parsowania ISO. - Dla pełnej kontroli — biblioteki typu Luxon lub Day.js z niestandardowymi parserami.
- Unikaj
new Date(string)z niezweryfikowanymi danymi. - Testuj cross-browserowo: V8, SpiderMonkey, JSC.
Porównanie z Pythonem
Python unika dwuznaczności:
datetime.fromisoformat(): tylko ISO 8601.strptime(): ścisły format, bez nadmiarowych znaków.
JavaScript legacy-parser domyśla się cicho, Python rzuca ValueError.
Co ważne
- Parser
Datetworzy poprawne, ale nieprawidłowe obiekty z losowego tekstu. - Różnica między parserami (ISO vs legacy) zależy od prefiksów i spacji.
- W środowisku produkcyjnym: zawsze waliduj dane wejściowe przed parsowaniem.
- Przejdź na Temporal API dla kodu przyszłości.
- Testy cross-browserowe są obowiązkowe z powodu różnic w silnikach.
— Editorial Team
Brak komentarzy.