DateFormatter: مفاهیم کلیدی، قالب‌بندی تاریخ و محلی‌سازی

نویسنده: IT Sectr منتشر شده: 2026-07-12 زمان مطالعه: 7 دقیقه

DateFormatter « یک کلاس از Foundation است که برای تبدیل دوطرفه بین اشیاء Date و نمایش رشته‌ای آنها طراحی شده است. این کلاس محلی (locale)، منطقه زمانی و تقویم کاربر را در نظر می‌گیرد و نمایش صحیح تاریخ را در هر منطقه از جهان تضمین می‌کند. بر اساس Apple Developer Documentation (2025)، DateFormatter از چهار سبک از پیش تعیین‌شده تاریخ و زمان، و همچنین فرمت‌های کاملاً سفارشی از طریق رشته الگو پشتیبانی می‌کند. بدون DateFormatter نمی‌توان تاریخ را به درستی در یک برنامه بین‌المللی‌سازی شده به کاربر نمایش داد.

اهم نکات

  • DateFormatter « کلاسی برای تبدیل Date به رشته و بالعکس با در نظر گرفتن locale و منطقه زمانی.
  • dateStyle و timeStyle « سبک‌های از پیش تعیین‌شده (.short، .medium، .long، .full) برای قالب‌بندی سریع.
  • dateFormat « رشته الگو برای فرمت سفارشی که از طریق نمادهای مشخصات Unicode LDML تعیین می‌شود.
  • Locale و TimeZone « ویژگی‌های فرمت‌دهنده که نمایش منطقه‌ای و منطقه زمانی را تعیین می‌کنند.
  • ISO8601DateFormatter « جایگزین سریع‌تر برای فرمت ISO 8601 هنگام سریال‌سازی در API.

DateFormatter چیست؟

DateFormatter « کلاسی از فریم‌ورک Foundation است که تبدیل دوطرفه بین Date و رشته را پیاده‌سازی می‌کند. این کلاس در OpenStep به عنوان NSDateFormatter ظاهر شد و از آن زمان به عنوان ابزار اصلی قالب‌بندی تاریخ در تمام پلتفرم‌های Apple باقی مانده است. کلاس از Formatter ارث‌بری می‌کند و API مناسبی برای نمایش محلی‌سازی شده تاریخ‌ها فراهم می‌کند.

اصل کار DateFormatter بر اساس الگوهای Unicode LDML « همان مواردی است که در ICU (International Components for Unicode) استفاده می‌شود. الگو از طریق ویژگی dateFormat تعیین می‌شود، جایی که نمادهای y، M، d، H، m، s به ترتیب با سال، ماه، روز، ساعت، دقیقه، ثانیه مطابقت دارند. تکرار نماد فرمت را تعیین می‌کند: "y" « سال دو رقمی، "yyyy" « چهار رقمی.

ایجاد DateFormatter « عملیات پرهزینه‌ای است، زیرا هنگام مقداردهی اولیه داده‌های locale و تقویم بارگذاری می‌شوند. Apple توصیه می‌کند فرمت‌دهنده را یک بار برای هر نوع قالب‌بندی ایجاد کرده و مجدداً استفاده کنید. در SwiftUI و UIKit، فرمت‌دهنده‌ها اغلب در ویژگی‌های استاتیک ذخیره یا به صورت تنبل (lazy) در اولین فراخوانی ایجاد می‌شوند.

DateFormatter در iOS SDK

DateFormatter در بسیاری از اجزای سیستمی iOS استفاده می‌شود. UIDatePicker از DateFormatter در داخل خود برای نمایش تاریخ‌ها در حالت countDownTimer استفاده می‌کند. TextField با فرمت‌دهنده در ورودی می‌تواند به طور خودکار تاریخ وارد شده توسط کاربر را اعتبارسنجی کند. Core Data از ویژگی‌های نوع Date پشتیبانی می‌کند، اما نمایش رشته‌ای آنها همیشه از طریق DateFormatter انجام می‌شود.

