C#
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented C#
-
Section 4: Working with Data
-
Section 5: Error Handling
-
Section 6: Delegates and Events
-
Section 7: Async Programming
-
Section 8: More Language Features
-
Section 9: File I/O and Serialization
-
Section 10: Networking in .NET
-
Section 11: The .NET Ecosystem
-
Section 12: Memory and Performance
-
Section 13: Concurrency Beyond Async
-
Section 14: Reflection and Attributes
-
Section 15: Testing and Best Practices
-
Section 16: Design Patterns in C#
-
Section 17: Standard Library Deep Dive
-
Section 18: Data Structures and Algorithms in C#
-
Section 19: GUI and Desktop Development Overview
-
Section 20: Practical Projects
-
Section 21: More Practice Exercises
-
Section 22: More Standard Library and Text Processing
-
Section 23: More Design Patterns
-
Section 24: More Projects
-
Section 25: Interview Practice
-
Section 26: C# Keywords Reference (Modifiers)
-
Section 27: C# Keywords Reference (Statements)
-
Section 28: C# Keywords Reference (Operators)
-
Section 29: BCL: System.Collections.Generic
-
Section 30: BCL: System.Linq
-
Section 31: BCL: System.Threading
-
Section 32: BCL: System.IO
-
Section 33: BCL: System.Text and System.Text.Json
-
Section 34: BCL: System.Net.Http
-
Section 35: C# Language Specification Topics
-
Section 36: More Practice Exercises
-
Section 37: More Async Patterns
-
Section 38: More BCL: System.Reflection and System.Diagnostics
-
Section 39: Nullable Reference Types In Depth
-
Section 40: C# Records and Pattern Matching In Depth
-
Section 41: Dependency Injection Deep Dive
-
Section 42: More Interview and Whiteboard Practice
112: The DateTime and DateOnly Types
A few years ago, I was reviewing a pull request for a medical records system. The developer had used DateTime to store patient birthdates. It seemed fine until we deployed to a server in a different time zone. Suddenly, patients born on the 1st of the month were appearing as if they were born on the 31st of the previous month. Because DateTime includes a time component, the system was trying to be "helpful" by adjusting the time to the local zone, which pushed the date back a few hours—and into the previous day. It was a classic, frustrating bug that could have been avoided if we had a type that simply didn't care about hours, minutes, or time zones.
The Complexity of DateTime and its Kind
For a long time, DateTime was the only tool in the shed. It represents a point in time, combining both a date and a time of day. The trickiest part of DateTime is the Kind property. You'll see DateTimeKind.Utc, DateTimeKind.Local, and DateTimeKind.Unspecified. If you're building an app that users access globally, you should almost always store your data in UTC. I can't stress this enough: avoid DateTime.Now in your business logic; use DateTime.UtcNow instead. If you use local time, you're essentially gambling that your server's regional settings will never change.
// The common way to capture the exact moment something happened
DateTime timestamp = DateTime.UtcNow;
Console.WriteLine($"Event occurred at: {timestamp}");
// Converting it to the user's local time for display
DateTime localTime = timestamp.ToLocalTime();
Console.WriteLine($"Local time: {localTime}");
The problem is that DateTime is "heavy." When you only need to know that someone was born on May 12th, you don't need to know that they were born at 12:00:00 AM UTC. That extra precision is where the time zone bugs I mentioned earlier creep in.
When to Reach for DateOnly
Introduced in .NET 6, DateOnly is exactly what it sounds like: a date without a time. It's a value type that represents a date, and it doesn't have a Kind property because it doesn't have a time zone. This makes it the perfect choice for birthdays, anniversary dates, or any calendar-based deadline where the specific hour of the day is irrelevant. It removes the ambiguity and prevents the "shifting date" bug entirely.
// Creating a specific date (Year, Month, Day)
DateOnly birthday = new DateOnly(1990, 5, 12);
Console.WriteLine($"Birthday: {birthday}");
// You can easily create a DateOnly from an existing DateTime
DateTime now = DateTime.UtcNow;
DateOnly today = DateOnly.FromDateTime(now);
// Adding days is straightforward
DateOnly nextWeek = today.AddDays(7);
If you're working with a legacy database that only provides DateTime, you can use DateOnly.FromDateTime() to strip away the time component as soon as the data enters your application logic. Conversely, if you need to perform a calculation that requires a time (like calculating the exact number of seconds until a date), you can use birthday.ToDateTime(TimeOnly.MinValue) to turn it back into a DateTime. Just be mindful of which time zone you're assuming when you make that jump.
📋 Practical Task
Build a Subscription Expiry Tracker
Create a small program that manages a user's subscription. Your goal is to use both DateTime and DateOnly appropriately to avoid the "time zone shift" problem.
- Create a
DateOnlyvariable representing the SubscriptionEndDate (set it to a date 30 days from today). - Create a
DateTimevariable representing the LastLoginTimestamp usingDateTime.UtcNow. - Write a method that takes these two values and calculates how many full days are remaining until the subscription expires.
- Print a summary to the console that looks like this:
Last Login: [UTC Timestamp]Subscription Ends: [DateOnly Value]Days Remaining: [Number]
Hint: Since you cannot subtract a DateOnly from a DateTime directly, you will need to convert the LastLoginTimestamp to a DateOnly first.
There are no comments for now.