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:

  1. The local time on the computer
  2. UTC
  3. 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

Leave a Reply

Your email address will not be published. Required fields are marked *