Menu
flag Ar iconالعربيةdown icon

DateTime وTimeSpan في C#: Now وUtcNow والإضافة والطرح والمقارنة والقراءة

العمل مع التواريخ والأوقات في C#: إنشاء قيم DateTime، وNow مقابل UtcNow مقابل Today، وإضافة الأيام والأشهر، والطرح للحصول على TimeSpan، وTotalHours مقابل Hours، ومقارنة التواريخ، وDayOfWeek، والقراءة بـ ParseExact وTryParse، وDateTimeOffset، وDateOnly.

تحتوي هذه الصفحة على محررات قابلة للتشغيل - حرّر، شغّل، وشاهد النتيجة فوراً.

يمثّل System.DateTime تاريخًا ووقتًا من اليوم، من السنة 1 إلى السنة 9999، بدقة 100 نانوثانية ("tick" واحدة). ويمثّل System.TimeSpan مدة: الفرق بين قيمتي DateTime. كلاهما نوع قيمة غير قابل للتغيير (بنية)، فكل عملية تعيد قيمة جديدة.

المخرجات:

2026-09-24 00:00:00
2026-09-24 14:30:00
14:30:05.250
2026 9 24
14:30
Thursday
267
2026-09-24 00:00
14:30:00

كل مثال في هذه الصفحة يطبع التواريخ بنص تنسيق صريح. تتبع ToString() الافتراضية الثقافة الحالية (9/24/2026 2:30:00 PM في الولايات المتحدة، و24.09.2026 14:30:00 في ألمانيا)، فتعتمد مخرجاتها على الجهاز. رموز التنسيق في تنسيق DateTime.

التاريخ غير الصالح يرمي استثناءً: new DateTime(2026, 2, 30) ترمي ArgumentOutOfRangeException، وكذلك الشهر 13 أو الساعة 24.

Now وUtcNow وToday

ثلاث خصائص ساكنة تقرأ الساعة:

مخرجات على سبيل المثال:

Now:    2026-09-24 18:20:41 (Local)
UtcNow: 2026-09-24 16:20:41 (Utc)
Today:  2026-09-24 00:00:00

في هذا المثال تسبق المنطقة الزمنية المحلية توقيت UTC بساعتين، فيختلف السطران الأولان بساعتين؛ وعلى جهاز مضبوط على UTC يتطابقان. تسجّل الخاصية Kind هل القيمة Local أو Utc أو Unspecified (الافتراضي للتواريخ التي تنشئها بنفسك). استخدم DateTime.UtcNow لكل ما تخزّنه أو تسجّله أو تقارنه أو ترسله إلى نظام آخر: لا يقفز حين يبدأ التوقيت الصيفي أو ينتهي، ويعني اللحظة نفسها على كل خادم. حوّل إلى الوقت المحلي فقط حين تعرض القيمة لشخص.

لقياس مدة تنفيذ الشيفرة استخدم System.Diagnostics.Stopwatch بدل طرح قيمتي DateTime.Now؛ فدقته أعلى بكثير ولا يتأثر بتعديلات الساعة.

إضافة الوقت وطرحه

تعيد AddDays وAddHours وAddMinutes وAddSeconds وAddMonths وAddYears قيمة DateTime جديدة. مرّر رقمًا سالبًا للرجوع. ولأن DateTime غير قابل للتغيير يجب إسناد النتيجة:

المخرجات:

2026-01-31
2026-02-03 09:00
2026-01-30 21:00
2026-02-28
2027-01-31
10:30
29
True

تقيّد AddMonths القيمة بآخر يوم في الشهر حين لا يوجد اليوم: 31 يناير مع شهر إضافي هو 28 فبراير (أو 29 في السنة الكبيسة)، لا 3 مارس. لذلك قد تعطي إضافة شهر مرتين وإضافة شهرين تاريخين مختلفين.

طرح التواريخ: TimeSpan

طرح DateTime من آخر يعطي TimeSpan:

المخرجات:

3.20:30:00
Days: 3, Hours: 20, Minutes: 30
TotalDays: 3.85
TotalHours: 92.5
TotalMinutes: 5550
Nights: 4

هذا الجزء من الواجهة هو الأكثر عرضة للخطأ. Days وHours وMinutes وSeconds هي مكوّنات المدة (3 أيام، 20 ساعة، 30 دقيقة). وTotalDays وTotalHours وTotalMinutes هي المدة كلها بوحدة واحدة، كقيمة double. "كم ساعة أقام النزيل؟" هي TotalHours (92.5)، لا Hours (20).

يُظهر السطر الأخير نقطة مرتبطة: انقضت 3.85 يوم، لكن النزيل أقام 4 ليالٍ. مقارنة أجزاء .Date تعدّ الأيام التقويمية، وهذا عادة ما تريده الفوترة وعروض "كم يومًا بقي".

إنشاء قيم TimeSpan وتنسيقها

المخرجات:

02:15:00
01:30:00
1.12:00:00
True
03:45:00
True
02:15
36h 0m
00:00:00

يدعم TimeSpan المعاملين + و- والمقارنات وDuration() (القيمة المطلقة) وNegate(). تحتاج التنسيقات المخصصة مثل @"hh\:mm" إلى شرطة مائلة عكسية قبل المحارف الحرفية، وhh هناك تُظهر مكوّن الساعات فقط (من 0 إلى 23)، فللمدد التي تتجاوز يومًا ابنِ النص من TotalHours كما في السطر قبل الأخير.

مقارنة التواريخ

يدعم DateTime المعاملات == و!= و< و> و<= و>=، إضافة إلى CompareTo وDateTime.Compare. لمقارنة التاريخ فقط وتجاهل الوقت قارن خصائص .Date:

المخرجات:

True
True
1
True
2026-09-01

لسؤال "هل هذا الطابع الزمني ضمن 30 سبتمبر؟" قارن ببداية اليوم التالي بـ <، كما في المثال. كتابة check <= end ستستبعد كل ما بعد منتصف الليل في اليوم الأخير، لأن end هي 2026-09-30 00:00:00.

تنظر المقارنات إلى النبضات (ticks) فقط لا إلى Kind: قيمة Local وقيمة Utc تُطبعان متطابقتين تُعتبران متساويتين مع أنهما لحظتان مختلفتان. سبب آخر لإبقاء الأوقات المخزّنة بتوقيت UTC.

يوم الأسبوع وبداية الأسبوع

DayOfWeek تعداد من Sunday (0) إلى Saturday (6). الحساب عليه يجد أيام العمل وحدود الأسبوع:

المخرجات:

Thursday
4
Weekend: False
Week starts 2026-09-21 (Monday)
Next Friday: 2026-09-25
2026-09-01 to 2026-09-30

أسماء الأيام التي تطبعها DayOfWeek.ToString() إنجليزية دائمًا. لاسم مترجم نسّق التاريخ بـ "dddd" وثقافة.

قراءة التواريخ من النصوص

حين تعرف صيغة المدخلات استخدم ParseExact أو TryParseExact مع CultureInfo.InvariantCulture. يستخدم نص الصيغة الرموز نفسها التي يستخدمها التنسيق:

المخرجات:

2026-09-24 00:00
2026-09-24 18:05
'2026-02-28' -> Saturday, February 28
'2026-02-30' -> invalid
'28.02.2026' -> invalid
'' -> invalid
2026-02-28
2026-09-24 10:00 Utc

تحاول DateTime.Parse(text) دون صيغة التخمين بالثقافة الحالية. "03/04/2026" هي 4 مارس على جهاز أمريكي و3 أبريل على جهاز بريطاني، والتاريخ الذي يُقرأ على حاسوبك المحمول قد يرمي FormatException على خادم. أبقِ Parse لمدخلات يكتبها مستخدم محلي؛ واستخدم ParseExact مع الثقافة الثابتة للملفات وواجهات API وقواعد البيانات. ترمي ParseExact استثناء FormatException حين لا يطابق النص؛ وتعيد TryParseExact القيمة false بدلًا من ذلك.

حساب العمر

طرح تواريخ الميلاد والقسمة على 365 خطأ حول أعياد الميلاد والسنوات الكبيسة. قارن السنوات، ثم صحّح إن لم يأتِ عيد الميلاد هذا العام بعد:

المخرجات:

36
35
18
70 days to go

DateTimeOffset

لا يسجّل DateTime المنطقة الزمنية التي هو فيها سوى بعلامة Kind الغامضة. أما DateTimeOffset فيخزّن القيمة مع إزاحتها عن UTC، فيحدد دائمًا لحظة واحدة دقيقة:

المخرجات:

2026-09-24 14:00 +02:00
2026-09-24 12:00
2026-09-24 12:30
00:30:00
21:00 +09:00

استخدم DateTimeOffset (أو قيم DateTime بتوقيت UTC) للطوابع الزمنية: متى قُدّم طلب، ومتى أُرسلت رسالة. تتعامل معه قواعد البيانات وأدوات تسلسل JSON جيدًا. وللتحويل بين مناطق زمنية مسمّاة بقواعد التوقيت الصيفي استخدم TimeZoneInfo.ConvertTime؛ تختلف معرّفات المناطق بحسب نظام التشغيل في إصدارات .NET الأقدم ("Europe/Paris" في Linux و"Romance Standard Time" في Windows)، وتقبل .NET 6 وما بعدها الاثنين.

