DateComponents هي بنية في Foundation تخزن مكونات التاريخ التقويمي كحقول منفصلة: السنة والشهر واليوم والساعة والدقيقة والثانية وغيرها. على عكس Date الذي يمثل لحظة زمنية مطلقة، يحتوي DateComponents على قيم readable تعتمد على التقويم والمنطقة الزمنية. وفقاً لوثائق Apple Developer (2025)، يُستخدم DateComponents كحلقة وصل بين Date و Calendar — من خلاله يتم استخراج وبناء التواريخ التقويمية، وإجراء العمليات الحسابية وتحويل التواريخ دون حساب يدوي.
الرئيسية
DateComponents هو نوع قيم في Foundation مصمم لتخزين مكونات الوقت التقويمية. يتم تمثيل كل مكون بحقل Int اختياري: year، month، day، hour، minute، second، nanosecond، weekday، weekOfMonth، weekOfYear، quarter، yearForWeekOfYear وغيرها.
الفرق الرئيسي عن Date هو الارتباط بالتقويم. Date يخزن الوقت المطلق (عدد الثواني من تاريخ المرجع)، بينما DateComponents هو تمثيل readable له معنى فقط في سياق Calendar محدد. نفس Date يمكن تمثيله بـ DateComponents مختلفة في تقاويم ومناطق زمنية مختلفة.
DateComponents ليس نوع وقت مستقل، بل حاوية بيانات. لتفسير DateComponents كتاريخ، يلزم Calendar يفهم كيفية ارتباط المكونات بنظام التقويم. Calendar.dateComponents(from: Date) يقوم باستخراج المكونات، Calendar.date(from: DateComponents) يقوم بالتجميع العكسي.
كل حقل في DateComponents اختياري (Int?)، وهو أمر أساسي للعمل مع التواريخ الجزئية. إذا تم تحديد السنة والشهر فقط، يقوم Calendar بملء الحقول المفقودة بقيم افتراضية: اليوم = 1، الساعة = 0، الدقيقة = 0. هذا مناسب لإنشاء تواريخ بداية الفترة — تحتاج فقط إلى تحديد المكونات المعنية.
عند مقارنة DateComponents باستخدام العامل ==، تتم مقارنة الحقول المحددة فقط (غير nil). هيكلان DateComponents كلاهما بالسنة 2026 ولكن بأشهر مختلفة يعتبران مختلفين. isEqual من NSObjectProtocol لا ينطبق على DateComponents — DateComponents لا يرث من NSObject.
الحقول الرئيسية لـ DateComponents تشمل year، month، day، hour، minute، second، nanosecond. كل حقل يخزن قيمة رقمية في الوحدة المقابلة: السنة — 2026، الشهر — 1..12، اليوم — 1..31، الساعة — 0..23، الدقيقة — 0..59، الثانية — 0..59. يمكن أن تتراوح النانو ثواني من 0 إلى 999999999.
حقول الأسبوع — weekday (1..7، حيث 1 = الأحد في التقويم الميلادي)، weekOfMonth، weekOfYear. تعتمد هذه الحقول على Calendar وليس لها معنى خارج سياقه. يعتمد weekday على إعداد firstWeekday للتقويم: في الإعدادات المحلية الروسية يبدأ الأسبوع من الاثنين (weekday = 2 في النظام الميلادي)، بينما في الأمريكية يبدأ من الأحد (weekday = 1).
حقول متخصصة — quarter (1..4)، yearForWeekOfYear (السنة التي ينتمي إليها الأسبوع)، isLeapMonth (علامة منطقية للأشهر الكبيسة في التقويمين العبري أو الصيني). يخزن الحقلان calendar و timeZone مراجع للكائنات المقابلة التي تم إنشاء البنية بها.
| الفئة | الحقول | النطاق |
|---|---|---|
| تقويمية | year, month, day | 1..∞, 1..12, 1..31 |
| زمنية | hour, minute, second, nanosecond | 0..23, 0..59, 0..59, 0..999999999 |
| أسبوعية | weekday, weekOfMonth, weekOfYear | 1..7, 1..5, 1..53 |
| خاصة | quarter, yearForWeekOfYear | 1..4, تابع |
عند استخراج المكونات عبر Calendar.dateComponents، من المهم طلب الحقول المطلوبة فقط للأداء. Calendar يستخرج جميع الحقول المطلوبة في تمريرة واحدة — وهذا أسرع بكثير من استدعاء Calendar.component لكل حقل على حدة.
تهيئة DateComponents — الطريقة الأبسط: إنشاء بنية فارغة وملء الحقول المطلوبة. جميع الحقول غير المحددة تحصل تلقائياً على nil. التاريخ الذي تم إنشاؤه من مكونات جزئية لا يتم التحقق منه في مرحلة التهيئة — قد يحدث خطأ فقط عند التحويل إلى Date عبر Calendar.
المُهيئ DateComponents(calendar:timeZone:era:year:month:day:hour:minute:second:nanosecond:weekday:…) يسمح بتعيين جميع الحقول في استدعاء واحد. هذا المُهيئ مناسب لإنشاء تاريخ كامل من قيم جاهزة، لكن نادراً ما يُستخدم بأكثر من 5-6 وسائط بسبب readability.
Calendar.dateComponents(_:from:) — الطريقة الرئيسية للحصول على DateComponents من Date موجود. الوسيطة الثانية هي مجموعة المكونات المطلوب استخراجها. Calendar يقوم بالحسابات التقويمية مع مراعاة المنطقة الزمنية ويعيد بنية تحتوي فقط على الحقول المطلوبة؛ الحقول المتبقية تبقى nil.
import Foundation
// إنشاء عبر مُهيئ الحقول
var components = DateComponents()
components.year = 2026
components.month = 7
components.day = 21
// استخراج من Date
let now = Date()
let extracted = Calendar.current.dateComponents(
[.year, .month, .day],
from: now
)
print("Today: \(extracted.day!).\(extracted.month!).\(extracted.year!)")
// إنشاء عبر مُهيئ موسع
let birthday = DateComponents(
calendar: Calendar.current,
year: 1990, month: 5, day: 15
)
عند إنشاء DateComponents يدوياً عن طريق تعيين الحقول، تحقق دائماً من Calendar قبل التحويل إلى Date. عند تحويل date(from:)، قد يعيد Calendar nil إذا كانت المكونات تشكل تاريخاً غير موجود — مثلاً 31 فبراير أو 30 فبراير في سنة غير كبيسة. التحقق من التاريخ هو مسؤولية Calendar، وليست DateComponents.
Calendar.date(from:) — الطريقة الرئيسية لتحويل DateComponents إلى Date. Calendar يفسر المكونات وفقاً لتقويمه ومنطقته الزمنية. إذا لم يتم تعيين بعض الحقول (nil)، يستخدم Calendar القيم الافتراضية: اليوم = 1، الساعة = 0، الدقيقة = 0، الثانية = 0.
الطريقة تُعيد Date اختيارياً — يحدث nil إذا كانت المكونات تتعارض مع بعضها البعض أو تشكل تاريخاً غير صالح. الأسباب النموذجية لـ nil: تاريخ غير موجود (32 يناير، 29 فبراير 2023)، حقول متعارضة (weekday=1، day=5 في نفس المجموعة)، سنة مستحيلة للتقويم المعطى (السنة 0 في التقويم الميلادي).
DateComponents مع timeZone — إذا كان DateComponents يحتوي على timeZone، يستخدمها Calendar أثناء التحويل. إذا لم يتم تحديد timeZone، يستخدم Calendar المنطقة الزمنية الحالية الخاصة به. إذا كانت Calendar.timeZone لا تتطابق مع المنطقة الزمنية المتوقعة للتاريخ، قد يختلف الناتج بعدة ساعات — تأكد من تعيين timeZone بشكل صريح في أحد الكائنين.
let calendar = Calendar(identifier: .gregorian)
// إنشاء Date من DateComponents
var comps = DateComponents()
comps.year = 2026
comps.month = 12
comps.day = 25
comps.hour = 10
if let date = calendar.date(from: comps) {
print("Christmas: \(date)")
}
// إنشاء مع تحديد timeZone
calendar.timeZone = TimeZone(identifier: "UTC")!
let utcComps = DateComponents(
calendar: calendar, year: 2026, month: 7, day: 21,
hour: 12
)
let utcDate = calendar.date(from: utcComps)!
Calendar.dateComponents لفرق التواريخ — حالة استخدام أخرى لـ DateComponents. Calendar.dateComponents([.year, .month, .day], from: Date(), to: futureDate) يُعيد الفرق بالسنوات والأشهر والأيام بين تاريخين. هذه هي الطريقة الصحيحة لحساب العمر بدلاً من قسمة TimeInterval على عدد الثواني في السنة، لأن Calendar يراعي السنوات الكبيسة.
Calendar — الفئة المركزية التي تعمل مع DateComponents. جميع عمليات الاستخراج والتجميع والمقارنة للتواريخ تمر عبر Calendar. بدون Calendar، DateComponents هو مجرد مجموعة أرقام ليس لها معنى زمني. Calendar يعطي المكونات تفسيرها: يحدد أن الشهر 2 هو فبراير، وأن weekday 2 هو الاثنين.
Calendar.nextDate و Calendar.enumerateDates — طريقتان تعتمدان على DateComponents. nextDate(after: Date(), matching: DateComponents) تجد التاريخ التالي المطابق للمكونات المحددة — مثلاً، الاثنين القادم بعد اليوم. enumerateDates(startingAfter:matching:matchingPolicy:using:) تتكرر على جميع التواريخ المطابقة للنمط حتى الحد المحدد.
Calendar.dateInterval — طريقة تُعيد DateInterval للمكون المحدد. dateInterval(of: .month, for: Date()) تُعيد بداية ونهاية الشهر الحالي. داخلياً، تستخدم هذه الطريقة DateComponents لإيجاد حدود الفترة: تنشئ DateComponents مع أول وآخر يوم في الشهر وتحولها إلى Date عبر Calendar.
let calendar = Calendar.current
// الاثنين القادم
let nextMonday = calendar.nextDate(
after: Date(),
matching: DateComponents(weekday: 2),
matchingPolicy: .nextTime
)!
// الفرق بين التواريخ بالأيام
let diff = calendar.dateComponents(
[.day], from: Date(), to: nextMonday
)
// نطاق الشهر
let monthInterval = calendar.dateInterval(
of: .month, for: Date()
)!
let startOfMonth = monthInterval.start
let endOfMonth = monthInterval.end
MatchingPolicy — معامل مهم لطرق Calendar عند العمل مع DateComponents. strictPolicy يتطلب تطابقاً دقيقاً لجميع المكونات، nextTimePolicy يختار التطابق الزمني التالي، nextTimePreservingSmallerComponents يحافظ على المكونات الأصغر (الدقائق، الثواني) من التاريخ المصدر. اختيار السياسة يؤثر على نتيجة البحث عن التواريخ، خاصة عند الانتقال عبر تغيير التوقيت الصيفي.
دعنا نستعرض حالات استخدام عملية لـ DateComponents في التطبيق. كل مثال يوضح مهمة نموذجية يواجهها مطور iOS عند العمل مع التواريخ التقويمية.
Calendar.nextDate مع DateComponents(day: 1) تجد اليوم الأول من الشهر التالي. Calendar يحدد تلقائياً عدد الأيام في الشهر الحالي وينتقل إلى التالي. للإشعارات المتكررة، استخدم enumerateDates أو Combine.Timer مع مفتاح Calendar.
func firstDayOfNextMonth(from date: Date) -> Date {
let calendar = Calendar.current
let comps = DateComponents(day: 1)
return calendar.nextDate(
after: date,
matching: comps,
matchingPolicy: .nextTime
)!
}
// حساب العمر بالسنوات
func ageInYears(from birthDate: Date) -> Int {
let calendar = Calendar.current
let ageComponents = calendar.dateComponents(
[.year], from: birthDate, to: Date()
)
return ageComponents.year ?? 0
}
// تجميع الأحداث حسب السنة والشهر
func groupEventsByMonth(_ events: [Event]) -> [String: [Event]] {
let calendar = Calendar.current
return Dictionary(grouping: events) { event in
let comps = calendar.dateComponents(
[.year, .month], from: event.date
)
return "\(comps.year!)-\(comps.month!)"
}
}
حساب العمر عبر Calendar.dateComponents([.year], from:to:) — الطريقة الصحيحة الوحيدة التي تراعي السنوات الكبيسة. الحساب المستند إلى TimeInterval (ثواني / 31536000) يعطي خطأ للأشخاص المولودين في 29 فبراير. Calendar يحدد بشكل صحيح ما إذا كان عيد الميلاد قد حدث في السنة الحالية ويعيد العمر الدقيق.
التجميع حسب السنة والشهر — مهمة شائعة لشاشات السجل أو التقويم. DateComponents يعمل كمفتاح تجميع: استخرج السنة والشهر من تاريخ الحدث، شكل مفتاح نصي، وجمّع عبر Dictionary(grouping:). للعرض، استخدم DateFormatter مع قالب "LLLL yyyy" لاسم شهر مترجم.
| المهمة | طريقة Calendar | دور DateComponents |
|---|---|---|
| اليوم الأول من الشهر | nextDate(after:matching:) | day: 1 |
| حساب العمر | dateComponents(from:to:) | [.year] من الفرق |
| تجميع التواريخ | dateComponents(_:from:) | مفتاح year + month |
| البحث عن يوم الأسبوع | nextDate(after:matching:) | weekday: N |
الأسئلة المتكررة
الأسباب: تاريخ غير موجود (31 أبريل)، حقول متعارضة (weekday=1 مع day=5)، تركيبة غير صالحة للحقول للتقويم المحدد. Calendar يحاول تفسير المكونات في نظامه — إذا كانت التركيبة مستحيلة، تكون النتيجة nil. استخدم دائماً guard let أو if let عند التحويل.
نعم، عبر العامل ==. DateComponents يطبق Equatable، ويقارن جميع الحقول. هيكلان متساويان إذا كانت جميع حقولهما متساوية (nil == nil يعتبر صحيحاً). لمقارنة مجموعة فرعية فقط من الحقول — استخرج نفس المجموعة عبر Calendar.dateComponents.
Date هو لحظة زمنية مطلقة دون ارتباط بالتقويم. DateComponents هو مجموعة أرقام readable (سنة، شهر، يوم) لها معنى فقط في سياق Calendar. Date يمكن مقارنته، طرحه، تسلسله إلى ISO 8601. DateComponents هو تمثيل وسيط للتفاعل مع التقويم.
قم بتعيين حقلي year و month فقط، واترك الباقي كـ nil. عند التحويل إلى Date عبر Calendar.date(from:)، سيقوم Calendar تلقائياً بتعيين اليوم = 1، الساعة = 0، الدقيقة = 0. النتيجة هي Date الموافق لليوم الأول من الشهر المحدد عند منتصف الليل.
DateComponents لا يخزن معلومات المنطقة الزمنية في حقوله — قيم الحقول (السنة، الشهر، اليوم) نفسها تعتمد على timeZone التي تم استخراجها فيها. المكونات "21 يوليو 2026 14:00 MSK" و "21 يوليو 2026 10:00 UTC" تمثلان نفس Date، لكن حقول DateComponents مختلفة.
الخلاصة
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.
اقرأ أيضًا