RelativeDateTimeFormatter: özü, göreli tarihler ve Swift

Yazar: IT Sectr Yayınlanma: 2026-07-13 Okuma süresi: 10 dk

RelativeDateTimeFormatter, iOS ve macOS'ta mutlak tarihleri insan tarafından okunabilir göreli ifadelere dönüştüren bir Foundation sınıfıdır: "5 dakika önce", "dün", "3 gün içinde". Apple Developer Documentation, 2024'ye göre, RelativeDateTimeFormatter uygun birimi (saniye, dakika, saat, gün) otomatik olarak seçer ve çıktıyı cihazın geçerli yerel ayarının dilinde yerelleştirir. Calendar aracılığıyla tarihler arasındaki farkın manuel olarak hesaplanmasının aksine, bu sınıf her dilin dilbilimsel özelliklerini dikkate alır: bazı dillerde sayılar çekimlenir, diğerlerinde "dün" kelimesi için özel bir biçim kullanılır. Sınıf, iOS 13 ve macOS 10.15'ten itibaren kullanılabilir.

Önemli Noktalar

  • RelativeDateTimeFormatter — iOS ve macOS'ta göreli tarihleri görüntülemek için bir sınıf (iOS 13+)
  • Yerelleştirilmiş çıktı — geçerli yerel ayarın dilinde otomatik olarak ifadeleri seçer
  • Üç bağlam türü — geçmiş (önce), gelecek (içinde), şimdiki (şimdi) farklı ifadelerle
  • Otomatik birim seçimi — saniye, dakika, saat, gün, hafta, ay, yıl
  • Stil özelleştirme — numeric (3 gün içinde) veya abbreviated (3 gn. içinde)

RelativeDateTimeFormatter nedir?

RelativeDateTimeFormatter, Foundation'da Formatter'ın bir alt sınıfıdır; bir Date (veya saniye cinsinden fark) alır ve göreli zamanla birlikte yerelleştirilmiş bir dize döndürür. Örneğin, şu andan 5 dakika önceki bir tarih için "5 dakika önce" döndürür. Sınıf üç zamansal bağlamı destekler: geçmiş, gelecek ve şimdiki.

RelativeDateTimeFormatter'ın iç mantığı, tarihler arasındaki farkı hesaplamak ve doğru dilbilgisi biçimini seçmek için Calendar ve Locale kullanır. Türkçe için "1 dakika önce" ve "5 dakika önce" arasında seçim yapar. Bu işlevsellik, ICU (International Components for Unicode) verilerine dayanır ve geliştiriciden ek yapılandırma gerektirmez.

Apple WWDC 2019'a göre, RelativeDateTimeFormatter yerelleştirmeyi basitleştirmek için framework'ün bir parçası haline geldi — tanıtılmasından önce, geliştiricilerin tarih farkını manuel olarak hesaplaması ve String.localizedStringWithFormat aracılığıyla yerelleştirilmiş dizeleri değiştirmesi gerekiyordu. Bu, çekim hatalarına (özellikle Slav ve Arap dilleri için) ve ölçü birimlerinin yanlış seçilmesine yol açıyordu.

RelativeDateTimeFormatter "5 dakika önce"yi nasıl gösterir?

Algoritma üç adımdan oluşur: iletilen tarih ile geçerli an arasındaki farkı hesaplama, uygun birimi seçme (sıfır vermeyen en büyük olanı) ve yerel ayara göre biçimlendirme. Örneğin, 3720 saniyelik (1 saat 2 dakika) bir fark için "saat" birimi seçilir ve sonuç "1 saat önce" olur, "62 dakika önce" değil.

Birimler "en büyük sıfır olmayan" ilkesine göre seçilir: fark 86400 saniyeden (1 gün) büyükse günler kullanılır; 604800'den (1 hafta) büyükse haftalar kullanılır ve bu şekilde devam eder. Bu algoritma, sonucun her zaman doğal bir şekilde okunmasını sağlar: "518400 saniye önce" yerine kullanıcı "6 gün önce" görür. Birimlerin kesin sınırları, geçerli yerel ayarın takvimi tarafından belirlenir.

