Zachowaj wartość daty publikacji bez godziny, interpretując ją jako datę kalendarzową i traktując tę datę jako nadrzędną. Jeśli eksport wymaga znacznika czasu UTC, zastosuj jawną konwencję północy UTC. Ten przewodnik dla Terminala na Macu pokazuje tę różnicę i sprawdza konwersję w obie strony.

Ustal, co właściwie oznacza pole publikacji

Załóżmy, że lista publikacji zawiera syntetyczną wartość 2028-06-15. Twoim zadaniem jest zweryfikowanie tej wartości i przygotowanie jej do eksportu bez przypadkowego wyświetlenia 14 czerwca. Zanim wybierzesz parser, ustal, czy pole zapisuje dzień publikacji, czy konkretny moment publikacji.

Te cztery pojęcia pełnią różne funkcje:

Pojęcie Syntetyczny przykład Znaczenie
Data cywilna 2028-06-15 Dzień kalendarzowy bez godziny i przesunięcia
Chwila 2028-06-15T00:00:00+00:00 Konkretny moment wyrażony w UTC
Przesunięcie UTC +09:00 Różnica między lokalnym wskazaniem zegara a UTC
Nazwana strefa America/Los_Angeles Identyfikator służący do uzyskania obowiązujących reguł strefy czasowej

RFC 3339 definiuje osobno full-date i date-time; gramatyka znacznika czasu dodaje do daty godzinę i przesunięcie. Sama data publikacji nie zawiera więc informacji potrzebnych do ustalenia chwili publikacji. Wybór północy dodaje informację, której nie było w danych wejściowych. RFC 3339, sekcja 5.6

Nazwana strefa zawiera też więcej informacji niż pojedyncze przesunięcie liczbowe. Dokumentacja Python zoneinfo pokazuje, że strefa może używać różnych przesunięć podczas zmiany czasu na letni. W przypadku etykiety zawierającej wyłącznie datę nie trzeba ustalać tych reguł. Python: obsługa stref czasowych IANA

Przed eksportem zastosuj tę regułę decyzyjną:

  • Pole oznacza „opublikowano tego dnia”: przechowuj i wyświetlaj wyłącznie datę.
  • Format docelowy wymaga znacznika czasu: zachowaj datę i utwórz nośnik z północą UTC zgodnie z udokumentowaną konwencją.
  • Pole oznacza „opublikowano o tej chwili”: uzyskaj rzeczywistą godzinę i przesunięcie albo godzinę i nazwaną strefę potrzebne do ustalenia tej chwili.

Konwencja nośnika jest przydatna ze względu na zgodność, ale nie pozwala odtworzyć nieznanej godziny publikacji. Nazwa pola, taka jak utc_carrier, uwidacznia to ograniczenie; nazwa published_at sugerowałaby informacje, których źródło nigdy nie dostarczyło.

Ustal, na którym etapie pojawia się błąd z poprzednim dniem

Rozważmy dwie możliwe ścieżki przetwarzania wartości 2028-06-15. W pierwszej program dopisuje północ przy stałym przesunięciu +09:00, a następnie przelicza ten moment na UTC. Wynik to 2028-06-14T15:00:00+00:00: odjęcie dziewięciu godzin przekracza granicę daty.

W drugiej ścieżce program celowo tworzy północ UTC: 2028-06-15T00:00:00+00:00. Wyświetlenie tego samego momentu przy stałym przesunięciu -07:00 daje 2028-06-14T17:00:00-07:00. Są to przykładowe obliczenia przesunięć, a nie twierdzenia o przesunięciu nazwanej strefy w tym dniu.

Oba obliczenia zachowują odpowiadające im chwile. Żadne nie zachowuje pierwotnej etykiety kalendarzowej przy wyświetlaniu w każdej strefie. Python dokumentuje, że astimezone() zmienia pola zegara, zachowując reprezentowany czas UTC. Python: datetime.astimezone()

W praktyce parsowanie i wyświetlanie muszą korzystać ze wspólnej umowy. Jeśli data została zapisana jako nośnik z północą UTC, odczytaj jej pola kalendarzowe w UTC. Jeśli etykieta publikacji ma pozostać niezmieniona niezależnie od tego, gdzie ktoś otworzy eksport, wyświetl zachowaną datę cywilną.

