ISO8601DateFormatter — είναι μια κλάση Foundation σε iOS και macOS, σχεδιασμένη για μορφοποίηση και ανάλυση ημερομηνιών στο διεθνές πρότυπο ISO 8601. Σύμφωνα με το Apple Developer Documentation, 2024, το ISO8601DateFormatter επεξεργάζεται αυτόματα μορφές με χιλιοστά του δευτερολέπτου, ζώνες ώρας και κλάσματα δευτερολέπτου χωρίς να χρειάζεται να ορίσετε χειροκίνητα το DateFormat. Σε αντίθεση με το DateFormatter, αυτή η κλάση δεν εξαρτάται από Locale και TimeZone — λειτουργεί αυστηρά σύμφωνα με την προδιαγραφή ISO 8601, καθιστώντας την ιδανική για ανταλλαγή ημερομηνιών μεταξύ διακομιστή και πελάτη. Η κλάση είναι διαθέσιμη από iOS 10 και macOS 10.12.
Κύρια σημεία
ISO8601DateFormatter — είναι μια εξειδικευμένη υποκλάση του Formatter στο Foundation που υλοποιεί αμφίδρομη μετατροπή μεταξύ Date και συμβολοσειράς σε μορφή ISO 8601. Το πρότυπο ISO 8601 (International Standard for the Representation of Dates and Times) καθορίζει τη διεθνή μορφή ανταλλαγής ημερομηνιών και ώρας: 2024-07-21T14:30:00+00:00. Σε αντίθεση με το DateFormatter, αυτή η κλάση δεν απαιτεί τον καθορισμό dateFormat και καθορίζει αυτόματα τη δομή της συμβολοσειράς βάσει των δεδομένων επιλογών.
Τα κύρια πλεονεκτήματα του ISO8601DateFormatter έναντι του DateFormatter: απουσία εξάρτησης από το locale (η ανάλυση λειτουργεί ίδια σε οποιαδήποτε συσκευή), ενσωματωμένη υποστήριξη για κλάσματα δευτερολέπτου (με οποιονδήποτε αριθμό δεκαδικών ψηφίων) και αυτόματος καθορισμός μορφής βάσει των μεταβιβαζόμενων επιλογών. Η κλάση επίσης χειρίζεται σωστά το επίθημα Z (ένδειξη UTC), ζώνες ώρας σε μορφή +HH:mm και μειωμένη ακρίβεια (μόνο ημερομηνία χωρίς ώρα).
Σύμφωνα με το ISO Specification (ISO 8601-1:2019), το πρότυπο υποστηρίζει τέσσερα επίπεδα ακρίβειας: έτος (2024), έτος-μήνας (2024-07), πλήρης ημερομηνία (2024-07-21) και ημερομηνία-ώρα με ζώνη ώρας (2024-07-21T14:30:00+00:00). Το ISO8601DateFormatter καλύπτει όλα αυτά τα επίπεδα μέσω συνδυασμού επιλογών μορφής, απαλλάσσοντας τον προγραμματιστή από τη χειροκίνητη κατασκευή συμβολοσειράς dateFormat.
Αρχή λειτουργίας του ISO8601DateFormatter βασίζεται στον συνδυασμό επιλογών bit (formatOptions), καθεμία από τις οποίες ενεργοποιεί ένα συγκεκριμένο στοιχείο ημερομηνίας ή ώρας στην έξοδο. Για παράδειγμα, η επιλογή .withFullDate ενεργοποιεί το έτος, τον μήνα και την ημέρα· .withTime — ώρες, λεπτά και δευτερόλεπτα. Συνδυάζοντας επιλογές, ο προγραμματιστής επιτυγχάνει το επιθυμητό επίπεδο ακρίβειας χωρίς να γράφει συμβολοσειρά dateFormat.
Εσωτερικά, το ISO8601DateFormatter χρησιμοποιεί τη βιβλιοθήκη ICU για ανάλυση, αλλά με σταθερούς κανόνες ISO 8601. Αυτό σημαίνει ότι αγνοεί τις ρυθμίσεις Locale και TimeZone που είναι εγκατεστημένες στη συσκευή — το αποτέλεσμα είναι πάντα προβλέψιμο. Για τη ρύθμιση της ζώνης ώρας χρησιμοποιείται η ιδιότητα timeZone, η οποία από προεπιλογή ισούται με UTC. Εάν το timeZone έχει οριστεί σε nil, χρησιμοποιείται η τοπική ώρα της συσκευής.
| Επιλογή | Περιγραφή | Παράδειγμα εξόδου |
|---|---|---|
| .withFullDate | Έτος, μήνας, ημέρα | 2024-07-21 |
| .withTime | Ώρες, λεπτά, δευτερόλεπτα | 14:30:00 |
| .withMilliseconds | Κλάσματα δευτερολέπτου (έως 3 χαρακτήρες) | .123 |
| .withFractionalSeconds | Κλάσματα δευτερολέπτου (οποιαδήποτε ακρίβεια) | .123456 |
| .withTimeZone | Ζώνη ώρας | +03:00 |
| .withColonSeparatorInTimeZone | Διαχωριστής : στη ζώνη ώρας | +03:00 (αντί +0300) |
| .withInternetDateTime | Πλήρης μορφή (date + time + tz) | 2024-07-21T14:30:00+00:00 |
Συνδυασμός επιλογών: Το .withInternetDateTime ισοδυναμεί με τον συνδυασμό .withFullDate, .withTime και .withTimeZone. Για ανάλυση συμβολοσειρών με χιλιοστά, προσθέστε .withFractionalSeconds. Είναι σημαντικό να θυμάστε ότι το .withMilliseconds περιορίζει τα κλάσματα δευτερολέπτου σε τρεις χαρακτήρες, ενώ το .withFractionalSeconds υποστηρίζει οποιαδήποτε ακρίβεια — από ένα έως εννέα δεκαδικά ψηφία.
Επιλογές μορφής του ISO8601DateFormatter χωρίζονται σε τρεις ομάδες: στοιχεία ημερομηνίας (withFullDate, withYear, withMonth, withDay, withWeekOfYear), στοιχεία ώρας (withTime, withHours, withMinutes, withSeconds) και πρόσθετες ρυθμίσεις (withMilliseconds, withFractionalSeconds, withTimeZone, withColonSeparatorInTimeZone, withDashSeparatorInDate, withFullTime). Συνδυάζοντάς τα, μπορείτε να αποκτήσετε σχεδόν οποιαδήποτε υπομορφή ISO 8601.
Σημαντική απόχρωση: Τα .withFractionalSeconds και .withMilliseconds είναι αμοιβαία αποκλειόμενα — αν έχουν οριστεί και τα δύο, εφαρμόζεται το .withFractionalSeconds. Για ανάλυση χιλιοστών από δεδομένα διακομιστή, συνιστάται το .withFractionalSeconds, καθώς πολλοί διακομιστές στέλνουν κλάσματα δευτερολέπτου με τρεις, έξι ή εννέα χαρακτήρες και το .withFractionalSeconds χειρίζεται οποιοδήποτε μήκος.
import Foundation
// Διαμόρφωση ISO8601DateFormatter
let formatter = ISO8601DateFormatter()
formatter.timeZone = TimeZone(secondsFromGMT: 0)
// Διαφορετικοί συνδυασμοί επιλογών μορφής
formatter.formatOptions = [.withFullDate]
let dateOnly = formatter.string(from: Date())
print("Ημερομηνία: \(dateOnly)")
formatter.formatOptions = [.withFullDate, .withTime]
let dateTime = formatter.string(from: Date())
print("DateTime: \(dateTime)")
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
let full = formatter.string(from: Date())
print("Πλήρες: \(full)")
// Ανάλυση συμβολοσειράς με χιλιοστά
let serverString = "2024-07-21T14:30:00.123456+03:00"
if let parsed = formatter.date(from: serverString) {
print("Αναλύθηκε: \(parsed)")
}
Βασική χρήση του ISO8601DateFormatter συνοψίζεται στη δημιουργία μιας παρουσίας, ρύθμιση timeZone (συνιστάται UTC για δεδομένα διακομιστή) και formatOptions, μετά την οποία μπορείτε να καλέσετε string(from:) για μορφοποίηση και date(from:) για ανάλυση. Σε αντίθεση με το DateFormatter, δεν χρειάζεται να ανησυχείτε για το Locale — η κλάση αγνοεί τις τοπικές ρυθμίσεις.
import Foundation
let formatter = ISO8601DateFormatter()
// Ανάλυση διαφορετικών μορφών ISO 8601
let strings: [String] = [
"2024-07-21T14:30:00Z",
"2024-07-21T14:30:00+03:00",
"2024-07-21T14:30:00.123Z",
"2024-07-21"
]
for str in strings {
if let autoParsed = formatter.date(from: str) {
print("Αναλύθηκε '\(str)': \(autoParsed)")
} else {
// Χρήση withFullDate για συμβολοσειρές μόνο ημερομηνίας
formatter.formatOptions = [.withFullDate]
if let fallback = formatter.date(from: str) {
print("Fallback αναλύθηκε '\(str)': \(fallback)")
}
formatter.formatOptions = [.withInternetDateTime]
}
}
// Σειριοποίηση σε RFC 3339 (GitHub API)
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
let rfc3339 = formatter.string(from: Date())
print("RFC 3339: \(rfc3339)")
Ανάλυση ημερομηνιών με κλασματικά δευτερόλεπτα μεταβλητού μήκους — χαρακτηριστικό πολλών σύγχρονων API. Ο διακομιστής μπορεί να στείλει είτε 2024-07-21T14:30:00.123Z (3 χαρακτήρες) είτε 2024-07-21T14:30:00.123456Z (6 χαρακτήρες). Το ISO8601DateFormatter με την επιλογή .withFractionalSeconds θα χειριστεί σωστά και τις δύο παραλλαγές, ενώ το DateFormatter με dateFormat = „yyyy-MM-dd’T’HH:mm:ss.SSSZ” θα επεξεργαστεί μόνο τριψήφια χιλιοστά.
import Foundation
let variantFormatter = ISO8601DateFormatter()
variantFormatter.formatOptions = [
.withInternetDateTime,
.withFractionalSeconds
]
// Διαφορετική ακρίβεια κλασματικών δευτερολέπτων
let variants: [String] = [
"2024-07-21T14:30:00.1Z",
"2024-07-21T14:30:00.12Z",
"2024-07-21T14:30:00.123Z",
"2024-07-21T14:30:00.123456Z",
"2024-07-21T14:30:00.123456789Z"
]
for variant in variants {
if let parsed = variantFormatter.date(from: variant) {
print("OK: \(variant) -> \(parsed)")
} else {
print("FAIL: \(variant)")
}
}
// Χρήση withMilliseconds (μόνο 3 ψηφία)
variantFormatter.formatOptions = [
.withInternetDateTime,
.withMilliseconds
]
let milliParsed = variantFormatter.string(from: Date())
print("Με χιλιοστά: \(milliParsed)")
Δοκιμή ανάλυσης όλων των παραλλαγών: ο παραπάνω κώδικας δείχνει ότι το ISO8601DateFormatter με .withFractionalSeconds επεξεργάζεται με επιτυχία κλάσματα δευτερολέπτου οποιουδήποτε μήκους από 1 έως 9 χαρακτήρες. Αυτό είναι σημαντικό για συμβατότητα με διαφορετικές πλατφόρμες διακομιστή: το .NET συχνά δημιουργεί 7 χαρακτήρες (100 νανοδευτερόλεπτα ticks), Python — 6, Java — 3 ή 9 ανάλογα με την έκδοση.
DateFormatter μπορεί επίσης να αναλύσει ISO 8601, αλλά απαιτεί χειροκίνητη ρύθμιση dateFormat, locale και timeZone. Το κύριο πρόβλημα είναι ότι το DateFormatter εξαρτάται από το Locale, και αν δεν οριστεί en_US_POSIX, η ανάλυση μπορεί να αποτύχει σε χρήστες από περιοχές με μη τυπικές μορφές ημερομηνίας. Το ISO8601DateFormatter λύνει αυτό το πρόβλημα σε επίπεδο αρχιτεκτονικής: δεν χρησιμοποιεί Locale.
| Παράμετρος | ISO8601DateFormatter | DateFormatter |
|---|---|---|
| Ρύθμιση Locale | Δεν απαιτείται (αγνοεί) | en_US_POSIX υποχρεωτικό |
| DateFormat | Αυτόματος (μέσω επιλογών) | Χειροκίνητη συμβολοσειρά format |
| Κλασματικά δευτερόλεπτα | Οποιαδήποτε ακρίβεια (.withFractionalSeconds) | Σταθερό SSS |
| Επίθημα Z | Σωστός χειρισμός | Μέσω dateFormat |
| Απόδοση | Υψηλότερη (εξειδικευμένο) | Χαμηλότερη (γενικό) |
| Πρότυπο | Μόνο ISO 8601 | Οποιαδήποτε μορφή |
| Έκδοση iOS | iOS 10+ | iOS 2+ |
Πότε να χρησιμοποιείτε το DateFormatter: αν χρειάζεται να μορφοποιήσετε μια ημερομηνία σε μη ISO 8601 μορφή (π.χ. «21 Ιουλίου 2024» για UI) ή αν απαιτείται υποστήριξη για iOS 9 και παλαιότερα. Για όλες τις εργασίες ανταλλαγής ημερομηνιών με τον διακομιστή, χρησιμοποιήστε το ISO8601DateFormatter — είναι ασφαλέστερο, αποδοτικότερο και απαιτεί λιγότερο κώδικα. Το DateFormatter για ISO 8601 είναι πηγή πιθανών σφαλμάτων που σχετίζονται με το locale και τις τοπικές ρυθμίσεις.
Μετάβαση από DateFormatter σε ISO8601DateFormatter: αντικαταστήστε τη δημιουργία DateFormatter + ρύθμιση dateFormat + locale + timeZone με τη δημιουργία ISO8601DateFormatter + ρύθμιση formatOptions + timeZone. Η ανάλυση της συμβολοσειράς παραμένει αμετάβλητη μέσω date(from:). Για συμβατότητα προς τα πίσω, μπορείτε να χρησιμοποιήσετε #available(iOS 10, *) με fallback στο DateFormatter.
Ξεχασμένη ρύθμιση formatOptions έχει ως αποτέλεσμα ο formatter να χρησιμοποιεί την προεπιλεγμένη τιμή — .withInternetDateTime. Εάν ο διακομιστής στέλνει μόνο ημερομηνία χωρίς ώρα (2024-07-21), η ανάλυση θα επιστρέψει nil. Ελέγχετε πάντα ότι τα formatOptions καλύπτουν όλες τις πιθανές μορφές που μπορεί να έρθουν από τον διακομιστή. Για API με μεταβλητές μορφές, χρησιμοποιήστε δοκιμές fallback με διαφορετικούς συνδυασμούς επιλογών.
Σύγχυση μεταξύ withMilliseconds και withFractionalSeconds — συνηθισμένο λάθος κατά την ανάλυση ημερομηνιών με κλάσματα δευτερολέπτου. Το withMilliseconds αναμένει ακριβώς 3 δεκαδικά ψηφία. Εάν ο διακομιστής στείλει 6 ψηφία (μικροδευτερόλεπτα), η ανάλυση με withMilliseconds θα αποτύχει. Χρησιμοποιήστε το .withFractionalSeconds για συμβατότητα με οποιονδήποτε αριθμό χαρακτήρων. Το .withFractionalSeconds εμφανίστηκε στο iOS 13· για παλαιότερες εκδόσεις, χρησιμοποιήστε DateFormatter με dateFormat.
Αγνόηση της ζώνης ώρας — άλλο ένα συνηθισμένο πρόβλημα. Εάν ο διακομιστής στέλνει ημερομηνία με ζώνη ώρας (+03:00) και ο formatter έχει ρυθμιστεί σε UTC, η ανάλυση δεν θα αποτύχει, αλλά το αποτέλεσμα θα είναι σε UTC. Οι προγραμματιστές συχνά αναμένουν ότι η Date θα διατηρήσει τη ζώνη ώρας, αλλά η Date είναι μια απόλυτη χρονική στιγμή και δεν αποθηκεύει πληροφορίες ζώνης ώρας. Για σωστή εμφάνιση, αποθηκεύστε τη ζώνη ώρας ξεχωριστά ή χρησιμοποιήστε ISO8601DateFormatter με σωστό timeZone.
Σύμφωνα με το Apple Forum (2024), περίπου το 20% των ερωτήσεων σχετικά με το ISO8601DateFormatter αφορά τη μορφή όπου τα δευτερόλεπτα είναι προαιρετικά. Το πρότυπο ISO 8601 επιτρέπει μορφή χωρίς δευτερόλεπτα: 2024-07-21T14:30+03:00. Το ISO8601DateFormatter με .withInternetDateTime δεν υποστηρίζει αυτή τη μορφή — για την ανάλυσή της απαιτείται DateFormatter με dateFormat = «yyyy-MM-dd’T’HH:mmZ». Αυτός ο περιορισμός πρέπει να λαμβάνεται υπόψη όταν εργάζεστε με API που χρησιμοποιούν συντομευμένη μορφή ώρας.
Συχνές ερωτήσεις
ISO8601DateFormatter — εξειδικευμένη κλάση Foundation για μορφοποίηση και ανάλυση ημερομηνιών σε μορφή ISO 8601, διαθέσιμη από iOS 10. Επεξεργάζεται αυτόματα τυπικές μορφές χωρίς χειροκίνητο ορισμό dateFormat.
ISO8601DateFormatter δεν εξαρτάται από το Locale, χρησιμοποιεί επιλογές αντί για dateFormat και χειρίζεται σωστά κλάσματα δευτερολέπτου οποιουδήποτε μήκους. Το DateFormatter είναι καθολικό αλλά απαιτεί χειροκίνητη ρύθμιση και είναι επιρρεπές σε σφάλματα που σχετίζονται με τοπικές ρυθμίσεις.
Χρησιμοποιήστε την επιλογή .withFractionalSeconds — υποστηρίζει 1 έως 9 δεκαδικά ψηφία. Μην χρησιμοποιείτε .withMilliseconds αν η ακρίβεια μπορεί να διαφέρει. Το .withFractionalSeconds είναι διαθέσιμο από iOS 13.
Από προεπιλογή UTC. Για αλλαγή, ορίστε την ιδιότητα timeZone. Αν timeZone = nil, χρησιμοποιείται η τοπική ώρα της συσκευής. Κατά την ανάλυση συμβολοσειράς με ρητή ζώνη ώρας σε μορφή +HH:MM, ο formatter τη λαμβάνει αυτόματα υπόψη.
Επειδή τα προεπιλεγμένα formatOptions = .withInternetDateTime, που αναμένει ημερομηνία + ώρα + ζώνη ώρας. Για ανάλυση μόνο ημερομηνίας, ορίστε formatOptions = [.withFullDate]. Για υποστήριξη και των δύο παραλλαγών, χρησιμοποιήστε fallback με διαφορετικές επιλογές.
Σύνοψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης