Welcome to today’s post.
In today’s post I will be discussing the usage of the new date and time types that have been introduced into the .NET (Core) 6 framework that is included with the Visual Studio 2022 development environment.
The new types that have been introduced as part of the date and time .NET framework library are the DateOnly and TimeOnly types. The purpose behind introducing these types is to allow the developer to create instances of a date object that do not include the time part, and to create instances of a time object that do not include a date part.
With the previous version of the date and time library, when we created an instance of a DateTime type, both the date and time parts of the date will always be displayed when we use the string extension method ToString() to output the date time object. With an object created with a DateOnly the output from the same string extension method outputs the date only.
I will first show how to use the DateOnly type with some basic operations and compare them with the equivalent DateTime type.
Comparing Outputs from the DateOnly and DateTime Types
Before we can use the date and time types, declare the following system library:
using System;
First, we create two sets of objects of type DateType:
DateOnly startDateOnly = new DateOnly(2022, 06, 01);
DateOnly endDateOnly = new DateOnly(2022, 09, 15);
First, we create two sets of objects of type DateTime:
DateTime startDate = new DateTime(2022, 06, 01);
DateTime endDate = new DateTime(2022, 09, 15);
Code to output the objects of type DateType:
Console.WriteLine("Start Date (Only): " + startDateOnly.ToString());
Console.WriteLine();
Console.WriteLine("End Date (Only): " + endDateOnly.ToString());
Console.WriteLine();
Code to output the objects of type DateTime:
Console.WriteLine("Start Date (Old): " + startDate.ToString());
Console.WriteLine();
Console.WriteLine("End Date (Old): " + endDate.ToString());
Console.WriteLine();
The console displays the following outputs for the DateOnly and DateTime objects;
Start Date (Old): 1/06/2022 12:00:00 AM
End Date (Old): 15/09/2022 12:00:00 AM
Start Date (Only): 1/06/2022
End Date (Only): 15/09/2022
As we can see, the old DateTime objects output the data part and time part that includes 12:00:00 AM. The new DateOnly objects output the date part only.
The DayNumber Property from the DayOnly Type
A useful property of the DateOnly type is the DayNumber property, which returns the total number of days of the current day (of the object instance) since the minimum date, which is 01-01-0001.
int numDaysSinceZero = startDateOnly.DayNumber;
Console.WriteLine("Number of Days of Start Date Since Min Date (Only): " + numDaysSinceZero);
Console.WriteLine();
The output for the above is:
Number of Days of Start Date Since Min Date (Only): 738306
The equivalent calculation performed using the legacy DateTime type is show below:
int numDaysSinceZeroOld = startDate.Subtract(DateTime.MinValue).Days;
Console.WriteLine("Number of Days of Start Date Since Min Date (Old): " + numDaysSinceZeroOld);
Console.WriteLine();
The output for the above is:
Number of Days of Start Date Since Min Date (Old): 738306
Both are identical results with the DateOnly type having the calculation confined to one property instead of the combination of the multiple DateTime methods.
Determining Differences between Dates
With the DateOnly and DateTime types one other useful calculation is to be able to subtract two different DateType object instances to determine the number of days.
With the legacy DateTime type, determining the number of days difference between two dates is done using the DateTime type’s Subtract() method and Days property of the TimeSpan type. This is done as shown:
TimeSpan numDaysOldSpan = endDate.Subtract(startDate);
int numDaysOld = numDaysOldSpan.Days;
Console.WriteLine("Days Between Start Date and End Date (Old): " + numDaysOld);
Console.WriteLine();
The output from the above is shown below:
Days Between Start Date and End Date (Old): 106
With the DateOnly type, we use the DayNumber property of each DateOnly object and simply subtract them as shown:
int numDaysOnly = endDateOnly.DayNumber - startDateOnly.DayNumber;
Console.WriteLine("Days Between Start Date and End Date (Only): " + numDaysOnly);
Console.WriteLine();
The output from the above is shown below:
Days Between Start Date and End Date (Only): 106
As we can see, using the DayNumber property from the DateOnly type gives simpler arithmetic when performing date subtraction.
I will now show how to use the TimeOnly type with display outputs and basic calculations.
Comparing Outputs from the TimeOnly and DateTime Types
Before I show how to output times from a TimeOnly type, we will create object instances of each of the TimeOnly and DateTime types.
With the TimeOnly type, we do not need a default DateTime object instance to associate with the time object. A time that starts at 12pm is declared as shown:
TimeOnly startTimeOnly = new TimeOnly(12, 0, 0);
A time that ends at 6:30:15pm is declared as shown:
TimeOnly endTimeOnly = new TimeOnly(18, 30, 15);
The code to output for the above TimeOnly objects is shown below:
Console.WriteLine("Start Time (Only): " + startTimeOnly.ToString());
Console.WriteLine("Start Time (Only-Long Format): " + startTimeOnly.ToLongTimeString());
Console.WriteLine();
Console.WriteLine("End Time (Only): " + endTimeOnly.ToString());
Console.WriteLine("End Time (Only-Long Format): " + endTimeOnly.ToLongTimeString());
Console.WriteLine();
The resulting output when run is shown below:
Start Time (Only): 12:00 PM
Start Time (Only-Long Format): 12:00:00 PM
End Time (Only): 6:30 PM
End Time (Only-Long Format): 6:30:15 PM
We can see the short format for the TimeOnly type displays as HH:MM [AM/PM] and the long format shows as HH:MM:SS [AM/PM].
However, for a DateTime type, to create a time object, we will have to declare a default minimum date of type DateTime:
DateTime startZeroDate = DateTime.MinValue;
The respective start and end date time object instances are created as follows based on the zero-date object and using the hours, minutes, and seconds of the TimeOnly object instances created previously:
DateTime startTime = new DateTime(startZeroDate.Year, startZeroDate.Month, startZeroDate.Day, startTimeOnly.Hour, startTimeOnly.Minute, startTimeOnly.Second);
DateTime endTime = new DateTime(startZeroDate.Year, startZeroDate.Month, startZeroDate.Day, endTimeOnly.Hour, endTimeOnly.Minute, endTimeOnly.Second);
The code to output for the above DateTime objects is shown below:
Console.WriteLine("Start Time (Old): " + startTime.ToString());
Console.WriteLine();
Console.WriteLine("End Time (Old): " + endTime.ToString());
Console.WriteLine();
The resulting output when run is shown below:
Start Time (Old): 1/01/0001 12:00:00 PM
End Time (Old): 1/01/0001 6:30:15 PM
The main difference between TimeOnly and DateTime outputs is noticeable. With a DateTime type, the output is in the format DD/MM/YYYY HH:MM:SS [AM/PM], whereas the format for the TimeOnly type is: HH:MM:SS [AM/PM].
Determining Differences between Times
To determine differences between two times, we can use an approach that uses the TimeSpan type with the TotalMinutes property and FromMinutes method. We then read off the respective properties of Hours, Minutes and Seconds from the resulting TimeSpan object. This approach is shown below:
Double tsStart = new TimeSpan(12, 45, 0).TotalMinutes;
Double tsEnd = new TimeSpan(18, 30, 15).TotalMinutes;
TimeSpan ts = TimeSpan.FromMinutes(tsEnd - tsStart);
Console.WriteLine("Total Hours Between Start Time and End Time (Old): " + ts.TotalHours);
Console.WriteLine("Total Minutes Between Start Time and End Time (Old): " + ts.TotalMinutes);
Console.WriteLine("Total Seconds Between Start Time and End Time (Old): " + ts.TotalSeconds);
Console.WriteLine();
Console.WriteLine("Hours Difference Between Start Time and End Time (Old): " + ts.Hours);
Console.WriteLine("Minutes Difference Between Start Time and End Time (Old): " + ts.Minutes);
Console.WriteLine("Seconds Difference Between Start Time and End Time (Old): " + ts.Seconds);
Console.WriteLine();
The above code excerpt when run produces the following outputs:
Total Minutes Between Start Time and End Time (Old): 345.25
Hours Difference Between Start Time and End Time (Old): 5
Minutes Difference Between Start Time and End Time (Old): 45
Seconds Difference Between Start Time and End Time (Old): 15
The Hours, Minutes and Seconds properties from the TimeSpan object are the properties of the resulting time span object created from the differences in minutes between both time span objects.
If we were to read off just the TotalHours, TotalMinutes and TotalSeconds properties from the TimeSpan object, the outputs would correspond to the absolute time span of the instance including the fractional part.
Total Hours Between Start Time and End Time (Old): 5.754166666666666
Total Minutes Between Start Time and End Time (Old): 345.25
Total Seconds Between Start Time and End Time (Old): 20715
The above approach can also be amended with a difference in Ticks to achieve the same result as follows:
TimeSpan tsStart = new TimeSpan(12, 45, 0);
TimeSpan tsEnd = new TimeSpan(18, 30, 15);
TimeSpan ts = new TimeSpan(tsEnd1.Ticks - tsStart1.Ticks);
An approach that uses the TimeOnly type also uses the TimeSpan type as shown below:
TimeOnly startTimeOnly = new TimeOnly(12, 0, 0);
TimeOnly endTimeOnly = new TimeOnly(18, 30, 15);
TimeSpan tt = new TimeSpan(endTimeOnly.Ticks - startTimeOnly.Ticks);
Console.WriteLine();
Console.WriteLine("Hours Difference Between Start Time and End Time (Only): " + tt.Hours);
Console.WriteLine("Minutes Difference Between Start Time and End Time (Only): " + tt.Minutes);
Console.WriteLine("Seconds Difference Between Start Time and End Time (Only): " + tt.Seconds);
The above code excerpt when run produces the following outputs:
Hours Difference Between Start Time and End Time (Only): 5
Minutes Difference Between Start Time and End Time (Only): 45
Seconds Difference Between Start Time and End Time (Only): 15
With both approaches above, there is no discernible difference in the ease of calculations.
I will now show how a DataTime object can be constructed from a TimeOnly object.
Construction of a DateTime Object from a TimeOnly Object
Using the ToDateTime() extension method of the DateTime type, we can pass in a TimeOnly object instance, which will create an instance of a DateTime object that contains properties from a TimeOnly object.
DateTime startDateTime = startDateOnly.ToDateTime(startTimeOnly);
Console.WriteLine("Start Date Time (Old): " + startDateTime.ToString());
Console.WriteLine("Start Date Time (Old-Long Format): " + startDateTime.ToLongDateString());
When the above is run, the output is shown below:
Start Date Time (Old): 1/06/2022 12:00:00 PM
Start Date Time (Old-Long Format): Wednesday, 1 June 2022
One more useful method in the TimeOnly type helps us determine if a time is between two other times.
Determining Times Between a Time Period with the InBetween() method
Determining if a time is within the time period of a pair of times is a computation we may have performed in the past, and it requires comparing the time properties (hour, minute, second) against each of the time components from a start and end time. To help us, we use the InBetween() method of the TimeOnly type. It returns a Boolean result. An example is shown below:
// Determine if time is between two times
TimeOnly rightNow = TimeOnly.FromDateTime(DateTime.Now);
TimeOnly startAfternoonTimeOnly = new TimeOnly(13, 0, 0);
TimeOnly endAfternoonTimeOnly = new TimeOnly(18, 0, 0);
if (rightNow.IsBetween(startAfternoonTimeOnly, endAfternoonTimeOnly))
Console.WriteLine($"{rightNow} is between times {startAfternoonTimeOnly} and {endAfternoonTimeOnly}!");
else
Console.WriteLine($"{rightNow} is outside times {startAfternoonTimeOnly} and {endAfternoonTimeOnly}!");
TimeOnly startSleepTimeOnly = new TimeOnly(0, 0, 0);
TimeOnly endSleepTimeOnly = new TimeOnly(6, 0, 0);
if (rightNow.IsBetween(startSleepTimeOnly, endSleepTimeOnly))
Console.WriteLine($"{rightNow} is between times {startSleepTimeOnly} and {endSleepTimeOnly}!");
else
Console.WriteLine($"{rightNow} is outside times {startSleepTimeOnly} and {endSleepTimeOnly}!");
If we run the above just after midnight, the following result ensues:
12:01 AM is outside times 1:00 PM and 6:00 PM!
12:01 AM is between times 12:00 AM and 6:00 AM!
As you can see, there are some useful methods and properties of the DateOnly and TimeOnly types that have been introduced into the .NET (Core) 6 framework.
When used in conjunction with the existing DateTime and TimeSpan types of the existing date and time library, it allows us to solve date and time-based problems with less code and more efficiency.
That is all for today’s post.
I hope you have found this post useful and informative.
Andrew Halil is a blogger, author and software developer with expertise of many areas in the information technology industry including full-stack web and native cloud based development, test driven development and Devops.