Fark aralığıBirimtr_TR için örnek
0–59 saniyeSeconds30 saniye önce
1–59 dakikaMinutes5 dakika önce
1–23 saatHours3 saat önce
1–6 günDays2 gün önce
7–27 günWeeks1 hafta önce
28 gün–11 ayMonths3 ay önce
12+ ayYears1 yıl önce

Biçimlendirme bağlamı ifadenin sonunu belirler. Geçmiş için: "önce" (Türkçe). Gelecek için: "3 gün içinde" (Türkçe). Şimdiki için: "şimdi" (Türkçe). Bağlam, localizeString(fromTimeInterval:) yöntemi aracılığıyla veya doğrudan string(from: Date) aracılığıyla ayarlanır.

Birim ve stil ayarları

RelativeDateTimeFormatter, çıktıyı kontrol etmek için çeşitli ayarlar sağlar: unitsStyle özelliği biçimlendirme stilini (numeric, abbreviated, full, spellOut) belirler ve maximumUnitCount görüntülenen birim sayısını sınırlar. Örneğin, maximumUnitCount = 1 ile 1 saat 30 dakikalık bir fark "1 saat 30 dakika önce" yerine "1 saat önce" olarak gösterilir.

Biçimlendirme stilleri

  • .numeric — tam sayısal değer: "3 gün önce", "2 hafta içinde". Varsayılan olarak UI için önerilir
  • .abbreviated — kısaltılmış biçim: "3 gn. önce", "2 hf. içinde". Tablolarda ve listelerde kompakt görüntüleme için
  • .full — rakamsız sözlü biçim: "üç gün önce". Erişilebilirlik ve sesli arayüzler için
  • .spellOut — alternatif yazımla harf biçimi: "three days ago". Nadiren kullanılır, çoğunlukla özel amaçlar için

Birim sınırlaması: varsayılan olarak, RelativeDateTimeFormatter yalnızca bir (en büyük) birimi görüntüler. maximumUnitCount = 2 olarak ayarlamak, daha kesin bir tanım için sonraki birimi içerir: "1 saat 30 dakika önce". Ancak bu, kısa mesajlar (push bildirimleri, uyarılar) için dizeyi aşırı derecede uzun yapabilir. UI için maximumUnitCount = 1 tutulması önerilir.

swift
import Foundation

let formatter = RelativeDateTimeFormatter()

// Configure styles
formatter.unitsStyle = .numeric
formatter.maximumUnitCount = 1

// Examples with different dates
let fiveMinAgo = Date().addingTimeInterval(-300)
print("5 min ago: \(formatter.localizedString(for: fiveMinAgo, relativeTo: Date()))")

let twoDaysLater = Date().addingTimeInterval(172800)
print("2 days later: \(formatter.localizedString(for: twoDaysLater, relativeTo: Date()))")

// Abbreviated style
formatter.unitsStyle = .abbreviated
let oneWeekAgo = Date().addingTimeInterval(-604800)
print("Abbreviated: \(formatter.localizedString(for: oneWeekAgo, relativeTo: Date()))")

// Full style (spelled out)
formatter.unitsStyle = .full
let threeHours = Date().addingTimeInterval(10800)
print("Full: \(formatter.localizedString(for: threeHours, relativeTo: Date()))")

Farklı bağlamlar için stil seçimi: haber akışı için maximumUnitCount = 1 ile .numeric kullanın — bu Twitter, Instagram ve Facebook için standarttır. Erişilebilirlik (VoiceOver) için .full kullanın — yazılmış sayılar daha doğal okunur. Kompakt öğeler (bildirim rozeti, durum çubuğu) için yerden tasarruf etmek amacıyla .abbreviated kullanın.

Swift'te RelativeDateTimeFormatter: örnekler

