ISO8601DateFormatter — کلاس Foundation در iOS و macOS است که برای قالببندی و تجزیه تاریخها در استاندارد بینالمللی ISO 8601 طراحی شده است. بر اساس Apple Developer Documentation, 2024، ISO8601DateFormatter به طور خودکار فرمتهای دارای میلیثانیه، مناطق زمانی و کسری از ثانیه را بدون نیاز به تنظیم دستی DateFormat پردازش میکند. برخلاف DateFormatter، این کلاس به Locale و TimeZone وابسته نیست — دقیقاً مطابق با مشخصات ISO 8601 کار میکند که آن را برای تبادل تاریخ بین سرور و کلاینت ایدهآل میسازد. این کلاس از iOS 10 و macOS 10.12 در دسترس است.
نکات اصلی
ISO8601DateFormatter — زیرکلاس تخصصی Formatter در Foundation است که تبدیل دوطرفه بین Date و رشته با فرمت ISO 8601 را پیادهسازی میکند. استاندارد ISO 8601 (International Standard for the Representation of Dates and Times) فرمت بینالمللی تبادل تاریخ و زمان را تعریف میکند: 2024-07-21T14:30:00+00:00. برخلاف DateFormatter، این کلاس نیازی به تعیین dateFormat ندارد و به طور خودکار ساختار رشته را بر اساس گزینههای داده شده تعیین میکند.
مزایای اصلی ISO8601DateFormatter نسبت به DateFormatter: عدم وابستگی به locale (تجزیه روی هر دستگاهی یکسان کار میکند)، پشتیبانی داخلی از کسری ثانیه (با هر تعداد رقم اعشار) و تعیین خودکار فرمت بر اساس گزینههای ارسال شده. این کلاس همچنین پسوند Z (نشاندهنده UTC)، مناطق زمانی در قالب +HH:mm و دقت کاهشیافته (فقط تاریخ بدون زمان) را به درستی پردازش میکند.
طبق ISO Specification (ISO 8601-1:2019)، استاندارد از چهار سطح دقت پشتیبانی میکند: سال (2024)، سال-ماه (2024-07)، تاریخ کامل (2024-07-21) و تاریخ-زمان با منطقه زمانی (2024-07-21T14:30:00+00:00). ISO8601DateFormatter همه این سطوح را از طریق ترکیب گزینههای فرمت پوشش میدهد و برنامهنویس را از ساخت دستی رشته dateFormat بینیاز میکند.
اصل کار ISO8601DateFormatter بر اساس ترکیب گزینههای بیتی (formatOptions) است که هر کدام یک مؤلفه خاص تاریخ یا زمان را در خروجی فعال میکند. به عنوان مثال، گزینه .withFullDate سال، ماه و روز را فعال میکند؛ .withTime — ساعت، دقیقه و ثانیه. با ترکیب گزینهها، برنامهنویس سطح دقت مورد نیاز را بدون نوشتن رشته dateFormat به دست میآورد.
در داخل، ISO8601DateFormatter از کتابخانه ICU برای تجزیه استفاده میکند، اما با قوانین ثابت ISO 8601. این بدان معناست که تنظیمات Locale و TimeZone نصب شده روی دستگاه را نادیده میگیرد — نتیجه همیشه قابل پیشبینی است. برای تنظیم منطقه زمانی از ویژگی timeZone استفاده میشود که به طور پیشفرض برابر با UTC است. اگر timeZone روی nil تنظیم شود، از زمان محلی دستگاه استفاده میشود.
| گزینه | توضیحات | نمونه خروجی |
|---|---|---|
| .withFullDate | سال، ماه، روز | 2024-07-21 |
| .withTime | ساعت، دقیقه، ثانیه | 14:30:00 |
| .withMilliseconds | کسری از ثانیه (تا ۳ رقم) | .123 |
| .withFractionalSeconds | کسری از ثانیه (هر دقتی) | .123456 |
| .withTimeZone | منطقه زمانی | +03:00 |
| .withColonSeparatorInTimeZone | جداساز : در منطقه زمانی | +03:00 (به جای +0300) |
| .withInternetDateTime | فرمت کامل (date + time + tz) | 2024-07-21T14:30:00+00:00 |
ترکیب گزینهها: .withInternetDateTime معادل ترکیب .withFullDate، .withTime و .withTimeZone است. برای تجزیه رشتههای دارای میلیثانیه، .withFractionalSeconds را اضافه کنید. مهم است به خاطر داشته باشید که .withMilliseconds کسری ثانیه را به سه رقم محدود میکند، در حالی که .withFractionalSeconds هر دقتی را پشتیبانی میکند — از یک تا نه رقم اعشار.
گزینههای فرمت ISO8601DateFormatter به سه گروه تقسیم میشوند: مؤلفههای تاریخ (withFullDate، withYear، withMonth، withDay، withWeekOfYear)، مؤلفههای زمان (withTime، withHours، withMinutes، withSeconds) و تنظیمات اضافی (withMilliseconds، withFractionalSeconds، withTimeZone، withColonSeparatorInTimeZone، withDashSeparatorInDate، withFullTime). با ترکیب آنها، میتوان تقریباً هر زیرفرمت ISO 8601 را به دست آورد.
نکته مهم: .withFractionalSeconds و .withMilliseconds متقابلاً انحصاری هستند — اگر هر دو تنظیم شوند، .withFractionalSeconds اعمال میشود. برای تجزیه میلیثانیهها از دادههای سرور، .withFractionalSeconds توصیه میشود، زیرا بسیاری از سرورها کسری ثانیه را با سه، شش یا نه رقم ارسال میکنند و .withFractionalSeconds هر طولی را پردازش میکند.
import Foundation
// پیکربندی ISO8601DateFormatter
let formatter = ISO8601DateFormatter()
formatter.timeZone = TimeZone(secondsFromGMT: 0)
// ترکیبهای مختلف گزینههای فرمت
formatter.formatOptions = [.withFullDate]
let dateOnly = formatter.string(from: Date())
print("تاریخ: \(dateOnly)")
formatter.formatOptions = [.withFullDate, .withTime]
let dateTime = formatter.string(from: Date())
print("DateTime: \(dateTime)")
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
let full = formatter.string(from: Date())
print("کامل: \(full)")
// تجزیه رشته با میلیثانیه
let serverString = "2024-07-21T14:30:00.123456+03:00"
if let parsed = formatter.date(from: serverString) {
print("تجزیه شد: \(parsed)")
}
استفاده پایه ISO8601DateFormatter به ایجاد نمونه، تنظیم timeZone (توصیه میشود UTC برای دادههای سرور) و formatOptions خلاصه میشود، پس از آن میتوان string(from:) برای قالببندی و date(from:) برای تجزیه فراخوانی کرد. برخلاف DateFormatter، نیازی به نگرانی درباره Locale نیست — کلاس تنظیمات منطقهای را نادیده میگیرد.
import Foundation
let formatter = ISO8601DateFormatter()
// تجزیه فرمتهای مختلف ISO 8601
let strings: [String] = [
"2024-07-21T14:30:00Z",
"2024-07-21T14:30:00+03:00",
"2024-07-21T14:30:00.123Z",
"2024-07-21"
]
for str in strings {
if let autoParsed = formatter.date(from: str) {
print("تجزیه شد '\(str)': \(autoParsed)")
} else {
// برای رشتههای فقط تاریخ از withFullDate استفاده کنید
formatter.formatOptions = [.withFullDate]
if let fallback = formatter.date(from: str) {
print("بازگشت تجزیه شد '\(str)': \(fallback)")
}
formatter.formatOptions = [.withInternetDateTime]
}
}
// سریالسازی به RFC 3339 (API GitHub)
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
let rfc3339 = formatter.string(from: Date())
print("RFC 3339: \(rfc3339)")
تجزیه تاریخهای دارای کسری ثانیه با طول متغیر — ویژگی بسیاری از APIهای مدرن. سرور میتواند هم 2024-07-21T14:30:00.123Z (۳ رقم) و هم 2024-07-21T14:30:00.123456Z (۶ رقم) ارسال کند. ISO8601DateFormatter با گزینه .withFractionalSeconds هر دو گزینه را به درستی پردازش میکند، در حالی که DateFormatter با dateFormat = “yyyy-MM-dd’T’HH:mm:ss.SSSZ” فقط میلیثانیههای سه رقمی را پردازش میکند.
import Foundation
let variantFormatter = ISO8601DateFormatter()
variantFormatter.formatOptions = [
.withInternetDateTime,
.withFractionalSeconds
]
// دقت مختلف کسری ثانیه
let variants: [String] = [
"2024-07-21T14:30:00.1Z",
"2024-07-21T14:30:00.12Z",
"2024-07-21T14:30:00.123Z",
"2024-07-21T14:30:00.123456Z",
"2024-07-21T14:30:00.123456789Z"
]
for variant in variants {
if let parsed = variantFormatter.date(from: variant) {
print("OK: \(variant) -> \(parsed)")
} else {
print("FAIL: \(variant)")
}
}
// از withMilliseconds استفاده کنید (فقط ۳ رقم)
variantFormatter.formatOptions = [
.withInternetDateTime,
.withMilliseconds
]
let milliParsed = variantFormatter.string(from: Date())
print("با میلیثانیه: \(milliParsed)")
تست تجزیه همه گزینهها: کد بالا نشان میدهد که ISO8601DateFormatter با .withFractionalSeconds کسری ثانیه با هر طولی از ۱ تا ۹ رقم را با موفقیت پردازش میکند. این برای سازگاری با پلتفرمهای مختلف سرور مهم است: .NET اغلب ۷ رقم (تیکهای ۱۰۰ نانوثانیه)، Python — ۶، Java — ۳ یا ۹ بسته به نسخه تولید میکند.
DateFormatter نیز میتواند ISO 8601 را تجزیه کند، اما نیاز به تنظیم دستی dateFormat، locale و timeZone دارد. مشکل اصلی این است که DateFormatter به Locale وابسته است و اگر en_US_POSIX تنظیم نشود، تجزیه ممکن است برای کاربران مناطق با فرمتهای تاریخ غیراستاندارد خراب شود. ISO8601DateFormatter این مشکل را در سطح معماری حل میکند: از Locale استفاده نمیکند.
| پارامتر | ISO8601DateFormatter | DateFormatter |
|---|---|---|
| تنظیم Locale | نیاز ندارد (نادیده میگیرد) | en_US_POSIX اجباری |
| DateFormat | خودکار (از طریق گزینهها) | رشته format دستی |
| ثانیههای کسری | هر دقتی (.withFractionalSeconds) | SSS ثابت |
| پسوند Z | به درستی پردازش میکند | از طریق dateFormat |
| کارایی | بالاتر (تخصصی) | پایینتر (عمومی) |
| استاندارد | فقط ISO 8601 | هر فرمتی |
| نسخه iOS | iOS 10+ | iOS 2+ |
چه زمانی از DateFormatter استفاده کنیم: اگر نیاز به قالببندی تاریخ در قالبی غیر از ISO 8601 دارید (مثلاً «۲۱ ژوئیه ۲۰۲۴» برای UI) یا اگر پشتیبانی از iOS 9 و قدیمیتر مورد نیاز است. برای همه وظایف تبادل تاریخ با سرور از ISO8601DateFormatter استفاده کنید — ایمنتر، کارآمدتر و با کد کمتر است. DateFormatter برای ISO 8601 منبع اشکالات بالقوه مربوط به locale و تنظیمات منطقهای است.
مهاجرت از DateFormatter به ISO8601DateFormatter: ایجاد DateFormatter + تنظیم dateFormat + locale + timeZone را با ایجاد ISO8601DateFormatter + تنظیم formatOptions + timeZone جایگزین کنید. تجزیه رشته از طریق date(from:) بدون تغییر باقی میماند. برای سازگاری عقبگرد، میتوان از #available(iOS 10, *) با fallback روی DateFormatter استفاده کرد.
فراموش کردن تنظیم formatOptions باعث میشود formatter از مقدار پیشفرض — .withInternetDateTime استفاده کند. اگر سرور فقط تاریخ بدون زمان ارسال کند (2024-07-21)، تجزیه nil برمیگرداند. همیشه بررسی کنید که formatOptions همه فرمتهای ممکنی که از سرور میآیند را پوشش میدهد. برای API با فرمتهای متغیر، از تلاشهای fallback با ترکیبهای مختلف گزینهها استفاده کنید.
اشتباه گرفتن withMilliseconds و withFractionalSeconds — خطای رایج هنگام تجزیه تاریخهای دارای کسری ثانیه. withMilliseconds دقیقاً ۳ رقم اعشار انتظار دارد. اگر سرور ۶ رقم (میکروثانیه) ارسال کند، تجزیه با withMilliseconds با خطا مواجه میشود. برای سازگاری با هر تعداد رقم از .withFractionalSeconds استفاده کنید. .withFractionalSeconds در iOS 13 ظاهر شد؛ برای نسخههای قدیمیتر از DateFormatter با dateFormat استفاده کنید.
نادیده گرفتن منطقه زمانی — مشکل رایج دیگر. اگر سرور تاریخ را با منطقه زمانی ارسال کند (+03:00) و formatter روی UTC تنظیم شده باشد، تجزیه خراب نمیشود اما نتیجه به UTC خواهد بود. توسعهدهندگان اغلب انتظار دارند که Date منطقه زمانی را حفظ کند، اما Date یک لحظه مطلق در زمان است و اطلاعات منطقه زمانی را ذخیره نمیکند. برای نمایش صحیح، منطقه زمانی را جداگانه ذخیره کنید یا از ISO8601DateFormatter با timeZone درست استفاده کنید.
طبق Apple Forum (2024)، حدود ۲۰٪ سوالات مربوط به ISO8601DateFormatter به فرمتی مربوط میشود که ثانیه در آن اختیاری است. استاندارد ISO 8601 فرمت بدون ثانیه را مجاز میداند: 2024-07-21T14:30+03:00. ISO8601DateFormatter با .withInternetDateTime از این فرمت پشتیبانی نمیکند — برای تجزیه آن به DateFormatter با dateFormat = «yyyy-MM-dd’T’HH:mmZ» نیاز است. این محدودیت هنگام کار با APIهایی که از فرمت زمان کوتاهشده استفاده میکنند باید در نظر گرفته شود.
سوالات متداول
ISO8601DateFormatter — کلاس تخصصی Foundation برای قالببندی و تجزیه تاریخها در فرمت ISO 8601، قابل دسترس از iOS 10. به طور خودکار فرمتهای استاندارد را بدون تعیین دستی dateFormat پردازش میکند.
ISO8601DateFormatter به Locale وابسته نیست، به جای dateFormat از گزینهها استفاده میکند و کسری ثانیه را با هر طولی به درستی پردازش میکند. DateFormatter جهانی است اما نیاز به تنظیم دستی دارد و مستعد اشکالات مربوط به تنظیمات منطقهای است.
از گزینه .withFractionalSeconds استفاده کنید — از ۱ تا ۹ رقم اعشار را پشتیبانی میکند. اگر دقت میتواند متغیر باشد از .withMilliseconds استفاده نکنید. .withFractionalSeconds از iOS 13 در دسترس است.
پیشفرض UTC. برای تغییر، ویژگی timeZone را تنظیم کنید. اگر timeZone = nil باشد، از زمان محلی دستگاه استفاده میشود. هنگام تجزیه رشته با منطقه زمانی صریح در قالب +HH:MM، formatter به طور خودکار آن را در نظر میگیرد.
زیرا formatOptions پیشفرض = .withInternetDateTime است، که انتظار تاریخ + زمان + منطقه زمانی دارد. برای تجزیه فقط تاریخ، formatOptions = [.withFullDate] را تنظیم کنید. برای پشتیبانی از هر دو گزینه، از fallback با گزینههای مختلف استفاده کنید.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید