التاريخ والوقت في تطوير تطبيقات الجوال — أحد الموضوعات الرئيسية في هندسة تطبيقات الجوال. يستخدم iOS DateFormatter وISO8601DateFormatter، ويستخدم Android DateTimeFormatter وLocalDate من java.time. وفقًا لـ Android Developer Docs، فإن الاختيار الصحيح لواجهة برمجة التطبيقات (API) للجوال لتنسيق التاريخ والوقت يؤثر بشكل مباشر على الأداء وصحة العرض في مناطق زمنية مختلفة.
النقاط الرئيسية
على iOS، يتم تنسيق التاريخ والوقت تقليديًا عبر DateFormatter من Foundation. يقوم هذا الفصل بتحويل Date إلى سلسلة والعكس باستخدام نمط محدد وإعدادات إقليمية ومنطقة زمنية. المشكلة الرئيسية في DateFormatter — أنه غير آمن للخيوط ويتم إنشاؤه ببطء، لذلك يجب تخزين النسخ في ذاكرة التخزين المؤقت في كود الجوال. يتطلب التاريخ في تطوير تطبيقات الجوال على iOS المنسق الصحيح.
يستغرق إنشاء DateFormatter حوالي 1 مللي ثانية بسبب تحليل نمط dateFormat وتحميل الإعدادات الإقليمية وتحديد المنطقة الزمنية. عند تنسيق قائمة من مئات التواريخ، يؤدي الإنشاء المتكرر إلى تأخير ملحوظ في واجهة المستخدم. الحل — قم بإنشاء المنسق مرة واحدة للشاشة بأكملها وأعد استخدامه. للوصول متعدد الخيوط، استخدم المزامنة عبر DispatchQueue.
import Foundation
// DateFormatter مخزن مؤقتًا — يتم إنشاؤه مرة واحدة
private let dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "dd.MM.yyyy HH:mm"
formatter.locale = Locale(identifier: "ru_RU")
formatter.timeZone = TimeZone(identifier: "Europe/Moscow")
return formatter
}()
// ISO8601DateFormatter — بديل خفيف الوزن
private let isoFormatter: ISO8601DateFormatter = {
let formatter = ISO8601DateFormatter()
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
return formatter
}()
let now = Date()
let formattedDate = dateFormatter.string(from: now)
// "01.07.2026 14:30"
let isoDate = isoFormatter.string(from: now)
// "2026-07-01T14:30:00.000Z"
لتنسيق ISO 8601، استخدم ISO8601DateFormatter — لا يتطلب تحديد نمط ويعمل بشكل أسرع من DateFormatter. على iOS 10+، هذا الفصل مفضل للعمل مع تواريخ الخادم. للتنسيقات القديمة، احتفظ بـ DateFormatter ولكن قم بتخزين النسخة في ذاكرة التخزين المؤقت كخاصية ثابتة أو singleton.
على Android، تتم معالجة التاريخ والوقت عبر حزمة java.time، المتاحة من API 26. الفصول الرئيسية: LocalDate (التاريخ فقط)، LocalTime (الوقت فقط)، LocalDateTime (التاريخ والوقت بدون منطقة زمنية) وZonedDateTime (مع منطقة زمنية). DateTimeFormatter — منسق آمن للخيوط لمنصة Android للجوال. يتم تنسيق التاريخ في تطبيقات الجوال عبر DateTimeFormatter وLocalDate دون فقدان الأداء.
LocalDate يخزن التاريخ بدون وقت — مثالي لأعياد الميلاد. يخزن ZonedDateTime معلومات كاملة مع المنطقة الزمنية. يدعم DateTimeFormatter الأنماط المحددة مسبقًا (ISO_LOCAL_DATE، ISO_DATE_TIME) والأنماط المخصصة عبر ofPattern. جميع فئات java.time غير قابلة للتغيير وآمنة للخيوط، مما يلغي حالات السباق عند العمل في خيوط متعددة.
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
import java.time.ZoneId
// إنشاء وتنسيق التاريخ الحالي
val today = LocalDate.now()
val formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy")
val formatted = today.format(formatter)
// العمل مع المناطق الزمنية
val utcTime = ZonedDateTime.now(ZoneId.of("UTC"))
val moscowTime = utcTime.withZoneSameInstant(ZoneId.of("Europe/Moscow"))
DateTimeFormatter أكثر كفاءة بشكل ملحوظ من SimpleDateFormat لأنه لا يحلل النمط في كل استدعاء. يوصى بإنشاء ثوابت المنسق على مستوى الفصل أو الكائن المرافق. لتحويل Unix Timestamp، استخدم Instant.ofEpochSecond، ثم حول إلى LocalDateTime أو ZonedDateTime عبر المنطقة الزمنية.
تعتمد التاريخ والوقت الصحيحين على شاشة المستخدم على المعالجة الصحيحة للمناطق الزمنية. يتطلب التاريخ والوقت في تطوير تطبيقات الجوال لنظام Android مراعاة المنطقة الزمنية للجهاز. القاعدة الذهبية: قم بتخزين الوقت بصيغة UTC، وقم بالتحويل إلى المنطقة الزمنية المحلية فقط للعرض. يوفر iOS TimeZone من Foundation، ويوفر Android ZoneId من java.time. تدعم كلتا واجهات برمجة التطبيقات (API) لمنصات الجوال الكشف التلقائي عن المنطقة الزمنية الحالية للجهاز. يعتمد التاريخ في تطبيق الجوال على ZoneId وTimeZone.
يخلق التوقيت الصيفي غموضًا: يمكن تمثيل لحظة زمنية واحدة بساعتين محليتين مختلفتين. تتعامل java.time وFoundation مع DST تلقائيًا. استخدم معرفات IANA (Europe/Moscow، America/New_York)، وليس الاختصارات (MSK، EST) — الاختصارات غامضة وتتجاهل الانتقال إلى التوقيت الصيفي.
import Foundation
// تحويل UTC إلى الوقت المحلي على iOS
func formatToLocalTime(utcDate: Date, timeZoneId: String) -> String {
let formatter = DateFormatter()
formatter.dateFormat = "dd.MM.yyyy HH:mm"
formatter.timeZone = TimeZone(identifier: timeZoneId)
return formatter.string(from: utcDate)
}
// تحديد المنطقة الزمنية للجهاز
let localTimeZone = TimeZone.current
let isDST = localTimeZone.isDaylightSavingTime()
let secondsFromGMT = localTimeZone.secondsFromGMT()
على Android، استخدم ZoneId.systemDefault() للحصول على المنطقة الزمنية للجهاز. عند تحويل UTC إلى الوقت المحلي، قم بإنشاء ZonedDateTime مع ZoneOffset.UTC وقم بتطبيق withZoneSameInstant. لواجهات برمجة التطبيقات (API) القديمة بدون java.time، استخدم TimeZone.getDefault() وCalendar — ولكن من الأفضل إضافة ThreeTenABP مباشرة.
import java.time.Instant
import java.time.ZonedDateTime
import java.time.ZoneId
// تحويل Unix Timestamp إلى الوقت المحلي
fun toLocalDisplay(unixSeconds: Long): String {
val instant = Instant.ofEpochSecond(unixSeconds)
val local = instant.atZone(ZoneId.systemDefault())
return local.format(DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm"))
}
ThreeTenABP — مكتبة محول تنقل java.time إلى إصدارات Android API الأقل من 26. توفر نفس الفئات: LocalDate، LocalTime، ZonedDateTime، DateTimeFormatter، Instant. إذا كان التطبيق يدعم Android 5 أو 6 (API 21-25)، فإن ThreeTenABP هو الطريقة الوحيدة لاستخدام واجهة برمجة تطبيقات (API) حديثة للجوال دون الانتقال إلى Calendar القديم. يتم تنفيذ التاريخ والوقت في تطوير تطبيقات الجوال للأجهزة القديمة عبر هذه المكتبة. للأجهزة القديمة، يتم تنفيذ التاريخ والوقت عبر ThreeTenABP.
تتطلب المكتبة التهيئة في طريقة Application.onCreate() عن طريق استدعاء AndroidThreeTen.init(). يؤدي ذلك إلى تحميل بيانات المناطق الزمنية من assets. بعد التهيئة، تكون واجهة برمجة التطبيقات (API) مطابقة تمامًا لـ java.time — سيكون الكود لـ API 26+ وAPI 21+ هو نفسه. يبلغ حجم المكتبة حوالي 400 كيلوبايت، وهو مقبول لمعظم تطبيقات الجوال.
import org.threeten.bp.LocalDate
import org.threeten.bp.format.DateTimeFormatter
// التهيئة في Application
class App : Application() {
override fun onCreate() {
super.onCreate()
AndroidThreeTen.init(this)
}
}
// واجهة برمجة التطبيقات مطابقة لـ java.time
val today = LocalDate.now()
val formatted = today.format(DateTimeFormatter.ISO_LOCAL_DATE)
أداء ThreeTenABP مشابه لـ java.time الأصلي على الأجهزة الحديثة. على الأجهزة القديمة، قد يكون هناك تأخير في الاستدعاء الأول بسبب تحميل بيانات المناطق الزمنية. قم بترحيل الكود الحالي من Calendar إلى ThreeTenABP تدريجيًا: استبدل Date وCalendar بـ Instant وLocalDate في الوحدات الجديدة، مع ترك الكود القديم دون تغيير.
Unix Timestamp — عدد الثواني المنقضية منذ 1 يناير 1970 00:00 UTC. هذا تنسيق عالمي لتخزين وتبادل الوقت بين الخادم والعميل. ومع ذلك، قد يختلف الوقت على الجهاز عن الوقت الفعلي بسبب إعدادات المستخدم. للمزامنة، يُستخدم NTP — بروتوكول الوقت الدقيق عبر UDP. Unix Timestamp هو التنسيق الذي يتم به تخزين التاريخ والوقت على الخادم.
يقوم Date().timeIntervalSince1970 على iOS بإرجاع الثواني منذ 1970. على Android، يقوم System.currentTimeMillis() بإرجاع أجزاء الثانية. للدقة، استخدم NTP: ترسل مكتبة TrueTime لنظام iOS وAndroidNtp لنظام Android طلبًا إلى خادم NTP وتحسب إزاحة الوقت. دقة NTP هي 1-10 مللي ثانية على الشبكة المحلية و10-100 مللي ثانية عبر الإنترنت.
import Foundation
// الحصول على Unix Timestamp على iOS
let seconds = Date().timeIntervalSince1970
let milliseconds = Int64(seconds * 1000)
// مزامنة NTP مع TrueTime
TrueTime.shared.start { result in
switch result {
case .success:
let ntpTime = TrueTime.shared.now()
print("الوقت الدقيق: \(ntpTime)")
case .failure(let error):
print("خطأ NTP: \(error)")
}
}
بدون مزامنة في تطبيق الجوال، من الممكن حدوث أخطاء: علامات زمنية غير صحيحة للأحداث، وعدم التزامن مع الخادم. تتم مزامنة الوقت في تطوير تطبيقات الجوال عبر NTP مرة واحدة عند بدء تشغيل التطبيق، وبعد ذلك يتم استخدام الإزاحة المحسوبة. لتطبيقات الجوال الحرجة من حيث الدقة (المالية، اللوجستية)، يكون بروتوكول NTP إلزاميًا قبل إرسال البيانات إلى الخادم.
الأسئلة الشائعة
يقوم DateFormatter بتحليل نمط التنسيق وتحميل الإعدادات الإقليمية وتحديد المنطقة الزمنية في كل مرة يتم فيها إنشاء نسخة. قم بتخزين المنسق في ذاكرة التخزين المؤقت كثابت ثابت أو استخدم ISO8601DateFormatter لـ ISO 8601 — فهو أخف وأسرع.
DateTimeFormatter من حزمة java.time — آمن للخيوط وعالي الأداء. متاح من API 26. للإصدارات القديمة، استخدم ThreeTenABP الذي يوفر واجهة برمجة تطبيقات (API) مماثلة.
أضف ThreeTenABP — إصدار backport من java.time لنظام Android API 21+. التهيئة عبر AndroidThreeTen.init() في Application.onCreate(). واجهة برمجة التطبيقات (API) مطابقة تمامًا لـ java.time الأصلي.
نعم، قم بتخزين الوقت بصيغة UTC على الخادم وفي قاعدة البيانات المحلية. قم بالتحويل إلى المنطقة الزمنية المحلية للمستخدم فقط في مرحلة العرض. هذا يلغي الأخطاء عند تغيير المنطقة الزمنية أو السفر.
يقوم Date().timeIntervalSince1970 بإرجاع عدد الثواني منذ 1 يناير 1970. لأجزاء الثانية، اضرب في 1000 وحول إلى Int64. للمزامنة الدقيقة، استخدم مكتبة TrueTime مع خادم NTP.
الملخص
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.