Temel kullanım, bir örnek oluşturma, özellikleri yapılandırma ve biçimlendirme yöntemlerinden birini çağırmadan oluşur. Ana yöntemler şunlardır: localizedString(for:relativeTo:) — bir çift tarih için, localizedString(fromTimeInterval:) — saniye cinsinden fark için ve string(for:) — otomatik bağlam (geçmiş/gelecek) ile Date için.

swift
import Foundation

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

// Social network UI examples
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)")
}

// Future dates
let reminderFormatter = RelativeDateTimeFormatter()
reminderFormatter.unitsStyle = .abbreviated
let inOneHour = Date().addingTimeInterval(3600)
let reminderText = reminderFormatter.localizedString(
    for: inOneHour,
    relativeTo: Date()
)
print("Reminder: \(reminderText)")

"Az önce" senaryosunu işleme — RelativeDateTimeFormatter'ın çok küçük aralıklar için "az önce" ifadesi için yerleşik desteği yoktur. 5 saniyeden az bir fark için "0 saniye önce" döndürür, bu da UI'da hoş görünmez. Biçimlendirici çağrısını koşullu mantıkla sarmanız önerilir: fark belirlenmiş bir eşikten (örneğin, 5 saniye) küçükse — manuel olarak "az önce" gösterin, aksi halde tarihi biçimlendiriciye iletin.

swift
import Foundation

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

    // "Just now" threshold
    if interval < 5 {
        return "just now"
    }

    // "Today" threshold
    if interval < 60 {
        return "just now"
    }

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

    // Display without "ago" suffix
    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:) yöntemi saniye cinsinden bir fark kabul eder ve bağlamı otomatik olarak belirler (pozitif değer — gelecek, negatif — geçmiş). Bu, fark zaten biliniyorsa (örneğin, sunucudan unix zaman damgası olarak alındıysa) kullanışlıdır. Bu durumda, bir Date oluşturmaya gerek yoktur — fark doğrudan iletilir.

Göreli tarihlerin yerelleştirilmesi

RelativeDateTimeFormatter, Locale.current'e dayalı olarak çıktıyı otomatik olarak yerelleştirir. Biçimlendirme dilini değiştirmek için locale özelliğini ayarlayın — DateFormatter'ın aksine, RelativeDateTimeFormatter için yerel ayar sabit değildir ve her çağrı için değiştirilebilir. Bu, göreli tarihlerin arayüz dilinden farklı bir dilde görüntülenmesine olanak tanır (örneğin, orijinal dilde içerik).

Göreli tarihlerin yerelleştirilmesinin karmaşıklığı, farklı dillerin dilbilgisel özelliklerinde yatmaktadır. Türkçe farklı sayı biçimleri gerektirir: "1 dakika", "2 dakika". Arapça, 3'ten 10'a kadar sayılar için çoğul biçimi ve 11+ için özel biçimler kullanır. Çince'de hiç çekim yoktur, bu da görevi basitleştirir. RelativeDateTimeFormatter, ek kod gerektirmeden ICU kuralları aracılığıyla tüm bu durumları kapsar.

swift
import Foundation

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

let targetDate = Date().addingTimeInterval(-7200) // 2 hours ago

// Different 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)")
}

// Check Turkish pluralization
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()))")
}

Önemli bir nüans: RelativeDateTimeFormatter, .numeric ayarları için farkı hesaplarken TimeZone'u yok sayar — saniye cinsinden mutlak farkı kullanır. Ancak .full stili (yazılmış sayılarla) ve özel durumlar (dün, bugün) için TimeZone dikkate alınır. Tutarlılık için her zaman TimeZone'u açıkça ayarlayın, özellikle uygulama UTC'deki sunucu tarihleriyle çalışıyorsa.

Yaygın biçimlendirme hataları

Göreli tarihleri hesaplarken TimeZone'u yok sayma — sunucu tarihleriyle çalışırken yaygın bir hatadır. Sunucu UTC'de bir Date gönderiyorsa ve RelativeDateTimeFormatter TimeZone.current kullanıyorsa, geçerli ana yakın tarihler için fark yanlış hesaplanabilir. Sunucu verileri için her zaman formatter.timeZone = TimeZone(secondsFromGMT: 0) ayarlanması önerilir.

