Dates and Times
Three structs can be used for dates and times:
1. DateTime
2. DateTimeOffset
3. TimeSpan
TimeSpan
Represents time of the day, simply the clock time with date missing.
Has a resolution of 100ns, maximum value of about 10 million days and it can be positive or negative.
The static Parse method does the opposite of ToString, converting a string to a TimeSpan.
TryParse does the same but returns false rather than throwing an exception if the conversion fails.
The XmlConvert class also provides TimeSpan string-conversion methods that follow standard XML formatting protocols.
The default value for a TimeSpan is TimeSpan.Zero.
To obtain the current time of day, call DateTime.Now.TimeOfDay.
Three ways to construct a TimeSpan:
1. Through one of the constructors
public TimeSpan (int hours, int minutes, int seconds); public TimeSpan (int days, int hours, int minutes, int seconds); public TimeSpan (int days, int hours, int minutes, int seconds, int milliseconds); public TimeSpan (long ticks); //example (hours, minutes, seconds) Console.WriteLine(new TimeSpan(2,30,0));
2. By calling one of the static From… methods
More convenient when you want to specify an interval in just a single unit, such as minutes, hours.
public static TimeSpan FromDays (double value); public static TimeSpan FromHours (double value); public static TimeSpan FromMinutes (double value); public static TimeSpan FromSeconds (double value); public static TimeSpan FromMilliseconds (double value);
Console.WriteLine(TimeSpan.FromHours(2.5)); Console.WriteLine(TimeSpan.FromHours(-2.5)); Console.WriteLine(TimeSpan.FromHours(2) + TimeSpan.FromMinutes(30)); Console.WriteLine(TimeSpan.FromDays(10) - TimeSpan.FromSeconds(1)); TimeSpan nearlyTenDays = TimeSpan.FromDays(10) - TimeSpan.FromSeconds(1); Console.WriteLine(nearlyTenDays.Days); Console.WriteLine(nearlyTenDays.Hours); Console.WriteLine(nearlyTenDays.Minutes); Console.WriteLine(nearlyTenDays.Seconds); Console.WriteLine(nearlyTenDays.Milliseconds);
Total… properties return values of type double.
Console.WriteLine(nearlyTenDays.TotalDays); Console.WriteLine(nearlyTenDays.TotalHours); Console.WriteLine(nearlyTenDays.TotalMinutes); Console.WriteLine(nearlyTenDays.TotalSeconds); Console.WriteLine(nearlyTenDays.TotalMilliseconds);
3. By subtracting one DateTime from another
Console.WriteLine (new TimeSpan (2, 30, 0)); // 02:30:00 Console.WriteLine (TimeSpan.FromHours (2.5)); // 02:30:00 Console.WriteLine (TimeSpan.FromHours (-2.5)); // -02:30:00 Console.WriteLine (DateTime.MaxValue - DateTime.MinValue); // TimeSpan overloads the < and > operators, as well as the + and - operators: (TimeSpan.FromHours(2) + TimeSpan.FromMinutes(30)).Dump ("2.5 hours"); (TimeSpan.FromDays(10) - TimeSpan.FromSeconds(1)).Dump ("One second short of 10 days");
DateTime and DateTimeOffset
DateTime and DateTimeOffset are immutable structs for representing a date and optionally a time with a resolution of 100ns and converting years range 0001 through 9999.
DateTimeOffset
It stores UTC offset, makes more meaningful when comparing values across different time zones.
July 01 2007 03:00:00 -06:00
Choosing between DateTime and DateTimeOffset
DateTime and DateTimeOffset differ how they handle time zones.
DateTime incorporates three-state flag:
- The local time on the computer
- UTC
- Unspecified
Equality Comparisons
DateTime ignore the three-state flag in comparisons and considers two values equal if they have the same year, month, hour, minute and so on.
DateTimeOffset considers two values equal if they refer to the same point in time.
Example
July 01 2007 09:00:00 +00:00 (GMT) July 01 2007 03:00:00 -06:00 (local time, Central America)
According to DateTime the two values are different whereas DateTimeOffset considers them equal.
For example for international event dates, DateTimeOffset is suitable compared to DateTime where it requires standardizing on a single time zone throughout the application.
Constructing a DateTime
DateTime defines constructors that accept integers for the year, month, day and also optionally the hour, minute, second and millisecond.
public DateTime (int year, int month, int day); public DateTime (int year, int month, int day,int hour, int minute, int second, int millisecond);
DateTime also incorporates DateTimeKind enum: Unspecified, Local, UTC.
Unspecified is default where DateTime is time-zone agnostic.
Local is local computer time zone.
DateTime d1 = new DateTime (2010, 1, 30); // Midnight, January 30 2010 DateTime d2 = new DateTime (2010, 1, 30, 12, 0, 0); // Midday, January 30 2010 DateTime d3 = new DateTime (2010, 1, 30, 12, 0, 0, DateTimeKind.Utc); DateTimeOffset d4 = d1; // Implicit conversion DateTimeOffset d5 = new DateTimeOffset (d1, TimeSpan.FromHours (-8)); // -8 hours UTC
DateTime constructors also overload to accept Calendar object which enables you to specify a date using any of the Calendar subclasses defined in System.Globalization.
DateTime d = new DateTime (5767, 1, 1, new System.Globalization.HebrewCalendar()); Console.WriteLine (d); // 12/12/2006 12:00:00 AM
You can also construct DateTime with a single ticks value of type long, where ticks is the number of 100 ns intervals from midnight 01/01/0001.
Converting
DateTime provides the static FromFileTime and FromFileTimeUtc methods for converting from windows file name and FromOADate for converting from OLE automation date/time.
To construct DateTime from string call the static Parse or ParseExact method.
Constructing a DateTimeOffset
public DateTimeOffset (int year, int month, int day,int hour, int minute, int second, TimeSpan offset); public DateTimeOffset (int year, int month, int day,int hour, int minute, int second, int millisecond,TimeSpan offset);
DateTimeOffset also has constructors that accept a Calendar object, a long ticks value, and static Parse and ParseExact methods that accept a string.
DateTimeOffset can be created from an existing DateTime either by using constructors or implicit cast.
//constructors public DateTimeOffset (DateTime dateTime); public DateTimeOffset (DateTime dateTime, TimeSpan offset); //implict cast DateTimeOffset dt = new DateTime(2000, 2, 3);
DateTimeOffset provides three properties that return values of type DateTime:
1. The UtcDateTime property returns a DateTime in UTC time.
2. The LocalDateTime property returns a DateTime in the current local time zone.
3. The DateTime property returns a DateTime in whatever zone it was specified, with a Kind of Unspecified.
The current DateTime/DateTimeOffset
Now property returns the current date and time.
//Now property Console.WriteLine(DateTime.Now); Console.WriteLine(DateTimeOffset.Now); //Today property Console.WriteLine(DateTime.Today); //Utc now property Console.WriteLine(DateTime.UtcNow); Console.WriteLine(DateTimeOffset.UtcNow);
Working with dates and times
DateTime dt = new DateTime (2000, 2, 3, 10, 20, 30); Console.WriteLine (dt.Year); // 2000 Console.WriteLine (dt.Month); // 2 Console.WriteLine (dt.Day); // 3 Console.WriteLine (dt.DayOfWeek); // Thursday Console.WriteLine (dt.DayOfYear); // 34 Console.WriteLine (dt.Hour); // 10 Console.WriteLine (dt.Minute); // 20 Console.WriteLine (dt.Second); // 30 Console.WriteLine (dt.Millisecond); // 0 Console.WriteLine (dt.Ticks); // 630851700300000000 Console.WriteLine (dt.TimeOfDay); // 10:20:30 (returns a TimeSpan) TimeSpan ts = TimeSpan.FromMinutes (90); Console.WriteLine (dt.Add (ts)); // 3/02/2000 11:50:30 AM Console.WriteLine (dt + ts); // 3/02/2000 11:50:30 AM DateTime thisYear = new DateTime (2007, 1, 1); DateTime nextYear = thisYear.AddYears (1); TimeSpan oneYear = nextYear - thisYear;
Computations
AddYears AddMonths AddDays AddHours AddMinutes AddSeconds AddMilliseconds AddTicks
Formatting and parsing
DateTime.Now.ToString().Dump ("Short date followed by long time"); DateTimeOffset.Now.ToString().Dump ("Short date followed by long time (+ timezone)"); DateTime.Now.ToShortDateString().Dump ("ToShortDateString"); DateTime.Now.ToShortTimeString().Dump ("ToShortTimeString"); DateTime.Now.ToLongDateString().Dump ("ToLongDateString"); DateTime.Now.ToLongTimeString().Dump ("ToLongTimeString"); // Culture-agnostic methods make for reliable formatting & parsing: DateTime dt1 = DateTime.Now; string cannotBeMisparsed = dt1.ToString ("o"); DateTime dt2 = DateTime.Parse (cannotBeMisparsed);
Null DateTime and DateTimeOffset values
Since DateTime and DateTimeOffset are structs they are not nullable.
Use Nullable type (DateTime? or DateTimeOffset?)
Use static field DateTime.MinValue or DateTimeOffset.MinValue
Dates and Time Zones
DateTime and Time Zones
DateTime uses two piece of information: 62-bit number, indicating the number of ticks since since 1/1/0001 and 2-bit enum, indicating the DateTimeKind (Unspecified, Local, or Utc).
// When you compare two DateTime instances, only their ticks values are compared; their DateTimeKinds are ignored: DateTime dt1 = new DateTime (2000, 1, 1, 10, 20, 30, DateTimeKind.Local); DateTime dt2 = new DateTime (2000, 1, 1, 10, 20, 30, DateTimeKind.Utc); Console.WriteLine (dt1 == dt2); // True DateTime local = DateTime.Now; DateTime utc = local.ToUniversalTime(); Console.WriteLine (local == utc); // False // You can construct a DateTime that differs from another only in Kind with the static DateTime.SpecifyKind method: DateTime d = new DateTime (2000, 12, 12); // Unspecified DateTime utc2 = DateTime.SpecifyKind (d, DateTimeKind.Utc); Console.WriteLine (utc2); // 12/12/2000 12:00:00 AM
ToUniversalTime/ToLocalTime – converts to universal/local time. It applies the computer’s current time zone settings and returns a new DateTime with a DateTimeKind or Utc or Local.
No conversion happens if you call ToUniversalTime on a DateTime that’s already Utc or ToLocalTime that’s already Local.
DateTimeOffset and TimeZones
DateTimeOffset comprises a DateTime field whose value is always in UTC and a 16-bit integer field for the UTC offset in minutes.
DateTimeOffset local = DateTimeOffset.Now; DateTimeOffset utc = local.ToUniversalTime(); Console.WriteLine (local.Offset); Console.WriteLine (utc.Offset);
TimeZone and TimeZoneInfo
TimeZone lets you access only the current local time zone whereas TimeZoneInfo provides access to all the world’s time zones.
TimeZone
The static TimeZone.CurrentTimeZone method returns a TimeZone object based on the current local settings.
TimeZone zone = TimeZone.CurrentTimeZone; Console.WriteLine(zone.StandardName); Console.WriteLine(zone.DaylightName);
IsDaylightSavingTime and GetUtcOffset methods
DateTime dt1 = new DateTime (2018, 1, 1); DateTime dt2 = new DateTime (2018, 6, 1); Console.WriteLine (zone.IsDaylightSavingTime(dt1)); Console.WriteLine (zone.IsDaylightSavingTime(dt2)); Console.WriteLine (zone.GetUtcOffset(dt1)); Console.WriteLine (zone.GetUtcOffset(dt2));
The GetDaylightChanges method returns specific daylight saving time information for a given year
DaylightTime day = zone.GetDaylightChanges(2018); Console.WriteLine (day.Start.ToString("M")); Console.WriteLine (day.End.ToString("M")); Console.WriteLine (day.Delta);
TimeZoneInfo
TimeZoneInfo.Local returns the current local time zone.
TimeZoneInfo zone = TimeZoneInfo.Local; Console.WriteLine (zone.StandardName); Console.WriteLine (zone.DaylightName);
Also provides IsDaylightSavingTime and GetUtcOffset methods only difference is that they accept either a DateTime or a DateTimeOffset.
FindSystemTimeZoneById with the zone Id you obtain TimeZoneInfo of the world’s time zones.
TimeZoneInfo wa = TimeZoneInfo.FindSystemTimeZoneById ("W. Australia Standard Time"); Console.WriteLine (wa.Id); // W. Australia Standard Time Console.WriteLine (wa.DisplayName); // (GMT+08:00) Perth Console.WriteLine (wa.BaseUtcOffset); // 08:00:00 Console.WriteLine (wa.SupportsDaylightSavingTime); // True
GetSystemTimeZones method returns all world time zones.
foreach (TimeZoneInfo z in TimeZoneInfo.GetSystemTimeZones()) Console.WriteLine(z.Id);
ConvertTime method converts a DateTime or DateTimeOffset from one time zone to another.
ConvertTimeFromUtc and ConvertTimeToUtc methods can be used to convert from or to UTC.
Daylight Saving Time
TimeZoneInfo provides the following additional methods:
IsValidTime returns true if a DateTime is within the hour that’s skipped when the clocks move forward.
IsAmbiguousTime returns true if a DateTime or DateTimeoffset is within the hour that’s repeated when the clock move back.
GetAmbiguousTimeOffsets returns an array of TimeSpans representing the valid offset choices for an ambiguous DateTime or DateTimeOffset.
GetAdjustmentRules, which returns a declarative summary of all daylight saving rules that apply to all years. Each rule has a DateStart and DateEnd indicating the date range within which the rule is valid.
// Western Australia's daylight saving rules are interesting, having introduced daylight // saving midseason in 2006 (and then subsequently rescinding it): TimeZoneInfo wa = TimeZoneInfo.FindSystemTimeZoneById ("W. Australia Standard Time"); foreach (TimeZoneInfo.AdjustmentRule rule in wa.GetAdjustmentRules()) Console.WriteLine ("Rule: applies from " + rule.DateStart + " to " + rule.DateEnd); foreach (TimeZoneInfo.AdjustmentRule rule in wa.GetAdjustmentRules()) { Console.WriteLine(); Console.WriteLine ("Rule: applies from " + rule.DateStart + " to " + rule.DateEnd); Console.WriteLine (" Delta: " + rule.DaylightDelta); Console.WriteLine (" Start: " + FormatTransitionTime (rule.DaylightTransitionStart, false)); Console.WriteLine (" End: " + FormatTransitionTime (rule.DaylightTransitionEnd, true)); }
AdjustmentRule
Has a DaylightDelta property of type TimeSpan and properties called DaylightTransitionStart and DaylightTransitionEnd.
Properties of TimeZoneInfo.TransitionTime:
public bool IsFixedDateRule { get; } public DayOfWeek DayOfWeek { get; } public int Week { get; } public int Day { get; } public int Month { get; } public DateTime TimeOfDay { get; }
Daylight Saving Time and DateTime
// The IsDaylightSavingTime tells you whether a given local DateTime is subject to daylight saving. Console.WriteLine (DateTime.Now.IsDaylightSavingTime()); // True or False // UTC times always return false: Console.WriteLine (DateTime.UtcNow.IsDaylightSavingTime()); // Always False // The end of daylight saving presents a particular complication for algorithms that use local time. // The comments on the right show the results of running this in a daylight-saving-enabled zone: DaylightTime changes = TimeZone.CurrentTimeZone.GetDaylightChanges (2010); TimeSpan halfDelta = new TimeSpan (changes.Delta.Ticks / 2); DateTime utc1 = changes.End.ToUniversalTime() - halfDelta; DateTime utc2 = utc1 - changes.Delta; // Converting these variables to local times demonstrates why you should use UTC and not local time // if your code relies on time moving forward: DateTime loc1 = utc1.ToLocalTime(); // (Pacific Standard Time) DateTime loc2 = utc2.ToLocalTime(); Console.WriteLine (loc1); // 2/11/2010 1:30:00 AM Console.WriteLine (loc2); // 2/11/2010 1:30:00 AM Console.WriteLine (loc1 == loc2); // True // Despite loc1 and loc2 reporting as equal, they are different inside: Console.WriteLine (loc1.ToString ("o")); // 2010-11-02T02:30:00.0000000-08:00 Console.WriteLine (loc2.ToString ("o")); // 2010-11-02T02:30:00.0000000-07:00 // The extra bit ensures correct round-tripping between local and UTC times: Console.WriteLine (loc1.ToUniversalTime() == utc1); // True Console.WriteLine (loc2.ToUniversalTime() == utc2); // True