RelativeDateTimeFormatter: a lényeg, relatív dátumok és Swift

Szerző: IT Sectr Megjelenés: 2026-07-13 Olvasási idő: 10 perc

A RelativeDateTimeFormatter egy Foundation-osztály iOS-ben és macOS-ben, amely az abszolút dátumokat emberi számára olvasható relatív megfogalmazásokká alakítja: „5 perce“, „tegnap“, „3 nap múlva“. A Apple Developer Documentation, 2024 szerint a RelativeDateTimeFormatter automatikusan kiválasztja a megfelelő egységet (másodperc, perc, óra, nap), és az eszköz aktuális lokáljának nyelvén lokalizálja a kimenetet. Ellentétben a Calendar segítségével történő manuális dátumkülönbség-számítással, ez az osztály figyelembe veszi minden nyelv nyelvi sajátosságait: egyes nyelveknél a számnevek ragozódnak, másoknál a „tegnap“ szóhoz speciális alakot használ. Az osztály iOS 13 és macOS 10.15 verziótól érhető el.

A legfontosabb

  • RelativeDateTimeFormatter — osztály a relatív dátumok megjelenítésére iOS-ben és macOS-ben (iOS 13+)
  • Lokalizált kimenet — automatikusan az aktuális lokál nyelvén választ megfogalmazásokat
  • Három kontextustípus — past (ezelőtt), future (múlva), present (most) különböző megfogalmazásokkal
  • Automatikus egységválasztás — másodperc, perc, óra, nap, hét, hónap, év
  • Stílus beállítása — numeric (3 nap múlva) vagy abbreviated (3 nap múlva)

Mi az a RelativeDateTimeFormatter?

RelativeDateTimeFormatter a Formatter alosztálya a Foundationben, amely elfogad egy Date-et (vagy másodpercben kifejezett különbséget), és lokalizált karakterláncot ad vissza relatív idővel. Például egy 5 perccel korábbi dátumhoz az ru_RU esetén az „5 perce“, az en_US esetén pedig az „5 minutes ago“ szöveget adja vissza. Az osztály három időbeli kontextust támogat: múlt (past), jövő (future) és jelen (present).

A RelativeDateTimeFormatter belső logikája a Calendar és a Locale segítségével számolja ki a dátumok közötti különbséget, és választja ki a helyes nyelvtani alakot. A magyar nyelvhez az osztály helyesen ragozza a számneveket: „1 perce“, „2 perce“, „5 perce“. Az angolhoz — az „minute ago“ és „minutes ago“ közül választ. Ez a funkció az ICU (International Components for Unicode) adataira épül, és nem igényel további beállítást a fejlesztő részéről.

Az Apple WWDC 2019 szerint a RelativeDateTimeFormatter a lokalizáció egyszerűsítésére vált a keretrendszer részévé — megjelenése előtt a fejlesztőknek manuálisan kellett kiszámítaniuk a dátumkülönbséget, és a String.localizedStringWithFormat segítségével lokalizált karakterláncokat beilleszteni. Ez ragozási hibákhoz (különösen a szláv és arab nyelveknél) és a mértékegységek helytelen kiválasztásához vezetett.

Hogyan jeleníti meg a RelativeDateTimeFormatter az „5 perce“ szöveget?

A működés algoritmusa a RelativeDateTimeFormatter három lépésből áll: az átadott dátum és az aktuális pillanat közötti különbség kiszámítása, a megfelelő egység kiválasztása (a legnagyobb, amely nem ad nullát), és a lokalizációt figyelembe vevő formázás. Például a 3720 másodperces (1 óra 2 perc) különbséghez az „óra“ egység kerül kiválasztásra, az eredmény pedig „1 órával ezelőtt“ lesz, nem „62 perccel ezelőtt“.

Az egységek a „legnagyobb nem nulla“ elve alapján kerülnek kiválasztásra: ha a különbség nagyobb, mint 86400 másodperc (1 nap), napokat használunk; ha nagyobb, mint 604800 (1 hét) — heteket, és így tovább. Ez az algoritmus garantálja, hogy az eredmény mindig természetesen olvasható: az „518400 másodperccel ezelőtt“ helyett a felhasználó „6 nappal ezelőtt“-et lát. Az egységek pontos határait az aktuális lokál naptára határozza meg.