Thread Safety « DateFormatter thread-safe نیست. تغییر ویژگی‌های فرمت‌دهنده از رشته‌های مختلف منجر به رفتار نامشخص می‌شود. برای استفاده چندنخی، نمونه‌های جداگانه فرمت‌دهنده برای هر نخ ایجاد کنید یا از همگام‌سازی از طریق NSLock یا serial queue استفاده کنید.

سبک‌های قالب‌بندی DateFormatter

dateStyle و timeStyle « ساده‌ترین راه‌های پیکربندی نمایش تاریخ هستند. هر سبک چهار گزینه دارد: .short، .medium، .long و .full. ترکیب dateStyle و timeStyle به شما امکان می‌دهد فرمت تاریخ و زمان را به طور مستقل پیکربندی کنید و ویژگی .none بخش مربوطه را غیرفعال می‌کند.

برای locale آمریکایی، .short تاریخ را به صورت "7/21/26" و برای روسی به صورت "21.07.2026" قالب‌بندی می‌کند. سبک .long برای locale روسی "۲۱ ژوئیه ۲۰۲۶" و .full « "دوشنبه، ۲۱ ژوئیه ۲۰۲۶" را با ذکر روز هفته نمایش می‌دهد. هر چهار سبک به طور خودکار با استانداردهای منطقه‌ای از جمله ترتیب اجزا و جداکننده‌ها تطبیق می‌یابند.

RelativeDateFormatter در iOS 15+ رویکرد جایگزینی را از طریق RelativeDateFormatter و DateIntervalFormatter ارائه می‌دهد. RelativeDateFormatter « امروز، دیروز، ۳ روز بعد » را برای زمینه فوری نمایش می‌دهد. DateIntervalFormatter محدوده تاریخ‌ها را نمایش می‌دهد: « ۲۱–۲۵ ژوئیه ۲۰۲۶ » « برای رزرو و برنامه‌ریزی.

سبکمثال (ru_RU)مثال (en_US)
.short21.07.20267/21/26
.medium۲۱ ژوئیه ۲۰۲۶Jul 21, 2026
.long۲۱ ژوئیه ۲۰۲۶July 21, 2026
.fullدوشنبه، ۲۱ ژوئیه ۲۰۲۶Tuesday, July 21, 2026

هنگام ترکیب سبک‌ها، DateFormatter به طور خودکار جداکننده را انتخاب می‌کند: برای .short.date + .short.time نتیجه ممکن است "21.07.2026, 14:30" باشد. برای .full.date + .full.time « "دوشنبه، ۲۱ ژوئیه ۲۰۲۶، ۱۴:۳۰:۰۰ UTC+۳:۳۰". جداکننده توسط locale کنترل می‌شود، نه توسط توسعه‌دهنده « این مطابقت با انتظارات منطقه‌ای کاربر را تضمین می‌کند.

فرمت‌های سفارشی از طریق dateFormat

dateFormat به شما امکان می‌دهد با استفاده از نمادهای مشخصات Unicode LDML یک الگوی قالب‌بندی دلخواه تعیین کنید. این کنترل کامل بر نمایش می‌دهد: می‌توانید فقط سال و ماه، یا روز هفته بدون تاریخ، یا زمان بدون ثانیه را نمایش دهید. فرمت سفارشی برای نیازهای خاص طراحی ضروری است.

نمادهای اصلی « yyyy (سال: 2026)، MM (ماه: 07)، dd (روز: 21)، HH (ساعت: 14)، mm (دقیقه: 30)، ss (ثانیه: 00). برای نام کامل ماه از MMMM (سپتامبر) و برای مختصر از MMM (سپت) استفاده کنید. روز هفته « EEEE (دوشنبه)، مختصر « E (دو).

هنگام استفاده از dateFormat، مهم است که locale فرمت‌دهنده را تنظیم کنید. اگر locale تنظیم نشود، فرمت‌دهنده از locale سیستم استفاده می‌کند که ممکن است برای فرمت ثابت در API نامطلوب باشد. Apple توصیه می‌کند locale = Locale(identifier: "en_US_POSIX") را برای فرمت ثابت بین‌منطقه‌ای تنظیم کنید، به ویژه هنگام تجزیه تاریخ‌ها از پاسخ‌های سرور.

swift
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "ru_RU")
formatter.dateFormat = "d MMMM yyyy"
let customString = formatter.string(from: Date())
// "۲۱ ژوئیه ۲۰۲۶"

// تجزیه رشته سفارشی
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let date = formatter.date(from: "2026-07-21 14:30:00")!

خطا در dateFormat « یکی از دلایل رایج کرش برنامه است. اگر فرمت با رشته مطابقت نداشته باشد، متد date(from:) nil برمی‌گرداند. از guard let یا ?? برای استخراج ایمن مقدار اختیاری استفاده کنید. برای اعتبارسنجی فرمت، آن را در تمام زبان‌های پشتیبانی‌شده تست کنید « برخی نمادهای LDML در locale‌های مختلف متفاوت عمل می‌کنند.

محلی‌سازی و TimeZone

Locale تعیین می‌کند که نام ماه‌ها، روزهای هفته و جداکننده‌ها چگونه نمایش داده شوند. DateFormatter به طور پیش‌فرض از Locale.current استفاده می‌کند، اما در برخی سناریوها باید یک locale خاص مشخص شود: برای فرمت ثابت در لاگ‌ها از en_US_POSIX استفاده کنید، برای تاریخ‌های سرور « locale مشابه سرور.

ویژگی TimeZone منطقه زمانی را برای نمایش تعیین می‌کند. به طور پیش‌فرض از منطقه زمانی سیستم استفاده می‌شود، اما برای برنامه‌های با مخاطب بین‌المللی اغلب لازم است تاریخ‌ها را در منطقه زمانی کاربر یا UTC نمایش دهید. تغییر timeZone فقط بر نمایش تأثیر می‌گذارد « مقدار Date بدون تغییر باقی می‌ماند.

ویژگی مهم: اگر DateFormatter برای تجزیه یک رشته استفاده می‌شود و رشته حاوی منطقه زمانی است (مثلاً "2026-07-21T14:30:00Z" با Z برای UTC)، ویژگی timeZone نادیده گرفته می‌شود « فرمت‌دهنده از منطقه زمانی موجود در رشته استفاده می‌کند. اگر منطقه زمانی در رشته وجود نداشته باشد، timeZone فرمت‌دهنده اعمال می‌شود.

swift
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "ru_RU")
formatter.timeZone = TimeZone(identifier: "Europe/Moscow")
formatter.dateStyle = .long
formatter.timeStyle = .short

let moscowTime = formatter.string(from: Date())
// "۲۱ ژوئیه ۲۰۲۶، ۱۴:۳۰"

// تجزیه بدون منطقه زمانی در رشته
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.dateFormat = "yyyy-MM-dd HH:mm"
let utcDate = formatter.date(from: "2026-07-21 10:30")!

AutoupdatingCurrentLocale « نوع خاصی از locale است که به طور خودکار با تغییر تنظیمات سیستم کاربر به‌روزرسانی می‌شود. DateFormatter به طور پیش‌فرض از آن پشتیبانی می‌کند. اگر برنامه در پس‌زمینه کار می‌کند و کاربر زبان سیستم را تغییر می‌دهد، فرمت‌دهنده ایجاد شده قبل از تغییر به استفاده از locale قدیمی ادامه می‌دهد « برای به‌روزرسانی باید یک نمونه جدید ایجاد کنید.

ISO8601DateFormatter و جایگزین‌ها

ISO8601DateFormatter « یک فرمت‌دهنده تخصصی برای کار با تاریخ‌ها در قالب ISO 8601 است. این فرمت استاندارد دی‌فاکتوی REST API، JSON و تبادل داده است. ISO8601DateFormatter به طور قابل توجهی سریع‌تر از DateFormatter کار می‌کند، زیرا به locale وابسته نیست و از دستور زبان تجزیه ثابتی استفاده می‌کند.

گزینه‌های اصلی فرمت‌دهنده « .withInternetDateTime (2026-07-21T14:30:00Z)، .withFractionalSeconds (میلی‌ثانیه اضافه می‌کند)، .withTimeZone (افست منطقه زمانی را شامل می‌شود). با ترکیب گزینه‌ها می‌توان هر نوع ISO 8601 را دریافت کرد: با میلی‌ثانیه، با منطقه زمانی، با تاریخ بدون زمان.

