RelativeDateTimeFormatter iOS اور macOS میں Foundation کی ایک کلاس ہے جو مطلق تاریخوں کو انسانی پڑھنے کے قابل نسبتی جملوں میں تبدیل کرتی ہے: "5 منٹ پہلے"، "کل"، "3 دنوں میں"۔ Apple Developer Documentation, 2024 کے مطابق، RelativeDateTimeFormatter خود بخود مناسب اکائی (سیکنڈ، منٹ، گھنٹے، دن) منتخب کرتا ہے اور آلے کے موجودہ مقام کی زبان میں آؤٹ پٹ کو مقامی بناتا ہے۔ Calendar کے ذریعے تاریخوں کے درمیان فرق کے دستی حساب کے برعکس، یہ کلاس ہر زبان کی لسانی خصوصیات کو مدنظر رکھتی ہے: کچھ زبانوں میں اعداد گردان کیے جاتے ہیں، دوسری میں "کل" کے لفظ کے لیے ایک خاص شکل استعمال ہوتی ہے۔ یہ کلاس iOS 13 اور macOS 10.15 سے دستیاب ہے۔
اہم نکات
RelativeDateTimeFormatter Foundation میں Formatter کا ایک ذیلی طبقہ ہے جو ایک Date (یا سیکنڈوں میں فرق) لیتا ہے اور نسبتی وقت کے ساتھ ایک مقامی سٹرنگ لوٹاتا ہے۔ مثال کے طور پر، موجودہ سے 5 منٹ پہلے کی تاریخ کے لیے، یہ "5 منٹ پہلے" لوٹاتا ہے۔ کلاس تین وقتی سیاق و سباق کو سپورٹ کرتی ہے: ماضی، مستقبل اور حال۔
RelativeDateTimeFormatter کی داخلی منطق تاریخوں کے درمیان فرق کا حساب لگانے اور صحیح گرائمری شکل منتخب کرنے کے لیے Calendar اور Locale استعمال کرتی ہے۔ اردو کے لیے، یہ "1 منٹ پہلے" اور "5 منٹ پہلے" کے درمیان انتخاب کرتا ہے۔ یہ فعالیت ICU (International Components for Unicode) ڈیٹا پر مبنی ہے اور ڈویلپر سے کسی اضافی ترتیب کی ضرورت نہیں ہے۔
Apple WWDC 2019 کے مطابق، RelativeDateTimeFormatter مقامی بنانے کو آسان بنانے کے لیے فریم ورک کا حصہ بنا — اس کے متعارف ہونے سے پہلے، ڈویلپرز کو دستی طور پر تاریخ کے فرق کا حساب لگانا اور String.localizedStringWithFormat کے ذریعے مقامی سٹرنگ تبدیل کرنی پڑتی تھی۔ اس سے گردان کی غلطیاں (خاص طور پر سلاوی اور عربی زبانوں کے لیے) اور پیمائش کی اکائیوں کا غلط انتخاب ہوتا تھا۔
الگورتھم تین مراحل پر مشتمل ہے: منتقل کردہ تاریخ اور موجودہ لمحے کے درمیان فرق کا حساب، مناسب اکائی کا انتخاب (سب سے بڑی جو صفر نہیں دیتی)، اور مقام کے مطابق فارمیٹنگ۔ مثال کے طور پر، 3720 سیکنڈ (1 گھنٹہ 2 منٹ) کے فرق کے لیے، "گھنٹہ" اکائی منتخب ہوتی ہے اور نتیجہ "1 گھنٹہ پہلے" ہوتا ہے، "62 منٹ پہلے" نہیں۔
اکائیاں "سب سے بڑی غیر صفر" کے اصول پر منتخب ہوتی ہیں: اگر فرق 86400 سیکنڈ (1 دن) سے زیادہ ہے تو دن استعمال ہوتے ہیں؛ اگر 604800 (1 ہفتہ) سے زیادہ ہے تو ہفتے، اور اسی طرح۔ یہ الگورتھم یقینی بناتا ہے کہ نتیجہ ہمیشہ قدرتی طور پر پڑھا جائے: "518400 سیکنڈ پہلے" کے بجائے، صارف "6 دن پہلے" دیکھتا ہے۔ اکائیوں کی صحیح حدود موجودہ مقام کے کیلنڈر سے طے ہوتی ہیں۔
| فرق کی حد | اکائی | ur_PK کے لیے مثال |
|---|---|---|
| 0–59 سیکنڈ | Seconds | 30 سیکنڈ پہلے |
| 1–59 منٹ | Minutes | 5 منٹ پہلے |
| 1–23 گھنٹے | Hours | 3 گھنٹے پہلے |
| 1–6 دن | Days | 2 دن پہلے |
| 7–27 دن | Weeks | 1 ہفتہ پہلے |
| 28 دن–11 مہینے | Months | 3 مہینے پہلے |
| 12+ مہینے | Years | 1 سال پہلے |
فارمیٹنگ کا سیاق و سباق جملے کے اختتام کا تعین کرتا ہے۔ ماضی کے لیے: "پہلے" (اردو)۔ مستقبل کے لیے: "3 دنوں میں" (اردو)۔ حال کے لیے: "اب" (اردو)۔ سیاق و سباق localizeString(fromTimeInterval:) طریقہ کے ذریعے یا براہ راست string(from: Date) کے ذریعے متعین کیا جاتا ہے۔
RelativeDateTimeFormatter آؤٹ پٹ کو کنٹرول کرنے کے لیے کئی ترتیبات فراہم کرتا ہے: unitsStyle خاصیت فارمیٹنگ اسٹائل (numeric, abbreviated, full, spellOut) متعین کرتی ہے، اور maximumUnitCount ظاہر کردہ اکائیوں کی تعداد کو محدود کرتی ہے۔ مثال کے طور پر، maximumUnitCount = 1 کے ساتھ، 1 گھنٹہ 30 منٹ کا فرق "1 گھنٹہ 30 منٹ پہلے" کے بجائے "1 گھنٹہ پہلے" کے طور پر دکھایا جاتا ہے۔
اکائی کی حد: بطور ڈیفالٹ، RelativeDateTimeFormatter صرف ایک (سب سے بڑی) اکائی دکھاتا ہے۔ maximumUnitCount = 2 سیٹ کرنے سے زیادہ درست وضاحت کے لیے اگلی اکائی شامل ہوتی ہے: "1 گھنٹہ 30 منٹ پہلے"۔ تاہم، یہ مختصر پیغامات (پش نوٹیفیکیشن، الرٹ) کے لیے سٹرنگ کو ضرورت سے زیادہ لمبا بنا سکتا ہے۔ UI کے لیے، maximumUnitCount = 1 رکھنے کی سفارش کی جاتی ہے۔
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()))")
مختلف سیاق و سباق کے لیے اسٹائل کا انتخاب: نیوز فیڈ کے لیے، maximumUnitCount = 1 کے ساتھ .numeric استعمال کریں — یہ Twitter، Instagram اور Facebook کے لیے معیار ہے۔ رسائی (VoiceOver) کے لیے، .full استعمال کریں — ہجے والے اعداد زیادہ قدرتی طور پر پڑھے جاتے ہیں۔ کمپیکٹ عناصر (نوٹیفیکیشن بیج، اسٹیٹس بار) کے لیے، جگہ بچانے کے لیے .abbreviated استعمال کریں۔
بنیادی استعمال ایک مثال بنانے، خصوصیات کو ترتیب دینے اور فارمیٹنگ کے طریقوں میں سے ایک کو کال کرنے پر مشتمل ہے۔ اہم طریقے یہ ہیں: localizedString(for:relativeTo:) — تاریخوں کے ایک جوڑے کے لیے، localizedString(fromTimeInterval:) — سیکنڈوں میں فرق کے لیے، اور string(for:) — خودکار سیاق و سباق (ماضی/مستقبل) کے ساتھ Date کے لیے۔
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)")
"ابھی" منظر نامے کو ہینڈل کرنا — RelativeDateTimeFormatter میں بہت چھوٹے وقفوں کے لیے "ابھی" کے اظہار کے لیے کوئی بلٹ ان سپورٹ نہیں ہے۔ 5 سیکنڈ سے کم کے فرق کے لیے، یہ "0 سیکنڈ پہلے" لوٹاتا ہے، جو UI میں اچھا نہیں لگتا۔ فارمیٹر کال کو مشروط منطق میں لپیٹنے کی سفارش کی جاتی ہے: اگر فرق مقررہ حد (مثلاً، 5 سیکنڈ) سے کم ہے — دستی طور پر "ابھی" دکھائیں، بصورت دیگر تاریخ فارمیٹر کو بھیجیں۔
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:) طریقہ سیکنڈوں میں فرق قبول کرتا ہے اور خود بخود سیاق و سباق متعین کرتا ہے (مثبت قدر — مستقبل، منفی — ماضی)۔ یہ اس وقت مفید ہے جب فرق پہلے سے معلوم ہو (مثلاً، سرور سے unix ٹائم سٹیمپ کے طور پر موصول)۔ اس صورت میں، Date بنانے کی ضرورت نہیں ہے — فرق براہ راست بھیجا جاتا ہے۔
RelativeDateTimeFormatter Locale.current کی بنیاد پر خود بخود آؤٹ پٹ کو مقامی بناتا ہے۔ فارمیٹنگ زبان تبدیل کرنے کے لیے، locale خاصیت سیٹ کریں — DateFormatter کے برعکس، RelativeDateTimeFormatter کے لیے مقام طے شدہ نہیں ہے اور ہر کال کے لیے تبدیل کیا جا سکتا ہے۔ یہ انٹرفیس زبان سے مختلف زبان میں نسبتی تاریخیں دکھانے کی اجازت دیتا ہے (مثلاً، اصل زبان میں مواد)۔
نسبتی تاریخوں کے مقامی بنانے کی پیچیدگی مختلف زبانوں کی گرائمری خصوصیات میں مضمر ہے۔ اردو میں اعداد کی مختلف شکلیں درکار ہوتی ہیں: "1 منٹ"، "2 منٹ"۔ عربی 3 سے 10 تک کے اعداد کے لیے جمع کی شکل اور 11+ کے لیے خاص شکلیں استعمال کرتی ہے۔ چینی میں کوئی گردان نہیں ہے، جو کام کو آسان بناتا ہے۔ RelativeDateTimeFormatter اضافی کوڈ کی ضرورت کے بغیر، ICU قواعد کے ذریعے ان تمام معاملات کا احاطہ کرتا ہے۔
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 Urdu 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()))")
}
ایک اہم باریکی: RelativeDateTimeFormatter .numeric ترتیبات کے لیے فرق کا حساب لگاتے وقت TimeZone کو نظر انداز کرتا ہے — یہ سیکنڈوں میں مطلق فرق استعمال کرتا ہے۔ تاہم، .full اسٹائل (ہجے والے اعداد) اور خاص معاملات (کل، آج) کے لیے، TimeZone کو مدنظر رکھا جاتا ہے۔ مستقل مزاجی کے لیے ہمیشہ TimeZone کو واضح طور پر سیٹ کریں، خاص طور پر اگر ایپلیکیشن UTC میں سرور کی تاریخوں کے ساتھ کام کرتی ہے۔
نسبتی تاریخوں کا حساب لگاتے وقت TimeZone کو نظر انداز کرنا — سرور کی تاریخوں کے ساتھ کام کرتے وقت ایک عام غلطی۔ اگر سرور UTC میں Date بھیجتا ہے، اور RelativeDateTimeFormatter TimeZone.current استعمال کرتا ہے، تو موجودہ لمحے کے قریب تاریخوں کے لیے فرق غلط حساب کیا جا سکتا ہے۔ سرور ڈیٹا کے لیے ہمیشہ formatter.timeZone = TimeZone(secondsFromGMT: 0) سیٹ کرنے کی سفارش کی جاتی ہے۔
مختصر وقفوں کے لیے غلط اکائی کا انتخاب — RelativeDateTimeFormatter فرق کو سب سے بڑی اکائی میں گول کرتا ہے۔ 25 گھنٹوں کے لیے، نتیجہ "1 دن پہلے" ہوگا، جو صارف کو گمراہ کر سکتا ہے۔ اگر زیادہ درستگی کی ضرورت ہو (مثلاً، الٹی گنتی ٹائمر کے لیے)، RelativeDateTimeFormatter کے بجائے DateComponentsFormatter استعمال کریں — یہ ایک ساتھ متعدد اکائیاں دکھانے کی اجازت دیتا ہے۔
منفی TimeInterval کی جانچ کا فقدان — اگر مستقبل کی تاریخ ماضی کے طور پر بھیجی جاتی ہے (string(fromTimeInterval:) میں منفی قدر)، فارمیٹر غلط سٹرنگ لوٹا سکتا ہے۔ فارمیٹر کو بھیجنے سے پہلے ہمیشہ وقفے کی علامت چیک کریں، خاص طور پر سرور ڈیٹا کے ساتھ کام کرتے ہوئے جہاں ٹائم زون حساب کو بگاڑ سکتا ہے۔
Hacker News (2024) کے مطابق، RelativeDateTimeFormatter کے سب سے زیادہ زیر بحث مسائل میں سے ایک انگریزی زبان کے لیے "کل" اور "آج" کی بلٹ ان سپورٹ کا فقدان ہے۔ "کل" کے بجائے، 90000 سیکنڈ کے فرق کے لیے فارمیٹر "1 دن پہلے" لوٹاتا ہے۔ اردو کے لیے ایسا کوئی مسئلہ نہیں ہے — "1 دن پہلے" قدرتی لگتا ہے، لیکن انگریزی UI کے لیے "yesterday" ترجیح دی جاتی ہے۔ یہ فعالیت معاون نہیں ہے اور Calendar.isDateInToday/Yesterday کے ذریعے دستی جانچ کی ضرورت ہے۔
اکثر پوچھے گئے سوالات
RelativeDateTimeFormatter ایک Foundation کلاس ہے جو تاریخوں کو نسبتی فارمیٹ میں دکھاتی ہے: "5 منٹ پہلے"، "2 دنوں میں"۔ iOS 13 اور macOS 10.15 سے دستیاب۔
سب سے بڑی غیر صفر اکائی کے اصول کے ذریعے — سیکنڈ، منٹ، گھنٹے، دن، ہفتے، مہینے یا سال۔ مثال کے طور پر، 3720 سیکنڈ (1 گھنٹہ 2 منٹ) کے فرق کے لیے، "گھنٹہ" اکائی منتخب ہوتی ہے، "منٹ" نہیں۔
locale خاصیت سیٹ کریں مطلوبہ Locale مثال پر۔ بطور ڈیفالٹ Locale.current استعمال ہوتا ہے۔ مثال: جرمن کے لیے formatter.locale = Locale(identifier: "de_DE")۔
.numeric — مکمل شکل ("3 دن پہلے")، .abbreviated — مخفف شکل ("3 د. پہلے")۔ انتخاب سیاق و سباق پر منحصر ہے: مرکزی UI کے لیے numeric، کمپیکٹ عناصر کے لیے abbreviated۔
ایک دستی جانچ شامل کریں 5-10 سیکنڈ سے کم وقفے کے لیے۔ RelativeDateTimeFormatter "ابھی" کو سپورٹ نہیں کرتا — چھوٹے وقفوں کے لیے یہ "0 سیکنڈ پہلے" لوٹاتا ہے۔ حد کے ساتھ مشروط منطق استعمال کریں۔
خلاصہ
ہم ایک موبائل ایپلیکیشن ٹرنکی تیار کریں گے
IT Sectr 2017 سے اسٹارٹ اپس اور کاروبار کے لیے iOS اور Android ایپلیکیشنز بناتا ہے۔ ہم آپ کو مشورہ دیں گے اور بہترین حل تجویز کریں گے۔
مزید پڑھیں