Különbség-tartományEgységPélda ru_RU esetén
0–59 másodpercSeconds30 másodperccel ezelőtt
1–59 percMinutes5 perce
1–23 óraHours3 órával ezelőtt
1–6 napDays2 nappal ezelőtt
7–27 napWeeks1 hete
28 nap–11 hónapMonths3 hónapja
12+ hónapYears1 éve

A formázási kontextus határozza meg a kifejezés végét. A múlthoz: „ezelőtt“ (magyar), „ago“ (angol). A jövőhöz: „3 nap múlva“ (magyar), „in 3 days“ (angol). A jelenhez: „most“ (magyar), „now“ (angol). A kontextus a localizeString(fromTimeInterval:) metódussal vagy közvetlenül a string(from: Date) segítségével állítható be.

Az egységek és stílusok beállításai

RelativeDateTimeFormatter több beállítást kínál a kimenet szabályozására: a unitsStyle tulajdonság határozza meg a formázási stílust (numeric, abbreviated, full, spellOut), a maximumUnitCount pedig korlátozza a megjelenített egységek számát. Például a maximumUnitCount = 1 beállításnál az 1 óra 30 perc különbség „1 órával ezelőtt“ formában jelenik meg az „1 óra 30 perce“ helyett.

Formázási stílusok

  • .numeric — teljes numerikus érték: „3 nappal ezelőtt“, „2 hét múlva“. Alapértelmezésben ajánlott a UI-hoz
  • .abbreviated — rövidített forma: „3 nappal ezelőtt“, „2 hét múlva“. Kompakt megjelenítéshez táblázatokban és listákban
  • .full — betűkkel írt forma számok nélkül: „három nappal ezelőtt“. Accessibility és hanginterfészek számára
  • .spellOut — betűkkel írt forma alternatív írásmóddal: „three days ago“. Ritkán használt, főleg speciális célokra

Az egységek korlátozása: alapértelmezésben a RelativeDateTimeFormatter csak egy (a legnagyobb) egységet jelenít meg. A maximumUnitCount = 2 beállítás aktiválja a következő egységet a pontosabb leírás érdekében: „1 óra 30 perce“. Ez azonban túl hosszúvá teheti a karakterláncot a rövid üzenetekhez (push, értesítések). A UI-hoz ajánlott megtartani a maximumUnitCount = 1 értéket.

swift
import Foundation

let formatter = RelativeDateTimeFormatter()

// Stílusok beállítása
formatter.unitsStyle = .numeric
formatter.maximumUnitCount = 1

// Példák különböző dátumokkal
let fiveMinAgo = Date().addingTimeInterval(-300)
print("5 perce: \(formatter.localizedString(for: fiveMinAgo, relativeTo: Date()))")

let twoDaysLater = Date().addingTimeInterval(172800)
print("2 nap múlva: \(formatter.localizedString(for: twoDaysLater, relativeTo: Date()))")

// Rövidített stílus
formatter.unitsStyle = .abbreviated
let oneWeekAgo = Date().addingTimeInterval(-604800)
print("Rövidített: \(formatter.localizedString(for: oneWeekAgo, relativeTo: Date()))")

// Teljes stílus (betűkkel)
formatter.unitsStyle = .full
let threeHours = Date().addingTimeInterval(10800)
print("Teljes: \(formatter.localizedString(for: threeHours, relativeTo: Date()))")

Stílusválasztás különböző kontextusokhoz: a hírfolyamhoz használjon .numeric-ot maximumUnitCount = 1 értékkel — ez a Twitter, Instagram és Facebook szabványa. Az Accessibility (VoiceOver) számára használjon .full-t — a betűkkel írt számok természetesebben olvashatók. A kompakt elemekhez (értesítési sarok, állapotsor) használjon .abbreviated-et a helymegtakarítás érdekében.

RelativeDateTimeFormatter Swiftben: példák

Alapvető használat a RelativeDateTimeFormatter egy példány létrehozásából, a tulajdonságok beállításából és az egyik formázási metódus meghívásából áll. A főbb metódusok: localizedString(for:relativeTo:) — dátumok párjához, localizedString(fromTimeInterval:) — a másodpercben kifejezett különbséghez, és string(for:) — Date-hez automatikus kontextussal (múlt/jövő).

swift
import Foundation

