.NET 6 is currently scheduled to be released-to-production on 9th Nov 2021. So, to get ready for that, we're kicking off a short new series that shows some of the smaller, but cool, things we can do in .NET 6 with some new framework features.
Let's kick off this new series by discussing a long-awaited feature:
To represent a date without a time, we are forced to use the
DateTime class, which has several overloads that help us get the date.
var myDate = new DateTime(2021, 9, 23); var datePart = DateTime.Now.Date;
However, these overloads and properties will always return type
DateTime, which will include a time, regardless of whether or not we need it.
Similarly, before .NET 6 there was no real way to represent a time of day without a date. We could use the
TimeSpan class to represent time elapsed (e.g. 7 hours, 21 minutes and 9 seconds), but there was no way to represent a specific time of day (e.g. 7:21:09 AM) without using
.NET 6 introduces the new
TimeOnly structs, which allow us to represent dates without time of day, or time of day without a date.
DateOnly sep10th = new DateOnly(2021, 9, 10); var dec31st = new DateOnly(1999, 12, 31); DateOnly aug3rd = new(1988, 8, 3); TimeOnly nineThirtyPM = new TimeOnly(21, 30); //21:30, or 9:30 PM TimeOnly fourTwentyThreeAM = new(4, 23, 19); //04:23:19, or 4:23:19 AM
We can create instances of
TimeOnly from corresponding
DateTime instances using a special new overload,
DateTime dateOnlyExample = new DateTime(2004, 5, 19, 4, 45, 30); DateOnly date3 = DateOnly.FromDateTime(dateOnlyExample); //May 19th, 2004 DateTime sevenFortyFiveDT = new DateTime(2011, 11, 11, 7, 45, 00); TimeOnly sevenFortyFive = TimeOnly.FromDateTime(sevenFortyFiveDT); //07:45 AM
Automatically Determines Local Culture
DateOnly struct automatically determines the local culture, and uses that to output the date.
DateOnly cultureExample = new DateOnly(2004, 5, 19); //May 19 2004 Console.WriteLine(cultureExample); //American: 5/19/2004, European: 19/5/2004, Universal: 2004-05-19
AddDays(), AddMonths(), AddYears()
DateTime, we can use overload methods like
AddDays() to modify the value of a
DateOnly addTimeExample = new DateOnly(2004, 5, 19); //May 19 2004 addTimeExample = addTimeExample.AddYears(2).AddMonths(2).AddDays(5); //Jul 24th, 2006
Parsing from Strings
We can also parse
DateOnly instances from strings using
TryParse(), just like we did with
if(DateOnly.TryParse("09/21/2013", out DateOnly result)) Console.WriteLine(result); //American: 9/21/2013, European: 21/9/2013, Universal: 2013-09-21
Stores Value as Integer
DateOnly stores its value as an integer, where 0 is January 1st, 0001. We can get that integer value from a
DateOnly instance, and use the method
DateOnly.FromDayNumber to convert that integer to a
DateOnly integerTest = new(2019, 7, 1); //July 1st 2019 int dayNumber = integerTest.DayNumber; DateOnly integerResult = DateOnly.FromDayNumber(dayNumber); //July 1st 2019
TimeOnly represents a time of day, not time elapsed (the latter is what
TimeSpan is used for).
Stores Value as Ticks from Midnight
TimeOnly internally stores its value as a
long, which are the ticks (100 nanoseconds) since 00:00:00 (midnight). We can use the ticks to create a new
TimeOnly sixTen = new TimeOnly(6, 10); long ticks = sixTen.Ticks; TimeOnly sixTenAgain = new TimeOnly(ticks);
Math Operations result in TimeSpans
We can perform math operations on instances of
TimeOnly, which give us
var afternoon = new TimeOnly(15, 15); //3:15 PM var morning = new TimeOnly(9, 10); //9:10 AN TimeSpan difference = afternoon - morning; //6 hours 5 minutes
Checking for Value in a Range
We can check if a
TimeOnly instance falls in a given range using the
var now = TimeOnly.FromDateTime(DateTime.Now); var nineAM = new TimeOnly(9, 0); var fivePM = new TimeOnly(17, 0); if(now.IsBetween(nineAM, fivePM)) Console.WriteLine("Work time!"); else Console.WriteLine("Anything other than work time!");
We can even check for a value in range that goes across midnight.
var tenPM = new TimeOnly(22, 0); var twoAM = new TimeOnly(2, 0); var midnight = new TimeOnly(0); //0 ticks == midnight if(midnight.IsBetween(tenPM, twoAM)) Console.WriteLine("It's getting late...");
We can use comparison operators (such as
>) to compare two
TimeOnly noon = new(12, 0); if (now < noon) Console.WriteLine("Good Morning!");
If you're wondering why these classes were not simply called
Time, apparently that has to do with several considerations. Partly, it is because VB has a
Date object and .NET needs to be compatible with VB. You can read more in the following blog post:
As with all posts in this series, there is a demo project you can check out over on GitHub that shows examples of
TimeOnly, and the various things we can do with them. Check it out!