James Michael Hare

...hare-brained ideas from the realm of software development...
posts - 136 , comments - 1093 , trackbacks - 0

My Links

News

Welcome to my blog! I'm a Sr. Software Development Engineer in Seattle, WA. I've been doing C++/C#/Java development for over 18 years, but have definitely learned that there is always more to learn!

All thoughts and opinions expressed in my blog and my comments are my own and do not represent the thoughts of my employer.

Blogs I Read

MCC Logo MVP Logo

Follow BlkRabbitCoder on Twitter

Tag Cloud

Archives

Post Categories

C#/.NET Little Wonders: DateTime is Packed with Goodies

Once again lets dive into the Little Wonders of .NET, those small things in the .NET languages and BCL classes that make development easier by increasing readability, maintainability, or performance.

Today I’m going to focus a bit on the System.DateTime.  This nice little value type (struct) has been in the BCL since the beginning, and while being broadly used to represent an instance of a date and time, there are many great properties, methods, and operators in this class that perhaps folks are less familiar with but can give you a lot of power.

A quick note on default construction

One of the first things people notice when moving from Java to C# is that the default constructor for DateTime does not give the current time, but instead gives the minimum time.

   1: // what is the default date and time?
   2: var someTime = new DateTime();
   3:  
   4: // The date and time are: 1/1/0001 12:00:00 AM
   5: Console.WriteLine("The date and time are: " + someTime);

 

Why is this?  Why not the current date and time?  The key here is to realize that DateTime is a struct.  The choice of struct was chosen for performance reasons to make the DateTime instances very light, but one of the many consequences of a struct is that you can’t specify behavior in the default constructor.  The default constructor of a struct always initializes all fields to their default (zero for numeric types, null for reference types, etc).

For DateTime this means that its one instance field (dateData) is initialized to zero.  So what is dateData?  Well, it’s a long whose top two bits specify the kind of DateTime (Unspecfied, Utc, Local) and the remainder of the bits (62) specify the number of ticks from January 1st, 0001 (AD/CE) at midnight.

So, since by default all fields in a struct are their default values, this means that constructing a DateTime using the default constructor gives you an instance where dateData is zero.  This means that the number of ticks (the bottom 62 bits) is zero and the kind (the top 2 bits) is also zero.  This means an offset of zero ticks since the minimum DateTime and an Unspecified kind.

Now is now, but where?

So, if you do want to get the current local date and time, the easiest way is to call the static property DateTime.Now

   1: // currentTime will have the current local Date and Time
   2: var currentTime = DateTime.Now;

This is very useful for grabbing the local date and time, but if we are operating across multiple time zones, we may want to use UTC time instead to give us location independence.  In that case, you should instead call:

   1: // currentTime will have the current UTC Date and Time
   2: var someTime = DateTime.UtcNow;

The DateTime.UtcNow gets the current date and time, but instead of using the local time zone, it creates it as UTC time instead.  Which one you use is important, because in either case, it will set the upper two bits in dateData to represent the kind of DateTime.  We can query these upper two bits easily by using the Kind property:

   1: // Output: Local
   2: Console.WriteLine(DateTime.Now.Kind);
   3:  
   4: // Output: Utc
   5: Console.WriteLien(DateTime.UtcNow.Kind);

Constructing with DateTimeKind

So, using Now versus UtcNow will set the kind bits of dateData appropriately, but what if you want to hand-construct a date and time (say to represent certain holidays or key date/time events)?  Well, many of the constructors are overloaded in such a way to provide an optional DateTimeKind parameter:

   1: // specifies 11/18/2010 5:30 PM - Unspecified kind
   2: var someTime = new DateTime(2010, 11, 18, 17, 30, 0);
   3:  
   4: // specifies 11/18/2010 5:30 PM - Local Time
   5: var localTime = new DateTime(2010, 11, 18, 17, 30, 0, DateTimeKind.Local);
   6:  
   7: // specifies 11/18/2010 5:30 PM - Coordinated Universal Time (UTC)
   8: var utcTime = new DateTime(2010, 11, 18, 17, 30, 0, DateTimeKind.Utc);