let formatter = RelativeDateTimeFormatter()
formatter.unitsStyle = .numeric
formatter.maximumUnitCount = 1

// Közösségi hálózat UI példái
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)")
}

// Jövőbeli dátumok
let reminderFormatter = RelativeDateTimeFormatter()
reminderFormatter.unitsStyle = .abbreviated
let inOneHour = Date().addingTimeInterval(3600)
let reminderText = reminderFormatter.localizedString(
    for: inOneHour,
    relativeTo: Date()
)
print("Emlékeztető: \(reminderText)")

Az „éppen most“ forgatókönyv kezelése — a RelativeDateTimeFormatter nem rendelkezik beépített támogatással az „éppen most“ kifejezéshez nagyon kis intervallumok esetén. Az 5 másodpercnél kisebb különbséghez „0 másodperccel ezelőtt“-et ad vissza, ami nem szép a UI-ban. Ajánlott a formatter hívását feltételes logikába csomagolni: ha a különbség kisebb a beállított küszöbértéknél (például 5 másodperc) — az „éppen most“ szöveget manuálisan jelenítse meg, egyébként adja át a dátumot a formatternek.

swift
import Foundation

func relativeTimeString(from date: Date) -> String {
    let interval = Date().timeIntervalSince(date)

    // Az „éppen most“ küszöbérték
    if interval < 5 {
        return "just now"
    }

    // A „ma“ küszöbérték
    if interval < 60 {
        return "just now"
    }

    let formatter = RelativeDateTimeFormatter()
    formatter.unitsStyle = .numeric
    formatter.maximumUnitCount = 1

    // Megjelenítés „ezelőtt“ utótag nélkül
    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)))

A string(fromTimeInterval:) metódus elfogadja a másodpercben kifejezett különbséget, és automatikusan meghatározza a kontextust (pozitív érték — jövő, negatív — múlt). Ez akkor kényelmes, ha a különbség már ismert (például a szervertől unix timestampként érkezett). Ebben az esetben nem kell Date-et létrehozni — a különbség közvetlenül átadható.

A relatív dátumok lokalizációja

RelativeDateTimeFormatter automatikusan lokalizálja a kimenetet a Locale.current alapján. A formázási nyelv megváltoztatásához állítsa be a locale tulajdonságot — a DateFormatter-rel ellentétben a RelativeDateTimeFormatter esetén a locale nincs rögzítve, és minden híváshoz módosítható. Ez lehetővé teszi a relatív dátumok megjelenítését az interfész nyelvétől eltérő nyelven (például az eredeti nyelvű tartalom esetén).

A relatív dátumok lokalizációjának bonyolultsága a különböző nyelvek nyelvtani sajátosságaiban rejlik. Az orosz nyelv a számnevek különböző alakjait igényli: „1 perc“, „2 perc“, „5 perc“. Az arab — a 3-tól 10-ig terjedő számokhoz többes számú alakot, a 11+ esetén speciális alakokat használ. A kínai — egyáltalán nem ragoz, ami egyszerűsíti a feladatot. A RelativeDateTimeFormatter mindezeket az eseteket az ICU-szabályokon keresztül fedi le, további kód nélkül.

swift
import Foundation

let formatter = RelativeDateTimeFormatter()
formatter.unitsStyle = .numeric
formatter.maximumUnitCount = 1

let targetDate = Date().addingTimeInterval(-7200) // 2 órával ezelőtt

// Különböző lokálok
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)")
}

// Ellenőrizze az orosz ragozást
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)) perc: \(formatter.localizedString(for: date, relativeTo: Date()))")
}

Fontos részlet: a RelativeDateTimeFormatter a .numeric beállításoknál figyelmen kívül hagyja a TimeZone-t a különbség kiszámításakor — az abszolút másodpercben kifejezett különbséget használ. Azonban a .full stílushoz (betűkkel írt számokkal) és a speciális esetekhez (tegnap, ma) a TimeZone figyelembe van véve. Az egységesség érdekében mindig állítsa be kifejezetten a TimeZone-t, különösen, ha az alkalmazás UTC-ben használt szerverdátumokkal dolgozik.

Tipikus hibák a formázás során