JSONEncoder.DateEncodingStrategy به شما امکان می‌دهد کدگذاری تاریخ‌ها را برای تمام مدل‌های Codable به صورت سراسری پیکربندی کنید. گزینه‌ها « .iso8601 (استفاده از ISO8601DateFormatter)، .formatted(DateFormatter)، .millisecondsSince1970، .secondsSince1970. انتخاب استراتژی بر کل چرخه سریال‌سازی تأثیر می‌گذارد و باید برای همه نقاط API یکسان باشد.

swift
// ISO8601DateFormatter
let isoFormatter = ISO8601DateFormatter()
isoFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
let isoString = isoFormatter.string(from: Date())
// "2026-07-21T14:30:00.000Z"

// JSONEncoder با ISO8601
let encoder = JSONEncoder()
encoder.dateEncodingStrategy = .iso8601

// جایگزین: JSONEncoder با فرمت‌دهنده سفارشی
let customEncoder = JSONEncoder()
customEncoder.dateEncodingStrategy = .formatted(myFormatter)

DateFormatter در مقابل ISO8601DateFormatter « ISO8601DateFormatter را برای سریال‌سازی و تجزیه تاریخ‌ها در API انتخاب کنید، زیرا ۵–۱۰ بار سریع‌تر از DateFormatter کار می‌کند و خطاهای محلی‌سازی ندارد. DateFormatter را برای رابط کاربری نگه دارید، جایی که نمایش محلی‌سازی شده با نام ماه‌ها و روزهای هفته به زبان مادری کاربر مورد نیاز است.

نمونه‌های قالب‌بندی تاریخ

بیایید سناریوهای واقعی استفاده از DateFormatter را در یک برنامه iOS بررسی کنیم: نمایش در فهرست اخبار، ورود تاریخ تولد و خروجی گزارش با تاریخ‌ها در مناطق زمانی مختلف.

نمایش تاریخ خبر در فهرست

RelativeDateFormatter برای فیدهای خبری بهینه است. این فرمت‌دهنده « همین حالا »، « ۵ دقیقه پیش »، « دیروز » را برای اخبار تازه نمایش می‌دهد و برای اخبار قدیمی به تاریخ کامل سویچ می‌کند. آستانه سویچینگ از طریق calendar تنظیم می‌شود: برای اخبار از آستانه ۲۴ ساعت و برای پیام‌رسان‌ها از یک هفته استفاده کنید.

swift
func formatRelativeDate(_ date: Date) -> String {
    let relative = RelativeDateFormatter()
    relative.unitsStyle = .full

    let formatter = DateFormatter()
    formatter.dateStyle = .medium
    formatter.timeStyle = .short

    let daysDiff = Calendar.current.dateComponents(
        [.day], from: date, to: Date()
    ).day ?? 0

    return daysDiff < 1
        ? relative.localizedString(for: date, relativeTo: Date())
        : formatter.string(from: date)
}

ورود تاریخ تولد « یک سناریوی رایج دیگر. DateFormatter با dateFormat مشخص "dd.MM.yyyy" و locale "ru_RU" پیکربندی می‌شود. هنگام تجزیه رشته وارد شده، مهم است که خطاهای احتمالی را مدیریت کنید: فرمت‌دهنده برای رشته نامعتبر nil برمی‌گرداند. پس از تجزیه موفق، تاریخ از نظر قرار گرفتن در محدوده مجاز بررسی می‌شود « نه زودتر از سال ۱۹۰۰ و نه دیرتر از امروز.

خروجی گزارش با تاریخ‌ها نیاز به فرمت ثابتی دارد که به locale کاربر وابسته نباشد. از dateFormat "yyyy-MM-dd HH:mm:ss" با locale en_US_POSIX و منطقه زمانی UTC استفاده کنید. این رویکرد تضمین می‌کند که فایل در هر کشوری بدون توجه به تنظیمات منطقه‌ای سیستم به درستی باز می‌شود.

