RelativeDateTimeFormatter — iOS və macOS-da Foundation sinfi olub, mütləq tarixləri insanlar üçün oxunaqlı nisbi ifadələrə çevirir: “5 dəqiqə əvvəl”, “dünən”, “3 gün sonra”. Apple Developer Documentation, 2024 məlumatlarına görə, RelativeDateTimeFormatter avtomatik olaraq uyğun vahidi (saniyələr, dəqiqələr, saatlar, günlər) seçir və nəticəni cihazın cari lokalının dilində lokallaşdırır. Calendar vasitəsilə tarixlər arasındakı fərqi əl ilə hesablamaqdan fərqli olaraq, bu sinif hər dilin linqvistik xüsusiyyətlərini nəzərə alır: bəzi dillərdə saylar hallanır, bəzilərində isə “dünən” sözü üçün xüsusi forma istifadə olunur. Sinif iOS 13 və macOS 10.15-dən etibarən mövcuddur.
Əsas məqamlar
RelativeDateTimeFormatter — Foundation-da Formatter-in alt sinfi olub, Date-i (və ya saniyələrdəki fərqi) qəbul edir və nisbi zamanla lokallaşdırılmış sətir qaytarır. Məsələn, cari vaxtdan 5 dəqiqə əvvəlki tarix üçün ru_RU üçün “5 dəqiqə əvvəl” və ya en_US üçün “5 minutes ago” qaytaracaq. Sinif üç zaman kontekstini dəstəkləyir: keçmiş (past), gələcək (future) və indiki (present).
RelativeDateTimeFormatter-in daxili məntiqi tarixlər arasındakı fərqi hesablamaq və düzgün qrammatik formanı seçmək üçün Calendar və Locale-dən istifadə edir. Azərbaycan dili üçün sinif sayları düzgün hallandırır: “1 dəqiqə əvvəl”, “2 dəqiqə əvvəl”, “5 dəqiqə əvvəl”. İngilis dili üçün isə “minute ago” və “minutes ago” arasında seçim edir. Bu funksionallıq ICU (International Components for Unicode) məlumatlarına əsaslanır və tərtibatçıdan əlavə konfiqurasiya tələb etmir.
Apple WWDC 2019 məlumatlarına görə, RelativeDateTimeFormatter lokallaşdırmanı sadələşdirmək üçün framework-un bir hissəsi oldu — onun yaranmasından əvvəl tərtibatçılar tarix fərqini əl ilə hesablamalı və String.localizedStringWithFormat vasitəsilə lokallaşdırılmış sətirləri yerləşdirməli idilər. Bu, hallanmada (xüsusilə slavyan və ərəb dilləri üçün) səhvlərə və ölçü vahidlərinin səhv seçilməsinə gətirib çıxarırdı.
İş alqoritmi RelativeDateTimeFormatter üç addımdan ibarətdir: verilən tarixlə cari an arasındakı fərqin hesablanması, uyğun vahidin (sıfır verməyən ən böyüyün) seçilməsi və lokal nəzərə alınmaqla formatlaşdırma. Məsələn, 3720 saniyəlik fərq üçün (1 saat 2 dəqiqə) “saat” vahidi seçiləcək və nəticə “1 saat əvvəl” olacaq, “62 dəqiqə əvvəl” yox.
Vahidlər “ən böyük sıfırdan fərqli” prinsipi ilə seçilir: fərq 86400 saniyədən (1 gün) böyükdürsə, günlər istifadə olunur; 604800-dən (1 həftə) böyükdürsə — həftələr və s. Bu alqoritm nəticənin həmişə təbii oxunmasını təmin edir: “518400 saniyə əvvəl” əvəzinə istifadəçi “6 gün əvvəl” görür. Vahidlərin dəqiq sərhədlərini cari lokalın təqvimi müəyyən edir.
| Fərq diapazonu | Vahid | ru_RU üçün nümunə |
|---|---|---|
| 0–59 saniyə | Seconds | 30 saniyə əvvəl |
| 1–59 dəqiqə | Minutes | 5 dəqiqə əvvəl |
| 1–23 saat | Hours | 3 saat əvvəl |
| 1–6 gün | Days | 2 gün əvvəl |
| 7–27 gün | Weeks | 1 həftə əvvəl |
| 28 gün–11 ay | Months | 3 ay əvvəl |
| 12+ ay | Years | 1 il əvvəl |
Formatlaşdırma konteksti ifadənin sonunu müəyyən edir. Keçmiş üçün: “əvvəl” (azərbaycan), “ago” (ingilis). Gələcək üçün: “3 gün sonra” (azərbaycan), “in 3 days” (ingilis). İndiki üçün: “indi” (azərbaycan), “now” (ingilis). Kontekst localizeString(fromTimeInterval:) metodu və ya birbaşa string(from: Date) vasitəsilə təyin edilir.
RelativeDateTimeFormatter nəticəyə nəzarət üçün bir neçə parametr təqdim edir: unitsStyle xassəsi formatlaşdırma stilini (numeric, abbreviated, full, spellOut) müəyyən edir, maximumUnitCount isə göstərilən vahidlərin sayını məhdudlaşdırır. Məsələn, maximumUnitCount = 1 ilə 1 saat 30 dəqiqəlik fərq “1 saat 30 dəqiqə əvvəl” əvəzinə “1 saat əvvəl” kimi göstəriləcək.
Vahid məhdudiyyəti: default olaraq RelativeDateTimeFormatter yalnız bir (ən böyük) vahidi göstərir. maximumUnitCount = 2 təyin edilməsi daha dəqiq təsvir üçün növbəti vahidi də əlavə edir: “1 saat 30 dəqiqə əvvəl”. Lakin bu, qısa mesajlar (push, bildirişlər) üçün sətri həddindən artıq uzun edə bilər. UI üçün maximumUnitCount = 1-in saxlanması tövsiyə olunur.
import Foundation
let formatter = RelativeDateTimeFormatter()
// Stilleri konfiqurasiya edin
formatter.unitsStyle = .numeric
formatter.maximumUnitCount = 1
// Müxtəlif tarixlərlə nümunələr
let fiveMinAgo = Date().addingTimeInterval(-300)
print("5 dəq əvvəl: \(formatter.localizedString(for: fiveMinAgo, relativeTo: Date()))")
let twoDaysLater = Date().addingTimeInterval(172800)
print("2 gün sonra: \(formatter.localizedString(for: twoDaysLater, relativeTo: Date()))")
// Qısaldılmış stil
formatter.unitsStyle = .abbreviated
let oneWeekAgo = Date().addingTimeInterval(-604800)
print("Qısaldılmış: \(formatter.localizedString(for: oneWeekAgo, relativeTo: Date()))")
// Tam stil (sözlə)
formatter.unitsStyle = .full
let threeHours = Date().addingTimeInterval(10800)
print("Tam: \(formatter.localizedString(for: threeHours, relativeTo: Date()))")
Müxtəlif kontekstlər üçün stil seçimi: xəbər lentləri üçün maximumUnitCount = 1 ilə .numeric istifadə edin — bu, Twitter, Instagram və Facebook üçün standartdır. Accessibility (VoiceOver) üçün .full istifadə edin — sözlə yazılmış rəqəmlər daha təbii oxunur. Kompakt elementlər (bildiriş küncü, status sətri) üçün yerə qənaət etmək üçün .abbreviated istifadə edin.
Əsas istifadə RelativeDateTimeFormatter nümunə yaratmaq, xassələri konfiqurasiya etmək və formatlaşdırma metodlarından birini çağırmaqdan ibarətdir. Əsas metodlar: localizedString(for:relativeTo:) — bir cüt tarix üçün, localizedString(fromTimeInterval:) — saniyələrdəki fərq üçün və string(for:) — avtomatik kontekstlə (keçmiş/gələcək) Date üçün.
import Foundation
let formatter = RelativeDateTimeFormatter()
formatter.unitsStyle = .numeric
formatter.maximumUnitCount = 1
// Sosial şəbəkə UI nümunələri
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)")
}
// Gələcək tarixlər
let reminderFormatter = RelativeDateTimeFormatter()
reminderFormatter.unitsStyle = .abbreviated
let inOneHour = Date().addingTimeInterval(3600)
let reminderText = reminderFormatter.localizedString(
for: inOneHour,
relativeTo: Date()
)
print("Xatırlatma: \(reminderText)")
“İndicə” ssenarisinin işlənməsi — RelativeDateTimeFormatter çox kiçik intervallar üçün “indicə” ifadəsinə daxili dəstəyə malik deyil. 5 saniyədən az fərq üçün “0 saniyə əvvəl” qaytaracaq, bu da UI-da gözəl görünmür. Formatter çağırışını şərti məntiqlə bükmək tövsiyə olunur: fərq müəyyən edilmiş həddən (məs. 5 saniyə) azdırsa — “indicə” əl ilə göstərin, əks halda tarixi formatterə ötürün.
import Foundation
func relativeTimeString(from date: Date) -> String {
let interval = Date().timeIntervalSince(date)
// “İndicə” həddi
if interval < 5 {
return "just now"
}
// “Bu gün” həddi
if interval < 60 {
return "just now"
}
let formatter = RelativeDateTimeFormatter()
formatter.unitsStyle = .numeric
formatter.maximumUnitCount = 1
// “əvvəl” şəkilçisi olmadan göstərmək
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)))
string(fromTimeInterval:) metodu saniyələrdəki fərqi qəbul edir və konteksti avtomatik müəyyən edir (müsbət dəyər — gələcək, mənfi — keçmiş). Bu, fərq artıq məlum olduqda əlverişlidir (məs. serverdən unix timestamp kimi alınmışdır). Bu halda Date yaratmaq lazım deyil — fərq birbaşa ötürülür.
RelativeDateTimeFormatter nəticəni Locale.current əsasında avtomatik lokallaşdırır. Formatlaşdırma dilini dəyişmək üçün locale xassəsini təyin edin — DateFormatter-dən fərqli olaraq, RelativeDateTimeFormatter üçün locale sabit deyil və hər çağırış üçün dəyişdirilə bilər. Bu, nisbi tarixləri interfeys dilindən fərqli dildə (məs. orijinal dildəki məzmun) göstərməyə imkan verir.
Nisbi tarixlərin lokallaşdırılmasının mürəkkəbliyi müxtəlif dillərin qrammatik xüsusiyyətlərindədir. Rus dili müxtəlif say formaları tələb edir: “1 dəqiqə”, “2 dəqiqə”, “5 dəqiqə”. Ərəb dili — 3-dən 10-a qədər ədədlər üçün cəm formasından və 11+ üçün xüsusi formalardan istifadə edir. Çin dilində isə heç bir hallanma yoxdur, bu da vəzifəni sadələşdirir. RelativeDateTimeFormatter bütün bu halları ICU qaydaları ilə əlavə kod olmadan əhatə edir.
import Foundation
let formatter = RelativeDateTimeFormatter()
formatter.unitsStyle = .numeric
formatter.maximumUnitCount = 1
let targetDate = Date().addingTimeInterval(-7200) // 2 saat əvvəl
// Müxtəlif lokallar
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)")
}
// Rus dilinin hallanmasını yoxlayın
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)) dəq: \(formatter.localizedString(for: date, relativeTo: Date()))")
}
Vacib nüans: RelativeDateTimeFormatter .numeric parametrləri üçün fərq hesablayarkən TimeZone-u nəzərə almır — saniyələrdəki mütləq fərqdən istifadə edir. Lakin .full stil üçün (sözlə yazılmış rəqəmlərlə) və xüsusi hallar (dünən, bu gün) üçün TimeZone nəzərə alınır. Ardıcıllıq üçün həmişə TimeZone-u aydın şəkildə təyin edin, xüsusən də proqram server tarixləri ilə UTC-də işləyirsə.
Nisbi tarixlər hesablanarkən TimeZone-un nəzərə alınmaması — server tarixləri ilə işləyərkən geniş yayılmış səhvdir. Server Date-i UTC-də göndərir, RelativeDateTimeFormatter isə TimeZone.current istifadə edirsə, cari an yaxınlığındakı tarixlər üçün fərq səhv hesablana bilər. Server məlumatları üçün həmişə formatter.timeZone = TimeZone(secondsFromGMT: 0) təyin etmək tövsiyə olunur.
Qısa intervallar üçün vahidin səhv seçilməsi — RelativeDateTimeFormatter fərqi ən böyük vahidə yuvarlaqlaşdırır. 25 saat üçün nəticə “1 gün əvvəl” olacaq, bu da istifadəçini çaşdıra bilər. Yüksək dəqiqlik tələb olunarsa (məs. geri sayım taymerləri üçün), RelativeDateTimeFormatter əvəzinə DateComponentsFormatter istifadə edin — o, bir neçə vahidi eyni anda göstərməyə imkan verir.
Mənfi TimeInterval üçün yoxlamanın olmaması — gələcəkdəki tarix keçmiş kimi ötürülürsə (string(fromTimeInterval:) də mənfi dəyər), formatter səhv sətir qaytara bilər. Formatterə ötürməzdən əvvəl həmişə intervalın işarəsini yoxlayın, xüsusən də saat qurşağının hesablamanı təhrif edə biləcəyi server məlumatları ilə işləyərkən.
Hacker News (2024) məlumatlarına görə, RelativeDateTimeFormatter-in ən çox müzakirə olunan problemlərindən biri ingilis dili üçün “dünən” və “bu gün”-in daxili dəstəyinin olmamasıdır. “dünən” əvəzinə formatter 90000 saniyəlik fərq üçün “1 gün əvvəl” qaytaracaq. Rus dili üçün belə problem yoxdur — “1 gün əvvəl” təbii səslənir, lakin ingilis UI üçün “yesterday” daha üstündür. Bu funksionallıq dəstəklənmir və Calendar.isDateInToday/Yesterday vasitəsilə əl ilə yoxlama tələb edir.
Tez-tez verilən suallar
RelativeDateTimeFormatter — tarixləri nisbi formatda göstərmək üçün Foundation sinfi: “5 dəqiqə əvvəl”, “2 gün sonra”. iOS 13 və macOS 10.15-dən etibarən mövcuddur.
Ən böyük sıfırdan fərqli vahid prinsipi ilə — saniyələr, dəqiqələr, saatlar, günlər, həftələr, aylar və ya illər. Məsələn, 3720 saniyəlik fərq (1 saat 2 dəqiqə) üçün “saat” vahidi seçiləcək, “dəqiqələr” yox.
locale xassəsini istədiyiniz Locale nümunəsinə təyin edin. Default olaraq Locale.current istifadə olunur. Nümunə: formatter.locale = Locale(identifier: "de_DE") alman dili üçün.
.numeric — tam forma (“3 gün əvvəl”), .abbreviated — qısaldılmış (“3 gün əvvəl”). Seçim kontekstdən asılıdır: numeric əsas UI üçün, abbreviated kompakt elementlər üçün.
Əl ilə yoxlama əlavə edin 5–10 saniyədən az interval üçün. RelativeDateTimeFormatter “indicə”-ni dəstəkləmir — kiçik intervallar üçün “0 saniyə əvvəl” qaytarır. Həddi olan şərti məntiqdən istifadə edin.
Nəticələr
Açar təslim mobil tətbiq hazırlayacağıq
IT Sectr 2017-ci ildən startaplar və bizneslər üçün iOS və Android tətbiqləri yaradır. Sizə məsləhət verəcəyik və ən yaxşı həlli təklif edəcəyik.
Həm də oxuyun