Note, however, that the constructor that takes only a year, month, and day does not have a DateTimeKind parameter.  So, if you want to express a particular date-only instance of DateTime in terms of UTC or Local, it is better to be explicit:

   1: // currentTime will have the given date at midnight but unspecified kind
   2: var unspecifiedTime = new DateTime(2010, 11, 18);
   3:  
   4: // currentTime will have the given date at midnight local time
   5: var localTime = new DateTime(2010, 11, 18, 0, 0, 0, DateTimeKind.Local);
   6:  
   7: // currentTime will have the given date at midnight utc time
   8: var utcTime = new DateTime(2010, 11, 18, 0, 0, 0, DateTimeKind.Utc);

When you want date or time, but not both

Many times when we’re developing we really only care about the date component or time component.  There are some helpful properties that can help you easily get to these.

There’s no need to take a date time and hand-construct a new DateTime instance:

   1: var now = DateTime.Now;
   2:  
   3: // don't need to do this, use Today static property
   4: var today = new DateTime(now.Year, now.Month, now.Day, 0, 0, 0, DateTimeKind.Local);
   5:  
   6: // 11/18/2010 5:30:00 PM
   7: var someTime = new DateTime(2010, 11, 18, 17, 30, 0, DateTimeKind.Local);
   8:  
   9: // don't need to do this, use Date instance property
  10: var justDate = new DateTime(someTime.Year, someTime.Month, someTime.Day, 0, 0, 0, DateTimeKind.Local);
  11:  
  12: // don't need to do this, use TimeOfDay instance property
  13: var justTime = new TimeSpan(someTime.Hour, someTime.Minute, someTime.Second);

First, there is the static property Today that gives you a DateTime for the current day at midnight local time.  Or, if you have an instance of DateTime and only want the date or time component, you can call the Date and TimeOfDay properties respectively. 

The Date property returns a new instance containing the original instance date at midnight (zero hours, minutes, and seconds), and the TimeOfDay property returns a TimeSpan containing the original instance’s time (or, you can think of it as an offset since midnight of the original instance).

   1: // gets the current day based on local time (midnight).
   2: var today = DateTime.Today;
   3:  
   4: // 11/18/2010 at 5:30 PM local time
   5: var someTime = new DateTime(2010, 11, 18, 17, 30, 0, DateTimeKind.Local);
   6:  
   7: // 11/18/2010 12:00:00 AM
   8: var justDate = someTime.Date;
   9:  
  10: // 17:30:00
  11: var justTime = someTime.TimeOfDay;

Operations on DateTime

Most of us already know you can compare instance of DateTime with the traditional comparison operators (<, <=, >, >=, ==, !=) or with the CompareTo() method.  But you can also do addition and subtraction on DateTime instances.

Now, keep in mind that DateTime is immutable (can’t change) which means that every time you operate on a DateTime it creates a new instance.  But since it is a one-field struct the cost involved is extremely small and this shouldn’t be a factor.

Obviously, the concept of adding two DateTime instances together is meaningless.  How do you add 11/19/2010 07:30:00 and 12/31/2010 03:50:00?  The concept just doesn’t make sense, but you can add a TimeSpan to a date and time.  For instance, 11/19/2010 07:30:00 plus 2:00:00 would yield 11/19/2010 09:30:00.

Addition of TimeSpan instances to DateTime are supported with the Add() method and it’s counterpart the operator +.  The TimeSpan instances can be positive or negative as desired (adding a negative TimeSpan is the same as subtracting the positive TimeSpan and vice versa).

If you don’t need the full TimeSpan and just want to add (or subtract) individual components, you can use the Add…() convenience methods for days, hours, years, etc.

   1: // get today at midnight
   2: var today = DateTime.Today;
   3:  
   4: // 5 hours, 30 minutes, 10 seconds
   5: var interval = new TimeSpan(5, 30, 10);
   6:  
   7: // newTime is today at 5:30:10 am
   8: var newTime = today + interval;
   9:  
  10: // tomorrowNewTime is tomorrow at 5:30:10 am
  11: var tomorrowNewTime = newTime.AddDays(1);
  12:  
  13: // yesterdayNewTime is yesterday at 5:30:10 am
  14: var yesterdayNewTime = newTime.AddDays(-1);
  15:  
  16: // seeYouNextDecard is 10 years from today at midnight
  17: var seeYouNextDecade = today.AddYears(10);
  18:  
  19: Console.WriteLine(tomorrowNewTime);
  20: Console.WriteLine(yesterdayNewTime);
  21: Console.WriteLine(seeYouNextDecade);

 

