RelativeDateTimeFormatter is een Foundation-klasse in iOS en macOS die absolute datums omzet in leesbare relatieve formuleringen: „5 minuten geleden“, „gisteren“, „over 3 dagen“. Volgens Apple Developer Documentation, 2024 selecteert RelativeDateTimeFormatter automatisch de geschikte eenheid (seconden, minuten, uren, dagen) en lokaliseert de uitvoer in de taal van de huidige locale van het apparaat. In tegenstelling tot het handmatig berekenen van het verschil tussen datums via Calendar, houdt deze klasse rekening met de taalkundige kenmerken van elke taal: voor sommige talen worden telwoorden verbogen, voor andere wordt een speciale vorm gebruikt voor het woord „gisteren“. De klasse is beschikbaar vanaf iOS 13 en macOS 10.15.
Het belangrijkste
RelativeDateTimeFormatter is een subklasse van Formatter in Foundation die Date (of het verschil in seconden) accepteert en een gelokaliseerde tekenreeks met relatieve tijd retourneert. Voor een datum 5 minuten eerder dan de huidige retourneert het bijvoorbeeld „5 minuten geleden“ voor ru_RU of „5 minutes ago“ voor en_US. De klasse ondersteunt drie tijdscontexten: verleden (past), toekomst (future) en heden (present).
De interne logica van RelativeDateTimeFormatter gebruikt Calendar en Locale om het verschil tussen datums te berekenen en de juiste grammaticale vorm te kiezen. Voor het Nederlands verbuigt de klasse telwoorden correct: „1 minuut geleden“, „2 minuten geleden“, „5 minuten geleden“. Voor het Engels kiest het tussen „minute ago“ en „minutes ago“. Deze functionaliteit is gebaseerd op ICU-gegevens (International Components for Unicode) en vereist geen extra configuratie van de ontwikkelaar.
Volgens Apple WWDC 2019 werd RelativeDateTimeFormatter onderdeel van het framework om lokalisatie te vereenvoudigen — vóór de komst ervan moesten ontwikkelaars het datumsverschil handmatig berekenen en gelokaliseerde tekenreeksen invoegen via String.localizedStringWithFormat. Dit leidde tot verbuigingsfouten (vooral voor Slavische en Arabische talen) en een onjuiste keuze van meeteenheden.
Het werkingsalgoritme van RelativeDateTimeFormatter bestaat uit drie stappen: het berekenen van het verschil tussen de doorgegeven datum en het huidige moment, het kiezen van de geschikte eenheid (de grootste die geen nul oplevert) en het formatteren met inachtneming van de locale. Voor een verschil van 3720 seconden (1 uur 2 minuten) wordt bijvoorbeeld de eenheid „uur“ gekozen en het resultaat is „1 uur geleden“, niet „62 minuten geleden“.
Eenheden worden gekozen volgens het principe van de „grootste niet-nul“: als het verschil groter is dan 86400 seconden (1 dag), worden dagen gebruikt; als het groter is dan 604800 (1 week) — weken, enzovoort. Dit algoritme garandeert dat het resultaat altijd natuurlijk leest: in plaats van „518400 seconden geleden“ ziet de gebruiker „6 dagen geleden“. De exacte grenzen van de eenheden worden bepaald door de kalender van de huidige locale.
| Verschilbereik | Eenheid | Voorbeeld voor ru_RU |
|---|---|---|
| 0–59 seconden | Seconds | 30 seconden geleden |
| 1–59 minuten | Minutes | 5 minuten geleden |
| 1–23 uur | Hours | 3 uur geleden |
| 1–6 dagen | Days | 2 dagen geleden |
| 7–27 dagen | Weeks | 1 week geleden |
| 28 dagen–11 maanden | Months | 3 maanden geleden |
| 12+ maanden | Years | 1 jaar geleden |
De formatteringscontext bepaalt het einde van de zin. Voor het verleden: „geleden“ (Nederlands), „ago“ (Engels). Voor de toekomst: „over 3 dagen“ (Nederlands), „in 3 days“ (Engels). Voor het heden: „nu“ (Nederlands), „now“ (Engels). De context wordt ingesteld via de methode localizeString(fromTimeInterval:) of rechtstreeks via string(from: Date).
RelativeDateTimeFormatter biedt verschillende instellingen om de uitvoer te beheren: de eigenschap unitsStyle bepaalt de opmaakstijl (numeric, abbreviated, full, spellOut), en maximumUnitCount beperkt het aantal weergegeven eenheden. Met maximumUnitCount = 1 wordt een verschil van 1 uur 30 minuten bijvoorbeeld weergegeven als „1 uur geleden“ in plaats van „1 uur 30 minuten geleden“.
Eenheidsbeperking: standaard toont RelativeDateTimeFormatter slechts één (de grootste) eenheid. Door maximumUnitCount = 2 in te stellen, wordt de volgende eenheid geactiveerd voor een nauwkeurigere beschrijving: „1 uur 30 minuten geleden“. Dit kan de tekenreeks echter overmatig lang maken voor korte berichten (push, meldingen). Voor UI wordt aanbevolen maximumUnitCount = 1 te behouden.
import Foundation
let formatter = RelativeDateTimeFormatter()
// Configureer stijlen
formatter.unitsStyle = .numeric
formatter.maximumUnitCount = 1
// Voorbeelden met verschillende datums
let fiveMinAgo = Date().addingTimeInterval(-300)
print("5 min geleden: \(formatter.localizedString(for: fiveMinAgo, relativeTo: Date()))")
let twoDaysLater = Date().addingTimeInterval(172800)
print("over 2 dagen: \(formatter.localizedString(for: twoDaysLater, relativeTo: Date()))")
// Verkorte stijl
formatter.unitsStyle = .abbreviated
let oneWeekAgo = Date().addingTimeInterval(-604800)
print("Verkort: \(formatter.localizedString(for: oneWeekAgo, relativeTo: Date()))")
// Volledige stijl (uitgeschreven)
formatter.unitsStyle = .full
let threeHours = Date().addingTimeInterval(10800)
print("Volledig: \(formatter.localizedString(for: threeHours, relativeTo: Date()))")
Stijlkeuze voor verschillende contexten: gebruik voor de nieuwsfeed .numeric met maximumUnitCount = 1 — dit is de standaard voor Twitter, Instagram en Facebook. Gebruik voor Accessibility (VoiceOver) .full — uitgeschreven getallen lezen natuurlijker. Gebruik voor compacte elementen (meldingshoek, statusbalk) .abbreviated om ruimte te besparen.
Basisgebruik van RelativeDateTimeFormatter komt neer op het maken van een instantie, het configureren van eigenschappen en het aanroepen van een van de formatteringsmethoden. De belangrijkste methoden: localizedString(for:relativeTo:) — voor een paar datums, localizedString(fromTimeInterval:) — voor het verschil in seconden, en string(for:) — voor Date met automatische context (verleden/toekomst).
import Foundation
let formatter = RelativeDateTimeFormatter()
formatter.unitsStyle = .numeric
formatter.maximumUnitCount = 1
// UI-voorbeelden voor sociale netwerken
let postDates: [(title: String, date: Date)] = [
("Just now", Date().addingTimeInterval(-30)),
("5 min ago", Date().addingTimeInterval(-300)),
("Yesterday", Date().addingTimeInterval(-90000)),
("Last week", Date().addingTimeInterval(-700000)),
("Last year", Date().addingTimeInterval(-32000000))
]
for (title, postDate) in postDates {
let relative = formatter.localizedString(
for: postDate,
relativeTo: Date()
)
print("\(title): \(relative)")
}
// Toekomstige datums
let reminderFormatter = RelativeDateTimeFormatter()
reminderFormatter.unitsStyle = .abbreviated
let inOneHour = Date().addingTimeInterval(3600)
let reminderText = reminderFormatter.localizedString(
for: inOneHour,
relativeTo: Date()
)
print("Herinnering: \(reminderText)")
Het scenario „zojuist“ verwerken — RelativeDateTimeFormatter heeft geen ingebouwde ondersteuning voor de uitdrukking „zojuist“ voor zeer kleine intervallen. Voor een verschil kleiner dan 5 seconden retourneert het „0 seconden geleden“, wat er niet mooi uitziet in de UI. Het wordt aanbevolen de formatter-aanroep in voorwaardelijke logica te wikkelen: als het verschil kleiner is dan de ingestelde drempel (bijvoorbeeld 5 seconden) — geef „zojuist“ handmatig weer, anders geef de datum door aan de formatter.
import Foundation
func relativeTimeString(from date: Date) -> String {
let interval = Date().timeIntervalSince(date)
// Drempel „zojuist“
if interval < 5 {
return "just now"
}
// Drempel „vandaag“
if interval < 60 {
return "just now"
}
let formatter = RelativeDateTimeFormatter()
formatter.unitsStyle = .numeric
formatter.maximumUnitCount = 1
// Weergave zonder achtervoegsel „geleden“
return formatter.localizedString(
for: date,
relativeTo: Date()
)
}
print(relativeTimeString(from: Date().addingTimeInterval(-3)))
print(relativeTimeString(from: Date().addingTimeInterval(-120)))
print(relativeTimeString(from: Date().addingTimeInterval(-3600)))
De methode string(fromTimeInterval:) accepteert het verschil in seconden en bepaalt automatisch de context (positieve waarde — toekomst, negatieve — verleden). Dit is handig wanneer het verschil al bekend is (bijvoorbeeld ontvangen van de server als unix timestamp). In dit geval hoeft u geen Date te maken — het verschil wordt rechtstreeks doorgegeven.
RelativeDateTimeFormatter lokaliseert de uitvoer automatisch op basis van Locale.current. Om de formatteringstaal te wijzigen, stelt u de eigenschap locale in — in tegenstelling tot DateFormatter is locale voor RelativeDateTimeFormatter niet vast en kan deze per aanroep worden gewijzigd. Dit maakt het mogelijk relatieve datums in een andere taal dan de interfacetaal weer te geven (bijvoorbeeld inhoud in de oorspronkelijke taal).
De complexiteit van het lokaliseren van relatieve datums ligt in de grammaticale kenmerken van verschillende talen. Het Russisch vereist verschillende telwoordvormen: „1 minuut“, „2 minuten“, „5 minuten“. Het Arabisch gebruikt de meervoudsvorm voor getallen van 3 tot 10 en speciale vormen voor 11+. Het Chinees heeft helemaal geen verbuiging, wat de taak vereenvoudigt. RelativeDateTimeFormatter dekt al deze gevallen af via ICU-regels, zonder dat extra code nodig is.
import Foundation
let formatter = RelativeDateTimeFormatter()
formatter.unitsStyle = .numeric
formatter.maximumUnitCount = 1
let targetDate = Date().addingTimeInterval(-7200) // 2 uur geleden
// Verschillende locales
let locales: [String] = ["ru_RU", "en_US", "de_DE", "fr_FR", "ja_JP", "ar_SA"]
for identifier in locales {
formatter.locale = Locale(identifier: identifier)
let result = formatter.localizedString(
for: targetDate,
relativeTo: Date()
)
print("\(identifier): \(result)")
}
// Controleer Russische verbuiging
formatter.locale = Locale(identifier: "ru_RU")
let intervals: [TimeInterval] = [-60, -120, -180, -300]
for interval in intervals {
let date = Date().addingTimeInterval(interval)
print("\(-Int(interval / 60)) min: \(formatter.localizedString(for: date, relativeTo: Date()))")
}
Belangrijk detail: RelativeDateTimeFormatter negeert TimeZone bij het berekenen van het verschil voor .numeric-instellingen — het gebruikt het absolute verschil in seconden. Voor de .full-stijl (met uitgeschreven getallen) en speciale gevallen (gisteren, vandaag) wordt TimeZone echter wel meegenomen. Stel TimeZone altijd expliciet in voor consistentie, vooral als de app met serverdatums in UTC werkt.
TimeZone negeren bij het berekenen van relatieve datums — een veelvoorkomende fout bij het werken met serverdatums. Als de server Date in UTC verzendt en RelativeDateTimeFormatter TimeZone.current gebruikt, kan het verschil onjuist worden berekend voor datums dicht bij het huidige moment. Het wordt aanbevolen altijd formatter.timeZone = TimeZone(secondsFromGMT: 0) in te stellen voor servergegevens.
Onjuiste eenheidskeuze voor korte intervallen — RelativeDateTimeFormatter rondt het verschil af op de grootste eenheid. Voor 25 uur is het resultaat „1 dag geleden“, wat de gebruiker kan misleiden. Als hoge nauwkeurigheid vereist is (bijvoorbeeld voor afteltimers), gebruik dan DateComponentsFormatter in plaats van RelativeDateTimeFormatter — deze kan meerdere eenheden tegelijk weergeven.
Geen controle op negatieve TimeInterval — als een datum in de toekomst als verleden wordt doorgegeven (negatieve waarde in string(fromTimeInterval:)), kan de formatter een onjuiste tekenreeks retourneren. Controleer altijd het teken van het interval vóór doorgeven aan de formatter, vooral bij het werken met servergegevens waar de tijdzone de berekening kan verstoren.
Volgens Hacker News (2024) is een van de meest besproken problemen van RelativeDateTimeFormatter het ontbreken van ingebouwde ondersteuning voor „gisteren“ en „vandaag“ in het Engels. In plaats van „gisteren“ retourneert de formatter voor een verschil van 90000 seconden „1 dag geleden“. Voor het Russisch bestaat dit probleem niet — „1 dag geleden“ klinkt natuurlijk, maar voor een Engelstalige UI is „yesterday“ te prefereren. Deze functionaliteit wordt niet ondersteund en vereist handmatige controle via Calendar.isDateInToday/Yesterday.
Veelgestelde vragen
RelativeDateTimeFormatter — Foundation-klasse voor het weergeven van datums in relatief formaat: „5 minuten geleden“, „over 2 dagen“. Beschikbaar vanaf iOS 13 en macOS 10.15.
Volgens het principe van de grootste niet-nul eenheid — seconden, minuten, uren, dagen, weken, maanden of jaren. Voor een verschil van 3720 seconden (1 uur 2 minuten) wordt bijvoorbeeld de eenheid „uur“ gekozen, niet „minuten“.
Stel de eigenschap locale in op de gewenste Locale-instantie. Standaard wordt Locale.current gebruikt. Voorbeeld: formatter.locale = Locale(identifier: "de_DE") voor het Duits.
.numeric — de volledige vorm („3 dagen geleden“), .abbreviated — verkort („3 dagen geleden“). De keuze hangt af van de context: numeric voor de hoofd-UI, abbreviated voor compacte elementen.
Voeg een handmatige controle toe voor een interval kleiner dan 5–10 seconden. RelativeDateTimeFormatter ondersteunt „zojuist“ niet — voor kleine intervallen retourneert het „0 seconden geleden“. Gebruik voorwaardelijke logica met een drempel.
Samenvatting
We ontwikkelen een mobiele applicatie turnkey
IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.
Lees ook