Nie zakładaj, że każdy język programowania uzupełnia brakujące informacje w ten sam sposób. ECMAScript określa, że formy daty bez przesunięcia są interpretowane jako UTC, natomiast formy daty i godziny bez przesunięcia — jako czas lokalny. Dodanie T00:00:00 do ciągu znaków może więc zmienić jego interpretację w JavaScript. ECMAScript: Date.parse

Przeprowadź pełną kontrolę daty publikacji w Terminalu na Macu

Ten przewodnik weryfikuje trzy syntetyczne daty, wyświetla podgląd rozdzielony tabulatorami i sprawdza odrzucanie niepoprawnych danych wejściowych. Nie modyfikuje listy publikacji ani nie tworzy pliku eksportu.

W Finder otwórz /Applications/Utilities, a następnie kliknij dwukrotnie Terminal. Apple opisuje tę metodę jako obsługiwany sposób otwierania aplikacji wiersza poleceń. Apple: otwieranie i zamykanie Terminala na Macu

Uruchom następujące polecenie, aby sprawdzić interpreter:

python3 --version

W tym przewodniku użyj Pythona w wersji 3.11 lub nowszej. Jeśli python3 jest niedostępny lub zgłasza starszą wersję, przed kontynuowaniem postępuj zgodnie z oficjalnymi instrukcjami instalacji dla macOS, a następnie ponownie otwórz Terminal i jeszcze raz sprawdź wersję. Python dokumentuje zarówno instalator dla macOS, jak i uruchamianie skryptów za pomocą python3. Python: korzystanie z Pythona w systemie macOS

Wklej cały blok poniżej, łącznie z końcowym PY, i naciśnij Return. Argument - instruuje Pythona, aby odczytał program ze standardowego wejścia. Zawarto tu wszystkie przykładowe dane i importy. Python: wiersz poleceń i środowisko

python3 - <<'PY'
import re
from datetime import date, datetime, time, timezone


def parse_publication_date(text):
    if not isinstance(text, str) or re.fullmatch(
        r"[0-9]{4}-[0-9]{2}-[0-9]{2}", text
    ) is None:
        raise ValueError("Expected YYYY-MM-DD")
    return date.fromisoformat(text)


def utc_carrier(day):
    return datetime.combine(day, time(0), tzinfo=timezone.utc)


samples = ["2028-02-29", "2028-06-15", "2028-12-31"]
parsed = [(text, parse_publication_date(text)) for text in samples]

print("publication_date\tutc_carrier")
for original, day in parsed:
    carrier = utc_carrier(day)
    assert day.isoformat() == original
    assert carrier.astimezone(timezone.utc).date() == day
    print(day.isoformat(), carrier.isoformat(timespec="seconds"), sep="\t")

for invalid in [
    "2027-02-29",
    "2028-6-15",
    "2028-06-15T00:00:00",
    " 2028-06-15",
]:
    try:
        parse_publication_date(invalid)
    except ValueError:
        pass
    else:
        raise AssertionError(f"Unexpected acceptance: {invalid!r}")

print("Round-trip and rejection checks passed.")
PY

Oczekiwany wynik:

publication_date	utc_carrier
2028-02-29	2028-02-29T00:00:00+00:00
2028-06-15	2028-06-15T00:00:00+00:00
2028-12-31	2028-12-31T00:00:00+00:00
Round-trip and rejection checks passed.

Przykład i dwa obliczenia dla stałych przesunięć zostały pomyślnie wykonane przy użyciu dostępnego interpretera Python. Instrukcje dotyczące Maca i interpretera opierają się na podlinkowanej oficjalnej dokumentacji.

Dlaczego przykład zachowuje dzień publikacji

Parser osobno sprawdza dwie rzeczy: dokładny format ciągu znaków i poprawność daty kalendarzowej. Wyrażenie regularne wymaga czterech cyfr roku, dwóch cyfr miesiąca i dwóch cyfr dnia. Następnie parser dat odrzuca nieistniejące daty. Dzięki temu kolumna publikacji nie zaakceptuje po cichu znaczników czasu, otaczających spacji ani skróconych danych wejściowych.