Now, unlike Add(), you can meaningfully Subtract() two DateTime instances and get the interval in between the two.  So 11/19/2010 09:30:00 – 11/19/2010 07:30:00 would be a TimeSpan instance representing two hours.  You can, of course, also subtract a TimeSpan from a DateTime.

Notice, however, the Add…() component adds do not have Subtract…() counterparts.  In this case if you need to subtract days, hours, months, etc just add the negative value.

   1: // New year's day
   2: var newYears2010 = new DateTime(2010, 1, 1, 0, 0, 0, DateTimeKind.Local);
   3:  
   4: // thanksgiving day
   5: var thanksgiving2010 = new DateTime(2010, 11, 25, 0, 0, 0, DateTimeKind.Local);
   6:  
   7: // could also say thanksgiving2010.Subtract(newYears2010)
   8: var timeFromNewYearsToThanksgiving = thanksgiving2010 - newYears2010;
   9:  
  10: // 328 days 
  11: Console.WriteLine(timeFromNewYearsToThanksgiving);

Summary

The DateTime struct has a lot of goodness in it.  Even though it is very lightweight and performant, it is also extremely powerful in the range of operations that it supports.

 

 Technorati Tags: ,,,,,

 

Print | posted on Thursday, November 18, 2010 6:45 PM | Filed Under [ My Blog C# Software .NET Fundamentals Little Wonders ]

Feedback

Gravatar

# re: C#/.NET Little Wonders: DateTime is Packed with Goodies

what... no mention of DateTimeOffset? ;) it accurately portrays a DateTime with an offset from UTC and isn't time zone aware so no worrying about what "kind" it is

http://msdn.microsoft.com/en-us/library/system.datetimeoffset.aspx
11/19/2010 12:12 AM | Joey
Gravatar

# re: C#/.NET Little Wonders: DateTime is Packed with Goodies

It's very usefull, thanks!
11/19/2010 1:36 AM | JuniorNik
Gravatar

# re: C#/.NET Little Wonders: DateTime is Packed with Goodies

Nice article about several features of DateTime, thanks for sharing.
11/19/2010 2:58 AM | Marius Schulz
Gravatar

# re: C#/.NET Little Wonders: DateTime is Packed with Goodies

@Joey:

Yes, I was thinking about including that (and also noting that Stopwatch is better for getting accurate durations instead of subtracting DateTime instances), but I was trying to keep it short and focused on DateTime. Excellent point though! That may have to be one of my next posts...

@JuniorNik, @Marius: Thanks!
11/19/2010 9:11 AM | James Michael Hare
Gravatar

# re: C#/.NET Little Wonders: DateTime is Packed with Goodies

Indeed usefull, thanks for time and effort!!!
11/25/2010 2:29 AM | mahesh
Gravatar

# re: C#/.NET Little Wonders: DateTime is Packed with Goodies

The recommendation from Microsoft is that "DateTimeOffset should be considered the default date and time type for application development."

See http://msdn.microsoft.com/en-us/library/bb384267.aspx for details.

Do not use the original DateTime unless you have to (e.g. if you are using .NET 1.x). If you do, make sure you always use DateTimeKind.Utc when you are saving (e.g. in a database) or otherwise sharing times.
1/8/2011 2:11 AM | Sly Gryphon
Gravatar

# re: C#/.NET Little Wonders: DateTime is Packed with Goodies

@Sly:

Very true, I was just pointing out the littler known features of DateTime.
1/13/2011 6:30 PM | James Michael Hare
Gravatar

# re: C#/.NET Little Wonders: DateTime is Packed with Goodies

In the second to last code block there is a typo
6: // seeYouNextDecard is 10 years from today at midnight
17: var seeYouNextDecade = today.AddYears(10);

in the comment it says seeYouNextDecard. Thanks for the great posts, keep up the good work!
1/7/2014 5:47 PM | Joe
Post A Comment
Title:
Name:
Email:
Comment:
Verification:
 
 

Powered by: