Working with Dates, Times, and Time Zones
We've worked with numbers and now text but we haven't worked with dates yet. This is a really important set of data types for folks that are working in accounting, or finance and other industries that need to be able to track when various business events occur. Let's talk about all the different types of date data types and how to work with them in C#
Download this lesson as a Polyglot Notebook to open in Visual Studio Code, or open directly in your web browser with Binder.
In the beginning... there was DateTime
The original date data type that was available for net is the DateTime
data type. This data type allows you to store both a date and a time and interact with those values in the date and time. There's a minimum value and a maximum value property that you can work with and all kinds of formatting capabilities of a date time variable. We create a new DateTime
variable by using the new
keyword and specifying the year month, day, year, and optionally the time information including hour (in 24 hour format), minute, and second.
Let's take a look at some samples where we're going to schedule our next game night.
var gameNight = new DateTime(2024, 5, 1, 19, 0, 0); display(gameNight); display(gameNight.Hour); display(gameNight.Minute); display(gameNight.Second); var dateOfNextGame = new DateTime(2024, 5, 8); display(dateOfNextGame); var firstDate = DateTime.MinValue; display(firstDate); var lastDate = DateTime.MaxValue; display(lastDate);
Notice that the display of the date is returned in a standard format of year-2 digit month-2 digit day and 24 hour-based time with a Z at the end. Additionally the dateOfNextGame
is returned with a time of midnight on the 8th of May because no time was specified. The firstDate
value points to midnight on January 1st of the year 1 on the Gregorian Calendar that most of the world uses. There are other calendars available for use in C#, and you can specify which calendar to use as an optional argument when creating a DateTime
This type of structure is pretty good when working with dates and times in a single time zone or you need to do some date and time arithmetic but don't need to be concerned about things like daylight savings time.
Many people don't carry around date and time values in this very discrete format that we created a DateTime
value with, but rather something more humanly readable like May 1, 2024 7:00pm
In C#, we can Parse
those values to create a DateTime
that we can then work with.
var myTime = DateTime.Parse("May 1, 2024 7:00 PM"); display(myTime);
C# will do its best to parse the value passed in, and you can also specify the exact format of the string value using the ParseExact
method as well:
var exactTime = DateTime.ParseExact("5/1/2024 7:00 PM", "M/d/yyyy h:mm tt", null); display(exactTime);
Documentation for the custom format specified in this sample can be found on Microsoft Learn.
DateOnly and TimeOnly
However, many folks work with just a date or just a time. We can now work with just these parts that describe dates and times using the DateOnly
and TimeOnly
variable types.
var gameDay = new DateOnly(2024, 5, 1); display(gameDay); var startTime = new TimeOnly(19, 0, 0); display(startTime);
Time zones, Daylight Savings Time and DateTimeOffset
For more complex operations that need to include and handle information across time zones and daylight savings time, its recommended that you use the DateTimeOffset
date type. This type includes time zone information and can properly handle the transition of date information across daylight savings regulations. This type is significantly more accurate for specifying an exact point in time and reflecting that in different time zones.
The easiest way to work with DateTimeOffset
is to create one from a DateTime
and specify the time zone information or the type of DateTime
that was intended using a DateTimeKind
value. The DateTimeKind
allows you to specify if the DateTime
is in UTC time, Local time to a specific time zone, or doesn't reference a specific time zone.
// let's specify that our gameNight is a local time DateTime.SpecifyKind(gameNight, DateTimeKind.Local); var gameNightOffset = new DateTimeOffset(gameNight); display(gameNight); display(gameNightOffset);
You can see that the gameNight
value points to 7pm as we originally specified, and in my local time zone (EST which is 4 hours behind UTC), the gameNightOffset
is 23:00 UTC. We can also force the creation of a DateTimeOffset
with a specific time zone offset by passing in a TimeSpan
value that specifies the number of hours of the offset:
DateTime.SpecifyKind(gameNight, DateTimeKind.Local); // -5 for Central Daylight Time, used in Chicago, St. Louis, and Austin, Texas var gameNightOffset = new DateTimeOffset(gameNight, TimeSpan.FromHours(-5)); display(gameNight); display(gameNightOffset);
Now everyone will see that the gameNightOffset
is stored with the value of May 2nd at midnight UTC. We can convert that back to local time or any other time zone using the TimeZoneInfo
object. For example, if I wanted to convert this to Eastern European Time for my friends in Sofia, Bulgaria to know when my game night is planned, I can reference the FLE Standard Time
timezone on Windows like this:
var easternEuropeanTimeZone = TimeZoneInfo.FindSystemTimeZoneById("FLE Standard Time"); var eetGameNight = TimeZoneInfo.ConvertTime(gameNightOffset, easternEuropeanTimeZone); display(eetGameNight);
Your system's time zone names might be different, and you can see a list of the time zones available with this command:
display(TimeZoneInfo.GetSystemTimeZones());
TimeSpans - Referencing a Duration of Time
We saw above the use of the time span object to create a block of five hours. A TimeSpan
in C# allows you to define or reference a duration of time. We can use a TimeSpan
to reference the time of day as a duration from midnight to that specific time of day and we can also use time spans to help with arithmetic around dates and times.
var timeOurGameStarts = gameNight.TimeOfDay; display(timeOurGameStarts); var gameDuration = new TimeSpan(2, 0, 0); // 2 hours for our game night display(gameDuration); var timeToNextGameNight = new TimeSpan(14, 0, 0, 0); // 14 days until our next game night display(timeToNextGameNight);
You can use this syntax to define a TimeSpan
from days, hours, minutes, and seconds. You can also use some shortcut methods to create a TimeSpan
for a duration:
var fiveDays = TimeSpan.FromDays(5); var threeHours = TimeSpan.FromHours(3);
Arithmetic and Dates
Importantly, there are times when developers want to perform calculations between two dates, determine the end time for a duration, and other operations. The DateTime
and DateTimeOffset
objects provide the ability to Add
and Subtract
durations and date values.
Notice in the following sample that we also use the value DateTime.Now
to reference the current time in the computer's current timezone. We can also use the value DateTime.UtcNow
to reference the current time in the UTC time zone
var gameEndingTime = gameNight.Add(TimeSpan.FromHours(3)); display(gameEndingTime); var timeToGameNight = gameNight.Subtract(DateTime.Now); display(timeToGameNight);