DateTime
Think of a certain point in time. In C# programs, a DateTime
struct
instance can be used to represent this time value (and handle its complexities).
We use DateTime
and its many formatting codes to parse and format time. As a struct
, a DateTime
is more like an int
than a class
instance.
Here we call the instance DateTime
constructor. The arguments must match a real date that occurred. This is a validating constructor—the month and day follow the year argument.
DateTime
struct
to the console.using System; // Create a DateTime with 3 arguments. DateTime value = new DateTime(2022, 6, 22); Console.WriteLine("TIME: {0}", value);TIME: 6/22/2022 12:00:00 AM
The DateTime
constructor validates possible arguments. If we try to create a DateTime
that cannot exist, we get an argument exception.
using System; // This will cause an error. DateTime x = new DateTime(-1, 1, 1);An unhandled exception of type 'System.ArgumentOutOfRangeException' occurred in mscorlib.dll Additional information: Year, Month, and Day parameters describe an un-representable DateTime.
AddDays
, AddWe can add values to a DateTime
with methods like AddDays
, AddMonths
and Add()
which receives a TimeSpan
. The most versatile method is Add()
, and we can add any TimeSpan
.
TimeSpan
argument. We must first use the TimeSpan
constructor.AddDays
receives a double
—it can add or subtract days. We can use AddHours
, AddMinutes
, AddSeconds
and more.using System; var year2K = new DateTime(2000, 1, 1); // Update the time with AddDays, AddMonths and Add. var updated = year2K.AddDays(1); Console.WriteLine("ADD 1 DAY: {0}", updated); updated = updated.AddMonths(1); Console.WriteLine("ADD 1 MONTH: {0}", updated); // Add 1 day with Add and TimeSpan. updated = updated.Add(TimeSpan.FromDays(1)); Console.WriteLine("ADD 1 DAY: {0}", updated);ADD 1 DAY: 1/2/2000 12:00:00 AM ADD 1 MONTH: 2/2/2000 12:00:00 AM ADD 1 DAY: 2/3/2000 12:00:00 AM
Here we subtract one day from the current day. We do this by adding -1 to the current day. This is necessary because no "SubtractDays
" method is provided.
DateTime.Today
is always set to the machine's local time, which depends on the current system.using System; class Program { static void Main() { Console.WriteLine("Today: {0}", DateTime.Today); DateTime y = GetYesterday(); Console.WriteLine("Yesterday: {0}", y); } /// <summary> /// Gets the previous day to the current day. /// </summary> static DateTime GetYesterday() { // Add -1 to now. return DateTime.Today.AddDays(-1); } }Today: 11/30/2008 12:00:00 AM Yesterday: 11/29/2008 12:00:00 AM
To figure out tomorrow, we add one using the Add()
method. This is useful with date queries in databases. We use the AddDays
method.
GetTomorrow
is a static
method—it does not require state to be saved. DateTime.Today
is also static
.DateTime
Add methods use offsets—they accept both negative and positive numbers. Here we go back in time.using System; class Program { static void Main() { Console.WriteLine("Today: {0}", DateTime.Today); DateTime d = GetTomorrow(); Console.WriteLine("Tomorrow: {0}", d); } /// <summary> /// Gets the next day, tomorrow. /// </summary> static DateTime GetTomorrow() { return DateTime.Today.AddDays(1); } }Today: 11/30/2008 12:00:00 AM Tomorrow: 12/1/2008 12:00:00 AM
We use a helper method to find the first day in a year. We use an overloaded method. With overloading, we often can use methods in an easier, clearer way.
FirstDayOfYear
with no parameter. The year from Today will be used.using System; class Program { static void Main() { Console.WriteLine("First day: {0}", FirstDayOfYear()); DateTime d = new DateTime(1999, 6, 1); Console.WriteLine("First day of 1999: {0}", FirstDayOfYear(d)); } /// <summary> /// Gets the first day of the current year. /// </summary> static DateTime FirstDayOfYear() { return FirstDayOfYear(DateTime.Today); } /// <summary> /// Finds the first day of year of the specified day. /// </summary> static DateTime FirstDayOfYear(DateTime y) { return new DateTime(y.Year, 1, 1); } }First day: 1/1/2008 12:00:00 AM First day of 1999: 1/1/1999 12:00:00 AM
Here we find the last day in any year. Leap years make this more complicated, as February may have 28 or 29 days. We must programmatically find the year's length.
DateTime
constructor, rather than DateTime.Parse
. This is faster and has clearer syntax.using System; class Program { static void Main() { Console.WriteLine("Last day: {0}", LastDayOfYear()); DateTime d = new DateTime(1999, 6, 1); Console.WriteLine("Last day of 1999: {0}", LastDayOfYear(d)); } /// <summary> /// Finds the last day of the year for today. /// </summary> static DateTime LastDayOfYear() { return LastDayOfYear(DateTime.Today); } /// <summary> /// Finds the last day of the year for the selected day's year. /// </summary> static DateTime LastDayOfYear(DateTime d) { // Get first of next year. DateTime n = new DateTime(d.Year + 1, 1, 1); // Subtract one from it. return n.AddDays(-1); } }Last day: 12/31/2008 12:00:00 AM Last day of 1999: 12/31/1999 12:00:00 AM
UtcNow
The Now property returns the current DateTime
, with all of the fields correctly filled. UtcNow
is Universal Coordinated Time, or Greenwich MeanTime
.
using System; var now = DateTime.Now; var utc = DateTime.UtcNow; // UTC is Greenwich MeanTime. Console.WriteLine("LOCAL: {0}", now); Console.WriteLine("UTC: {0}", utc);LOCAL: 3/14/2020 3:17:55 AM UTC: 3/14/2020 10:17:55 AM
Parse
It is possible to parse DateTime
instances. This converts a string
value into a DateTime
. The "Try" methods avoid expensive exceptions on invalid strings.
TryParse
in an if
-statement. If the parsing is successful, we print the RESULT line.string
is a correct date, we can use Parse
. But this will throw an exception on invalid data.using System; string value = "3/13/2020"; // Use TryParse to convert from string to DateTime. if (DateTime.TryParse(value, out DateTime result)) { Console.WriteLine("RESULT: {0}", result); }RESULT: 3/13/2020 12:00:00 AM
DateTime
We find the "age" of a certain date, and how long ago it was in time. We can do this with DateTime.Subtract
, which will return a TimeSpan
.
DateTime.Now
property.using System; string value1 = "3/13/2020"; string value2 = "3/14/2020"; // Parse the dates. var date1 = DateTime.Parse(value1); var date2 = DateTime.Parse(value2); // Compute the difference between the 2 dates. var difference = date2.Subtract(date1); Console.WriteLine("DAYS DIFFERENCE: {0}", difference.TotalDays);DAYS DIFFERENCE: 1
We access the Month property—this returns an integer from 1 to 12 that indicates the month. The value 3 here indicates the month of March.
using System; // Print the month for this date string. var date = DateTime.Parse("3/13/2020"); Console.WriteLine("MONTH: {0}", date.Month);MONTH: 3
DaysInMonth
Many static
methods are also available on the DateTime
class
. With DaysInMonth
we look up the number of days in a month based on the year.
using System; int days = DateTime.DaysInMonth(2014, 9); // September. Console.WriteLine(days); days = DateTime.DaysInMonth(2014, 2); // February. Console.WriteLine(days);30 28
null
A DateTime
cannot be assigned to null
. It is a struct
, and like an int
cannot be null
. To fix this program, try using the special value DateTime.MinValue
instead of null
.
using System; DateTime current = null;error CS0037: Cannot convert null to 'DateTime' because it is a non-nullable value type
MinValue
exampleWe can use a special value like DateTime.MinValue
to initialize an empty DateTime
. This is the same idea as a null
or uninitialized DateTime
.
using System; // This program can be compiled. // ... Use MinValue instead of null. DateTime current = DateTime.MinValue; Console.WriteLine("MIN VALUE: {0}", current);MIN VALUE: 1/1/0001 12:00:00 AM
We can never have a null
DateTime
instance. DateTime
is a value type. But we can use a nullable DateTime
. We use a question mark "DateTime
?" for this type.
TestNullable()
method. It receives a nullable DateTime
, and uses HasValue
and Value to test it.DateTime
and initializing it to Today. Then we create a null
DateTime
.using System; class Program { static void TestNullable(DateTime? argument) { // Handle nullable DateTime. if (argument.HasValue) { Console.WriteLine("VALUE: {0}", argument.Value); Console.WriteLine("VALUE YEAR: {0}", argument.Value.Year); } else { Console.WriteLine("NULL"); } } static void Main() { // Call method with nullable DateTime. DateTime? value = DateTime.Today; TestNullable(value); DateTime? value2 = null; TestNullable(value2); } }VALUE: 5/14/2019 12:00:00 AM VALUE YEAR: 2019 NULL
Sort
An array or List
of DateTime
structs can be sorted. This will go from low to high (early to later) in an ascending sort by default.
using System; using System.Collections.Generic; var values = new List<DateTime>(); values.Add(new DateTime(2023, 1, 1)); values.Add(new DateTime(2021, 1, 1)); values.Add(new DateTime(2022, 1, 1)); // Sort the dates. values.Sort(); // The dates have been sorted from first to last chronologically. foreach (var value in values) { Console.WriteLine(value); }1/1/2021 12:00:00AM 1/1/2022 12:00:00AM 1/1/2023 12:00:00AM
DateTime
cacheHere is a way to optimize DateTime
usage. When a method calls DateTime.Now
, we can sometimes cache this value. This prevents excessive time queries.
DateTime.Now
is slower than most property accesses as it must fetch the time from the OS.DateTime.Now
each time—no caching layer is introduced.DateTime
much faster than before.using System; using System.Diagnostics; static class DateTimeNowCache { const int _count = 20; static DateTime _recentTime = DateTime.Now; static int _skipped; public static DateTime GetDateTime() { // Get a new DateTime.Now every several requests. // ... This reduces the number of OS time accesses. _skipped++; if (_skipped > _count) { _recentTime = DateTime.Now; _skipped = 0; } return _recentTime; } } class Program { const int _max = 1000000; static void Main() { // Version 1: use cached DateTime.Now. var s1 = Stopwatch.StartNew(); for (int i = 0; i < _max; i++) { var result = DateTimeNowCache.GetDateTime(); if (result == DateTime.MaxValue) { return; } } s1.Stop(); // Version 2: use DateTime.Now each time. var s2 = Stopwatch.StartNew(); for (int i = 0; i < _max; i++) { var result = DateTime.Now; if (result == DateTime.MaxValue) { return; } } s2.Stop(); Console.WriteLine(((double)(s1.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns")); Console.WriteLine(((double)(s2.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns")); } }29.76 ns DateTimeNowCache 141.39 ns DateTime.Now
Time is a complex subject. But with DateTime
and TimeSpan
we represent it with relative ease in our programs. These types are value types—like an int
.