Kısa aralıklar için yanlış birim seçimi — RelativeDateTimeFormatter farkı en büyük birime yuvarlar. 25 saat için sonuç "1 gün önce" olur ve bu kullanıcıyı yanıltabilir. Yüksek hassasiyet gerekiyorsa (örneğin, geri sayım sayaçları için), RelativeDateTimeFormatter yerine DateComponentsFormatter kullanın — aynı anda birden çok birimi görüntülemeye izin verir.

Negatif TimeInterval kontrolünün olmaması — gelecekteki bir tarih geçmiş olarak iletilirse (string(fromTimeInterval:) içinde negatif değer), biçimlendirici yanlış bir dize döndürebilir. Biçimlendiriciye iletmeden önce her zaman aralığın işaretini kontrol edin, özellikle saat diliminin hesaplamayı bozabileceği sunucu verileriyle çalışırken.

Hacker News (2024)'e göre, RelativeDateTimeFormatter'ın en çok tartışılan sorunlarından biri, İngilizce dili için "dün" ve "bugün" için yerleşik desteğin olmamasıdır. "Dün" yerine, 90000 saniyelik bir fark için biçimlendirici "1 gün önce" döndürür. Türkçe için böyle bir sorun yoktur — "1 gün önce" doğal gelir, ancak İngilizce UI için "yesterday" tercih edilir. Bu işlevsellik desteklenmez ve Calendar.isDateInToday/Yesterday aracılığıyla manuel kontrol gerektirir.

Sıkça Sorulan Sorular

RelativeDateTimeFormatter nedir?

RelativeDateTimeFormatter, tarihleri göreli biçimde görüntülemek için bir Foundation sınıfıdır: "5 dakika önce", "2 gün içinde". iOS 13 ve macOS 10.15'ten itibaren kullanılabilir.

RelativeDateTimeFormatter birimleri nasıl seçer?

En büyük sıfır olmayan birim ilkesine göre — saniye, dakika, saat, gün, hafta, ay veya yıl. Örneğin, 3720 saniyelik (1 saat 2 dakika) bir fark için "saat" birimi seçilir, "dakika" değil.

Çıktı dili nasıl değiştirilir?

locale özelliğini ayarlayın istenen Locale örneğine. Varsayılan olarak Locale.current kullanılır. Örnek: Almanca için formatter.locale = Locale(identifier: "de_DE").

.numeric ve .abbreviated arasındaki fark nedir?

.numeric — tam biçim ("3 gün önce"), .abbreviated — kısaltılmış biçim ("3 gn. önce"). Seçim bağlama bağlıdır: ana UI için numeric, kompakt öğeler için abbreviated.

"0 saniye önce" yerine "az önce" nasıl gösterilir?

Manuel bir kontrol ekleyin 5-10 saniyeden az bir aralık için. RelativeDateTimeFormatter "az önce"yi desteklemez — küçük aralıklar için "0 saniye önce" döndürür. Bir eşikle koşullu mantık kullanın.

Özet

  • RelativeDateTimeFormatter — iOS 13+'te göreli tarihleri görüntülemek için kullanışlı bir sınıf
  • Otomatik yerelleştirme — ICU aracılığıyla desteklenen tüm dillerde doğru çekim
  • Üç stil — .numeric (standart), .abbreviated (kompakt), .full (yazıyla)
  • Birim seçimi — en büyük sıfır olmayan değere dayalı otomatik
  • TimeZone yapılandırması — sunucu tarihleriyle çalışırken tutarlılık için zorunlu
  • "Az önce" eşiği — yerleşik olarak desteklenmez; manuel aralık kontrolü gerektirir
  • "Dün" desteği yok — biçimlendirici İngilizce için yesterday biçimini kullanmaz

Anahtar teslim bir mobil uygulama geliştireceğiz

IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.

Projeyi tartış

Ayrıca okuyun