To jawne sprawdzenie formatu ma znaczenie, ponieważ date.fromisoformat() akceptuje również inne reprezentacje ISO, w tym daty bez separatorów i daty tygodniowe. datetime.combine() celowo dodaje następnie północ i UTC. Python: podstawowe typy daty i czasu

Program parsuje całą przykładową listę przed wyświetleniem nagłówka. Jeśli zastąpisz któryś przykład nieprawidłową wartością, wykonywanie zakończy się, zanim pojawią się jakiekolwiek wiersze podglądu. Zamierzone testy niepoprawnych danych na końcu są osobnymi sprawdzianami; oczekiwane wyjątki są przechwytywane, aby można było sprawdzić wszystkie przypadki odrzucenia.

Dwie asercje konwersji w obie strony wyrażają tę umowę. Pierwsza sprawdza, czy parsowanie i formatowanie daty odtwarzają pierwotną etykietę. Druga sprawdza, czy po odczytaniu pochodnego nośnika w UTC uzyskuje się tę samą datę. Razem weryfikują dokładne zadanie zachowania wartości, zamiast tylko sprawdzać, czy można utworzyć jakiś znacznik czasu.

Wynik jawnie używa +00:00. RFC 3339 dopuszcza również Z jako oznaczenie przesunięcia UTC, ale dodanie któregokolwiek z tych sufiksów do samej daty nie uzupełnia brakującego składnika godziny. Zachowaj format zawierający wyłącznie datę albo celowo utwórz pełny znacznik czasu. RFC 3339, sekcja 5.6

Zdiagnozuj eksport, w którym data nadal się zmienia

Jeśli podgląd w kolejnym systemie pokazuje poprzedni dzień, sprawdź wartości na każdym etapie. Użyj środkowego syntetycznego przykładu 2028-06-15, aby porównać każdy etap z jedną znaną etykietą.

Punkt kontrolny Oczekiwany wynik przy konwencji nośnika Co może oznaczać różnica
Pierwotne pole publikacji 2028-06-15 Dane wejściowe zmieniły się przed parsowaniem
Sparsowana data wyświetlona jako tekst 2028-06-15 Parsowanie lub normalizacja zmieniły etykietę
Pochodny nośnik UTC 2028-06-15T00:00:00+00:00 Wprowadzono inną godzinę lub przesunięcie
Data odtworzona w UTC 2028-06-15 Konwersja lub ekstrakcja opierała się na innej umowie
Etykieta publikacji wyświetlona czytelnikowi 2028-06-15 Przeglądarka może wyświetlać nośnik w innej strefie

Sprawdź renderer niezależnie od parsera. Poprawny nośnik UTC może nadal dać inną datę kalendarzową, jeśli jest wyświetlany z ustawieniem lokalnej strefy. W przypadku etykiety publikacji przekaż rendererowi zachowaną datę bez godziny. Jeśli akceptuje wyłącznie znaczniki czasu, użyj udokumentowanej opcji wyświetlania w UTC i zweryfikuj wynik.

Jeśli narzędzie docelowe obsługuje tylko chwile i zawsze wyświetla je lokalnie, konwencja północy UTC nie może zagwarantować niezmienności etykiety daty. Zachowaj osobną tekstową kolumnę zawierającą wyłącznie datę, użyj obsługiwanego pola daty bez godziny albo wybierz renderer z jawnym formatowaniem UTC. Ograniczenie wynika z przedstawionych wyżej obliczeń przesunięć; sama zmiana parsera go nie usunie.

Nie traktuj też istniejącego znacznika czasu tak, jakby był danymi wejściowymi zawierającymi wyłącznie datę. W przypadku prawdziwej chwili zmiana strefy oznacza jej przeliczenie. Python odróżnia tę operację od zastąpienia tzinfo, które przypisuje informację o strefie bez zmiany pól zegara. Python: działanie konwersji strefy czasowej

W przypadku swojej listy publikacji zachowaj publication_date jako nadrzędną wartość kalendarzową. Twórz nośnik UTC wyłącznie na potrzeby interfejsu, który go wymaga, zapisuj tę konwencję przy polu i sprawdzaj odczyt w UTC, zanim zaufasz podglądowi. Dzięki temu możesz w powtarzalny sposób zachować podany dzień bez wymyślania chwili publikacji.

Źródła