DateOnly وTimeOnly (.NET 6 وما بعده)

قيم كثيرة تاريخ بلا وقت (عيد ميلاد، تاريخ استحقاق) أو وقت بلا تاريخ (ساعات العمل). أضافت .NET 6 نوعين لها:

// .NET 6 and later
DateOnly birthday = new DateOnly(1990, 9, 24);
DateOnly due = DateOnly.FromDateTime(DateTime.Today).AddDays(14);
int daysLeft = due.DayNumber - DateOnly.FromDateTime(DateTime.Today).DayNumber;

TimeOnly opens = new TimeOnly(9, 0);
TimeOnly closes = new TimeOnly(17, 30);
bool isOpen = TimeOnly.FromDateTime(DateTime.Now).IsBetween(opens, closes);

يزيلان فئة من الأخطاء يزيح فيها وقت شارد أو منطقة زمنية التاريخ بيوم. الشيفرة الأقدم، والشيفرة التي تستهدف .NET Framework أو Unity، تستخدم DateTime مع ترك الوقت عند منتصف الليل.

أخطاء شائعة

  • تجاهل نتيجة AddDays. DateTime غير قابل للتغيير؛ أسند القيمة المعادة.
  • استخدام Hours بدل TotalHours. المكوّنات مقابل المدة الكلية.
  • تخزين DateTime.Now. خزّن UTC وحوّل للعرض.
  • استدعاء ToString() دون تنسيق في السجلات أو الملفات أو الاختبارات، حيث تعتمد المخرجات على ثقافة الجهاز.
  • القراءة بـ DateTime.Parse على بيانات الآلة. استخدم ParseExact والثقافة الثابتة.
  • الخلط بين mm وMM في نصوص التنسيق (الدقائق والأشهر). راجع تنسيق DateTime.

الأسئلة الشائعة

ما الفرق بين DateTime.Now وDateTime.UtcNow؟

DateTime.Now هو الوقت الحالي في المنطقة الزمنية المحلية للحاسوب، مع ضبط Kind على Local. وDateTime.UtcNow هو الوقت الحالي بتوقيت UTC، مع ضبط Kind على Utc، وهو أسرع أيضًا لأنه يتخطى تحويل المنطقة الزمنية. خزّن الطوابع الزمنية وقارنها بتوقيت UTC، وحوّل إلى الوقت المحلي فقط للعرض.

كيف أحصل على الفرق بين تاريخين في C#؟

اطرحهما: TimeSpan gap = end - start;. ثم اقرأ gap.TotalDays أو gap.TotalHours أو gap.TotalMinutes للمدة كاملة كقيمة double، أو gap.Days لجزء الأيام الكاملة. لا توجد خاصية مدمجة للأشهر أو السنوات التقويمية، لأن الأشهر مختلفة الأطوال؛ قارن حقلي السنة والشهر بنفسك.

ما الفرق بين TimeSpan.Hours وTotalHours؟

Hours هي مكوّن الساعات فقط، من 0 إلى 23، بعد إخراج الأيام الكاملة. أما TotalHours فهي المدة كلها معبّرًا عنها بالساعات، كقيمة double. لمدة يوم و3 ساعات تكون Hours مساوية لـ 3 وTotalHours مساوية لـ 27. استخدام Hours حيث المقصود TotalHours خطأ شائع جدًا.

كيف أقرأ نص تاريخ في C#؟

حين تعرف الصيغة استخدم DateTime.ParseExact(text, "yyyy-MM-dd", CultureInfo.InvariantCulture)، أو DateTime.TryParseExact لتحصل على false بدل FormatException مع المدخلات السيئة. تخمّن DateTime.Parse الصيغة من الثقافة الحالية، فتعني 03/04/2026 الرابع من مارس في الولايات المتحدة والثالث من أبريل في المملكة المتحدة.

لماذا لا تغيّر AddDays قيمة DateTime عندي؟

DateTime نوع قيمة غير قابل للتغيير. تعيد AddDays وAddHours والدوال الأخرى قيمة DateTime جديدة وتترك الأصل دون تغيير، فيجب أن تسند النتيجة: due = due.AddDays(7);.

متى أستخدم DateTimeOffset بدل DateTime؟

استخدم DateTimeOffset للطوابع الزمنية التي يجب أن تحدد لحظة دقيقة، مثل وقت تقديم طلب أو كتابة مدخل في سجل، خاصة إن كانت البيانات تنتقل بين خوادم ومناطق زمنية. يخزّن الإزاحة عن UTC مع القيمة. أما DateTime فمناسب للطوابع الزمنية بتوقيت UTC فقط وللتواريخ التي لا منطقة زمنية ذات معنى لها.

Coddy programming languages illustration

تعلّم البرمجة مع Coddy

ابدأ الآن