Aby sformatować chwilę publikacji w innej strefie czasowej, zachowaj pierwotną chwilę i przekaż formatterowi docelową strefę. Ten poradnik dotyczący Terminala na Macu pokazuje, jak utworzyć czytelną etykietę publikacji bez analizowania wyświetlonego tekstu ani ręcznego dodawania godzin.
Jakie informacje są potrzebne przed konwersją?
Załóżmy, że przygotowujesz dokument na Macu i otrzymujesz przykładowy znacznik czasu publikacji 2025-07-15T00:30:00Z. Potrzebujesz daty i godziny publikacji wyświetlanych w strefie America/Los_Angeles. Zadanie polega na opisaniu tej samej chwili za pomocą daty kalendarzowej i godziny obowiązujących w docelowej strefie.
O tym, czy masz wystarczające informacje, decydują cztery pojęcia:
| Pojęcie | Przykład | Znaczenie w tym zadaniu |
|---|---|---|
| Data cywilna | 2025-07-15 |
Dzień kalendarzowy bez godziny ani strefy |
| Chwila | Moment reprezentowany przez 2025-07-15T00:30:00Z |
Punkt w czasie, który zachowujesz |
| Przesunięcie względem UTC | -07:00 |
Różnica między czasem lokalnym a UTC w danej chwili |
| Nazwana strefa czasowa | America/Los_Angeles |
Reguły służące do ustalenia właściwego przesunięcia |
RFC 3339 definiuje znaczniki czasu zawierające pełną datę, godzinę i przesunięcie względem UTC. RFC 9557 wyjaśnia dodatkowe rozróżnienie: nazwana strefa określa reguły mapujące chwilę na przesunięcie, natomiast samo liczbowe przesunięcie w znaczniku czasu nie opisuje, jak przesunięcie w danej lokalizacji zachowuje się w innych terminach. RFC 3339, sekcja 5.6, RFC 9557, sekcja 1.2.
Przed konwersją podejmij decyzję na podstawie danych wejściowych:
- Masz już obiekt Foundation
Date: przekaż go bezpośrednio do formattera skonfigurowanego dla docelowej strefy. - Masz znacznik czasu z
Zlub jawnym przesunięciem liczbowym: sparsuj pierwotny znacznik czasu jeden raz, a następnie sformatuj otrzymaną chwilę. - Masz tylko datę cywilną: zachowaj ją jako datę, chyba że zadanie jawnie określa godzinę i strefę.
- Masz lokalną datę i godzinę bez przesunięcia: ustal strefę źródłową i rozstrzygnij ewentualną niejednoznaczność, zanim potraktujesz je jako konkretną chwilę.
Na przykład 2025-07-15 nie pozwala ustalić, czy publikacja nastąpiła przed północą, czy po niej w innej strefie. Przypisanie północy wprowadziłoby dodatkowe założenie. Podobnie lokalny odczyt zegara może odpowiadać zeru lub wielu chwilom podczas zmiany przesunięcia; RFC 9557 opisuje tę granicę. Konwersja już ustalonej chwili pozwala uniknąć tego problemu odwrotnego mapowania. RFC 9557, sekcja 1.2.
Dlaczego nie należy ponownie analizować tekstu wyświetlanego?
Apple definiuje Date z Foundation jako konkretny punkt w czasie, niezależny od kalendarza i strefy czasowej. Jego wartość nie musi zmieniać się na inny obiekt Date, aby można ją było przedstawić w innej strefie. Formatowanie zapewnia kalendarz i strefę potrzebne do utworzenia tekstu. Date.
Etykieta może pomijać pewne informacje. Jeśli sformatujesz chwilę jako datę bez godziny, a następnie sparsujesz tę etykietę, brakującej godziny nie da się z niej odzyskać. Dokumentacja formatowania Apple pokazuje konwersję w obie strony dla samej daty, która odtwarza północ. Jest to przydatne, gdy północ jest zamierzona, ale nie zachowuje dowolnej chwili publikacji. Date.FormatStyle.
Zachowaj następującą kolejność działań:
- Odczytaj pierwotny znacznik czasu lub pobierz istniejący obiekt
Date. - Zachowaj tę chwilę jako wartość roboczą.
- Wybierz docelową strefę, kalendarz i ustawienia regionalne wyświetlania.
- Sformatuj zachowaną chwilę, tworząc końcową etykietę.
Ręczne dodawanie przesunięcia również zmienia charakter zadania. Apple opisuje addingTimeInterval(_:) jako tworzenie nowej daty przez dodanie upływającego czasu. Dodanie siedmiu godzin tworzy więc inną chwilę; wybór strefy w formatterze zmienia sposób przedstawienia pierwotnej chwili. Date.
Uruchom kompletny przykład czasu publikacji na Macu
Przykład wymaga macOS 12 lub nowszego, w którym dostępne jest API Date.FormatStyle firmy Apple, oraz działającego łańcucha narzędzi Swift. Wykorzystuje Foundation i nie wymaga dodatkowych pakietów. Date.FormatStyle.
Otwórz Terminal i sprawdź łańcuch narzędzi:
swift --version
Jeśli potrzebujesz narzędzi wiersza poleceń Apple, uruchom poniższe polecenie i dokończ instalację w oknie dialogowym. Apple opisuje tę metodę instalacji i zaznacza, że pełna instalacja Xcode zawiera już narzędzia wiersza poleceń. Następnie ponownie sprawdź wersję. Instalowanie narzędzi wiersza poleceń.
xcode-select --install
W aplikacji TextEdit utwórz nowy dokument i wybierz Format > Make Plain Text. Wklej cały poniższy program w języku Swift i zapisz go na Biurku jako publication-time.swift. Upewnij się, że nazwa pliku kończy się na .swift i nie ma dodanego rozszerzenia .txt. W podręczniku Apple dotyczącym TextEdit opisano tworzenie, konwertowanie i zapisywanie dokumentów tekstowych. Tworzenie, otwieranie i konwertowanie dokumentów w TextEdit na Macu.
Program przyjmuje podany znacznik czasu z dokładnością do pełnej sekundy, analizuje go jeden raz i tworzy trzy reprezentacje z tej samej wartości instant. Apple opisuje ISO8601DateFormatter do analizowania i generowania reprezentacji ISO 8601 oraz jego opcję withInternetDateTime dla formatu RFC 3339. ISO8601DateFormatter, withInternetDateTime.
import Foundation
// Synthetic publication data. Change these two values as needed.
let sourceTimestamp = "2025-07-15T00:30:00Z"
let destinationIdentifier = "America/Los_Angeles"
enum ConversionError: Error {
case invalidTimestamp(String)
case unknownTimeZone(String)
}
let inputFormatter = ISO8601DateFormatter()
inputFormatter.formatOptions = [.withInternetDateTime]
inputFormatter.timeZone = TimeZone(secondsFromGMT: 0)
guard let instant = inputFormatter.date(from: sourceTimestamp) else {
throw ConversionError.invalidTimestamp(sourceTimestamp)
}
guard let destination = TimeZone(identifier: destinationIdentifier) else {
throw ConversionError.unknownTimeZone(destinationIdentifier)
}
let destinationFormatter = ISO8601DateFormatter()
destinationFormatter.formatOptions = [.withInternetDateTime]
destinationFormatter.timeZone = destination
let publicationLabelStyle = Date.FormatStyle(
date: .long,
time: .complete,
locale: Locale(identifier: "en_GB"),
calendar: Calendar(identifier: .gregorian),
timeZone: destination
)
let publicationDateStyle = Date.FormatStyle(
date: .long,
time: .omitted,
locale: Locale(identifier: "en_GB"),
calendar: Calendar(identifier: .gregorian),
timeZone: destination
)
print("Source instant: \(inputFormatter.string(from: instant))")
print("Destination zone: \(destination.identifier)")
print("Destination timestamp: \(destinationFormatter.string(from: instant))")
print("Publication label: \(instant.formatted(publicationLabelStyle))")
print("Publication date: \(instant.formatted(publicationDateStyle))")
Uruchom zapisany program:
swift "$HOME/Desktop/publication-time.swift"
Dla podanych wartości znacznik czasu w docelowej strefie powinien mieć postać 2025-07-14T17:30:00-07:00, a data publikacji powinna wskazywać 14 lipca 2025 r. Czytelna etykieta zawiera również godzinę i informacje o strefie; jej dokładne brzmienie i interpunkcja zależą od systemu formatowania.
Program Swift sprawdzono na podstawie pobranej dokumentacji API, ale nie uruchomiono go w środowisku badawczym, ponieważ Swift nie był tam dostępny. Obliczenia przykładowych konwersji liczbowych sprawdzono osobno za pomocą interfejsów API daty, czasu i stref Pythona.
Które ustawienia wpływają na wynik?
Inicjalizator formattera przyjmuje osobne ustawienia języka i regionu, kalendarza oraz strefy czasowej. W tym przypadku en_GB wybiera konwencje formatowania brytyjskiej odmiany angielskiego, kalendarz gregoriański określa system dat, a destination wyznacza lokalną datę i godzinę. Zmień identyfikator strefy, gdy potrzebujesz innej strefy docelowej; zmień ustawienia regionalne, gdy potrzebujesz innej konwencji prezentacji. init(date:time:locale:calendar:timeZone:capitalizationContext:).
Wynik zawierający samą datę nadal pochodzi bezpośrednio z instant. Ustawienie time: .omitted usuwa godzinę z etykiety. Nie wymaga to utworzenia nowej chwili o północy.
Sprawdź przejścia przez północ i zmiany sezonowe przesunięcia
Użyj poniższych przykładowych danych syntetycznych, aby sprawdzić, czy wynik zachowuje zamierzony sposób działania. To przykładowe znaczniki czasu publikacji wraz z obliczonymi reprezentacjami w docelowych strefach.
| Pierwotna chwila | Docelowa strefa | Reprezentacja w docelowej strefie |
|---|---|---|
2025-01-15T00:30:00Z |
America/Los_Angeles |
2025-01-14T16:30:00-08:00 |
2025-07-15T00:30:00Z |
America/Los_Angeles |
2025-07-14T17:30:00-07:00 |
2025-07-15T00:30:00Z |
Asia/Tokyo |
2025-07-15T09:30:00+09:00 |
2025-07-15T23:30:00Z |
Asia/Tokyo |
2025-07-16T08:30:00+09:00 |
Pierwsze dwa wiersze pokazują, dlaczego stała korekta nie wystarcza dla nazwanej strefy docelowej: jej właściwe przesunięcie może zależeć od chwili. Dwa ostatnie wiersze pokazują, dlaczego wraz z godziną trzeba wyznaczyć pełną datę kalendarzową w docelowej strefie. Publikacja może przypadać na poprzedni dzień, ten sam dzień albo następny dzień.
Nazwane strefy korzystają z reguł bazy danych stref czasowych. IANA informuje, że macOS korzysta z tej bazy, a jej aktualizacje zwykle docierają do użytkowników wraz z aktualizacjami systemu operacyjnego. Jeśli wyniki różnią się między komputerami, przed zmianą pierwotnego znacznika czasu sprawdź ich identyfikatory stref i dostępne uaktualnienia. Dane o strefach czasowych i czasie letnim.
Co zrobić, gdy dane wejściowe lub wynik są nieoczekiwane?
Wykonaj po kolei następujące czynności kontrolne:
- Upewnij się, że dane źródłowe określają konkretną chwilę. Zachowaj ich
Zlub przesunięcie liczbowe. Data bez godziny albo lokalny odczyt zegara wymaga dodatkowych informacji. - Sprawdź identyfikator docelowej strefy. Użyj identyfikatora, takiego jak
America/Los_Angeles. Inicjalizator Apple zwracanildla nieznanego identyfikatora; ten program zgłasza błąd zamiast podstawiać inną strefę. - Sprawdź obsługiwany format znacznika czasu. Przykład skonfigurowano dla internetowych znaczników czasu z dokładnością do pełnej sekundy. Ułamki sekund lub inna reprezentacja danych wejściowych wymagają odpowiedniej konfiguracji parsera. Zachowaj pierwotne dane podczas wprowadzania tej zmiany.
- Porównaj liczbowy znacznik czasu w docelowej strefie. Przed analizowaniem interpunkcji sprawdź datę, godzinę i przesunięcie.
- Sprawdź, czy wszystkie wyniki korzystają z pierwotnej wartości
instant. Usuń każdy pośredni krok, który formatuje etykietę, a następnie analizuje ją w celu wykonania kolejnej konwersji. init(identifier:), ISO8601DateFormatter.
Zgodnie z RFC 9557 nazwaną strefę i przesunięcie można również umieścić razem w rozszerzonym znaczniku czasu. Istnienie tego standardu nie oznacza, że parser użyty w tym przykładzie obsługuje zapisane w nawiasach kwadratowych adnotacje dotyczące strefy. Jeśli celowo nie implementujesz i nie weryfikujesz obsługi rozszerzonego formatu danych wejściowych, przechowuj identyfikator docelowej strefy w osobnej wartości destinationIdentifier użytej powyżej. RFC 9557.
Tworząc etykietę publikacji, zachowaj pierwotną chwilę jako wartość wielokrotnego użytku i generuj z niej tekst dla każdej docelowej strefy. Dzięki temu możesz zmieniać docelową strefę, język wyświetlania lub widoczne szczegóły, zachowując tę samą chwilę publikacji we wszystkich etykietach.
Źródła
- Date — Apple
- Date.FormatStyle — Apple
- init(date:time:locale:calendar:timeZone:capitalizationContext:) — Apple
- ISO8601DateFormatter — Apple
- withInternetDateTime — Apple
- init(identifier:) — Apple
- Instalowanie narzędzi wiersza poleceń — Apple
- Tworzenie, otwieranie i konwertowanie dokumentów w TextEdit na Macu — Apple
- RFC 3339: Data i godzina w Internecie: znaczniki czasu — RFC Editor
- RFC 9557: Data i godzina w Internecie: znaczniki czasu z dodatkowymi informacjami — RFC Editor
- Dane o strefach czasowych i czasie letnim — IANA