A TimeZone figyelmen kívül hagyása a relatív dátumok kiszámításakor — gyakori hiba a szerverdátumokkal való munka során. Ha a szerver UTC-ben küldi a Date-et, és a RelativeDateTimeFormatter a TimeZone.current-et használja, a különbség hibásan számolható ki az aktuális pillanathoz közeli dátumoknál. Ajánlott a szerveradatokhoz mindig beállítani a formatter.timeZone = TimeZone(secondsFromGMT: 0) értéket.

Helytelen egységválasztás rövid intervallumokhoz — a RelativeDateTimeFormatter a legnagyobb egységre kerekíti a különbséget. 25 órához az eredmény „1 nappal ezelőtt“ lesz, ami félrevezetheti a felhasználót. Ha nagy pontosság szükséges (például visszaszámlálóknál), a RelativeDateTimeFormatter helyett a DateComponentsFormatter-t használja — ez lehetővé teszi több egység egyidejű megjelenítését.

A negatív TimeInterval ellenőrzésének hiánya — ha egy jövőbeli dátumot múltként adnak át (negatív érték a string(fromTimeInterval:)-ban), a formatter helytelen karakterláncot adhat vissza. Mindig ellenőrizze az intervallum előjelét, mielőtt átadja a formatternek, különösen a szerveradatokkal végzett munka során, ahol az időzóna torzíthatja a számítást.

A Hacker News (2024) szerint a RelativeDateTimeFormatter egyik legtöbbet vitatott problémája az „tegnap“ és „ma“ beépített támogatásának hiánya az angol nyelvben. Az „tegnap“ helyett a formatter a 90000 másodperces különbséghez „1 nappal ezelőtt“-et ad vissza. Az orosz nyelvnél nincs ilyen probléma — az „1 nappal ezelőtt“ természetesen hangzik, de az angol UI-hoz a „yesterday“ előnyösebb. Ez a funkció nem támogatott, és manuális ellenőrzést igényel a Calendar.isDateInToday/Yesterday segítségével.

Gyakran ismételt kérdések

Mi az a RelativeDateTimeFormatter?

RelativeDateTimeFormatter — Foundation-osztály a dátumok relatív formátumban történő megjelenítésére: „5 perce“, „2 nap múlva“. iOS 13 és macOS 10.15 verziótól érhető el.

Hogyan választja ki a RelativeDateTimeFormatter az egységeket?

A legnagyobb nem nulla egység elve szerint — másodperc, perc, óra, nap, hét, hónap vagy év. Például a 3720 másodperces (1 óra 2 perc) különbséghez az „óra“ egység kerül kiválasztásra, nem a „perc“.

Hogyan változtathatom meg a kimenet nyelvét?

Állítsa be a locale tulajdonságot a kívánt Locale-példányra. Alapértelmezésben a Locale.current kerül használatra. Példa: formatter.locale = Locale(identifier: "de_DE") a német nyelvhez.

Mi a különbség a .numeric és az .abbreviated között?

.numeric — teljes forma („3 nappal ezelőtt“), .abbreviated — rövidített („3 nappal ezelőtt“). A választás a kontextustól függ: numeric a fő UI-hoz, abbreviated a kompakt elemekhez.

Hogyan jeleníthetem meg az „éppen most“ szöveget a „0 másodperccel ezelőtt“ helyett?

Adjon hozzá manuális ellenőrzést az 5–10 másodpercnél kisebb intervallumhoz. A RelativeDateTimeFormatter nem támogatja az „éppen most“ szöveget — a kis intervallumokhoz „0 másodperccel ezelőtt“-et ad vissza. Használjon küszöbértékes feltételes logikát.

Összegzés

  • RelativeDateTimeFormatter — kényelmes osztály a relatív dátumok megjelenítéséhez iOS 13+ rendszerben
  • Automatikus lokalizáció — helyes ragozás az összes támogatott nyelvhez az ICU-n keresztül
  • Három stílus — .numeric (standard), .abbreviated (kompakt), .full (betűkkel)
  • Egységválasztás — automatikus a legnagyobb nem nulla érték elve szerint
  • TimeZone beállítás — kötelező az egységességhez a szerverdátumokkal végzett munka során
  • Az „éppen most“ küszöbérték — nem támogatott beépítetten; az intervallum manuális ellenőrzése szükséges
  • Nincs „tegnap“ támogatás — az angol nyelvhez a formatter nem használja a yesterday formát

Kulcsrakész mobilalkalmazást fejlesztünk

Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.

Projekt megbeszélése

Olvassa el is