System.DateTime מייצג תאריך ושעה ביום, משנה 1 עד שנה 9999, בדיוק של 100 ננו-שניות ("tick" אחד). System.TimeSpan מייצג משך זמן: ההפרש בין שני ערכי DateTime. שניהם טיפוסי ערך בלתי ניתנים לשינוי (structs), כך שכל פעולה מחזירה ערך חדש.
פלט:
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
זה החלק ב-API שאנשים טועים בו הכי הרבה. 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 הוא enum מ-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 עם התרבות הבלתי תלויה (invariant) לקבצים, ל-APIs ולמסדי נתונים. 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) לחותמות זמן: מתי בוצעה הזמנה, מתי נשלחה הודעה. מסדי נתונים ו-serializers של 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 פירושו 4 במרץ בארה"ב ו-3 באפריל בבריטניה.
למה AddDays לא משנה את ה-DateTime שלי?
DateTime הוא טיפוס ערך בלתי ניתן לשינוי. AddDays, AddHours ושאר המתודות מחזירות DateTime חדש ומשאירות את המקורי ללא שינוי, אז חייבים להציב את התוצאה: due = due.AddDays(7);.
מתי כדאי להשתמש ב-DateTimeOffset במקום ב-DateTime?
השתמשו ב-DateTimeOffset לחותמות זמן שחייבות לזהות רגע מדויק, כמו מתי בוצעה הזמנה או מתי נכתבה רשומת לוג, במיוחד אם נתונים עוברים בין שרתים ואזורי זמן. הוא מאחסן את ההיסט מ-UTC יחד עם הערך. DateTime מתאים לחותמות זמן שהן UTC בלבד ולתאריכים שאין להם אזור זמן בעל משמעות.