سناریوفرمت‌دهندهتنظیم کلیدی
فید خبریRelativeDateFormatterunitsStyle = .full
ورود تاریخDateFormatterdateFormat + fallback
سریال‌سازی APIISO8601DateFormatterwithInternetDateTime
خروجی گزارشDateFormatteren_US_POSIX + UTC

سوالات متداول

چرا DateFormatter برای یک رشته صحیح nil برمی‌گرداند؟

شایع‌ترین دلیل « عدم تطابق dateFormat با فرمت رشته است. مثلاً، فرمات "dd.MM.yyyy" رشته "2026-07-21" را تجزیه نمی‌کند. دلیل دوم « عدم تطابق locale: رشته "July 21, 2026" با locale ru_RU تجزیه نمی‌شود. دلیل سوم « اشتباهات تایپی در نمادهای LDML: از yyyy استفاده کنید نه YYYY (معنای متفاوت).

آیا باید برای هر فراخوانی یک DateFormatter جدید ایجاد کرد؟

خیر. DateFormatter یک شیء سنگین است و مقداردهی اولیه آن شامل بارگذاری داده‌های locale است. یک نمونه برای هر نوع قالب‌بندی ایجاد کنید و مجدداً استفاده کنید. در محیط چندنخی، از Thread-local storage یا استخر فرمت‌دهنده‌ها با serial queue برای همگام‌سازی استفاده کنید.

تفاوت DateFormatter با RelativeDateFormatter چیست؟

DateFormatter تاریخ مطلق (۲۱ ژوئیه ۲۰۲۶) را نمایش می‌دهد، در حالی که RelativeDateFormatter « تاریخ نسبی (امروز، دیروز، ۳ روز بعد). RelativeDateFormatter در iOS 15+ ظاهر شد و از همان الگوی LDML استفاده می‌کند، اما به طور خودکار نمایش نسبی را انتخاب می‌کند.

چگونه تاریخ‌های بدون منطقه زمانی را از API پردازش کنیم؟

timeZone فرمت‌دهنده را قبل از تجزیه بر روی UTC تنظیم کنید. اگر سرور تاریخ را به وقت محلی بدون ذکر منطقه زمانی ارسال می‌کند، مشخصات API را بررسی کنید « به احتمال زیاد UTC مورد نظر است. برای ISO 8601 با Z در انتها، timeZone نیاز نیست « فرمت‌دهنده افست را از رشته استخراج می‌کند.

چگونه DateFormatter را thread-safe کنیم؟

از یک نمونه در رشته‌های مختلف بدون همگام‌سازی استفاده نکنید. در هر رشته یک نمونه جدید ایجاد کنید یا از Thread.current.threadDictionary برای ذخیره‌سازی استفاده کنید. جایگزین « NSLock با قفل کردن در طول string(from:) و date(from:).

نتایج

  • DateFormatter « کلاس Foundation برای تبدیل Date به رشته و بالعکس با در نظر گرفتن locale، منطقه زمانی و تقویم کاربر.
  • سبک‌های از پیش تعیین‌شده dateStyle و timeStyle با گزینه‌های .short، .medium، .long، .full بیشتر سناریوهای UI را پوشش می‌دهند.
  • dateFormat سفارشی از طریق نمادهای LDML کنترل کامل بر فرمت را فراهم می‌کند، اما نیاز به دقت در محلی‌سازی دارد.
  • Locale و TimeZone « ویژگی‌های ضروری برای نمایش صحیح: برای UI « locale سیستم، برای API « en_US_POSIX و UTC.
  • ISO8601DateFormatter « انتخاب ترجیحی برای سریال‌سازی تاریخ‌ها در API به دلیل سرعت و پایداری.
  • DateFormatter thread-safe نیست « از نمونه‌های جداگانه برای هر رشته استفاده کنید یا دسترسی را همگام کنید.
  • RelativeDateFormatter (iOS 15+) « راه‌حل بهینه برای نمایش تاریخ‌های نسبی در فیدهای خبری و پیام‌رسان‌ها.

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید