Skip to main content

Date and Time in .NET: DateTime, DateTimeOffset, TimeZoneInfo, DateOnly, TimeOnly, and TimeSpan

What is UTC

UTC (Coordinated Universal Time) is the world’s primary time standard used to regulate clocks and time zones. It serves as the reference point for civil time worldwide, ensuring that all local times are defined by their offset from UTC.

DateTime

The DateTime class provides a way to work with dates and times without including an offset. This approach can reduce a certain level of accuracy when dealing with time zones. For example, calling DateTime.Now returns the current date and time based on your computer’s local time zone.

DateTime.Now gives you the local time, while DateTime.UtcNow returns the universal coordinated time (UTC).

You typically use DateTime when you only need to track the date and time itself, without worrying about time zones. This is suitable for scenarios such as birthdays, deadlines, or local schedules, especially when your application is used primarily within a single time zone.

The DateTime class also includes a Kind property, which provides limited information about the time zone context.

DateTimeOffset 

The DateTimeOffset structure represents a specific date and time along with the offset from UTC for that particular date and time. It includes all the functionality of DateTime but adds the ability to track the offset.

For example, 2025-11-29T18:00:00+02:00 represents 18:00 local time, with an offset of +02:00 from UTC. This means that the corresponding UTC time is 16:00. The offset explicitly shows the difference between the local time and UTC.

It is important to note that a DateTimeOffset only knows the date, time, and UTC offset. It does not know the actual time zone, so it cannot account for Daylight Saving Time (DST) transitions or other time zone rules.

TimeZoneInfo

The TimeZoneInfo class is used to get complete information about time zones on a system. It allows you to create new time zones and easily convert times between different zones. This is especially useful for scenarios where you need to handle users in multiple countries, such as scheduling workflows or sending reminders. For example, you can trigger a workflow at 09:00 local time for each customer, regardless of their time zone.

For instance, calling TimeZoneInfo.Local.ToString() might return a string like (UTC+02:00) Helsinki, Kyiv, Riga, Sofia, Tallinn, Vilnius. You can then convert UTC time to local time using code like:

var localTimeZone = TimeZoneInfo.Local;

var helsinkiTime = TimeZoneInfo.ConvertTimeFromUtc(utc, localTimeZone);

One of the key advantages of TimeZoneInfo is that it helps avoid mistakes caused by daylight-saving time changes. For example, Finland switches between UTC+2 in winter and UTC+3 in summer. During the transition, clocks jump forward from 02:00 to 03:00, meaning the times from 02:00 to 02:59 do not exist. If you schedule a workflow to run at 02:30 on the DST transition day, it will not be triggered. Using TimeZoneInfo ensures that such scenarios are handled correctly.

DateOnly

The DateOnly structure was introduced in .NET 6 and is designed to represent a specific date without any time component. Prior to .NET 6, developers typically used DateTime to handle dates, even when the time part was not needed.

DateOnly represents a date from the start to the end of the day, but it does not include hours, minutes, seconds, or any UTC offset. This makes it ideal for scenarios where only the date matters, such as birthdays, anniversaries, or other business-related dates. While you can use DateTime and simply ignore the time component, DateOnly has several advantages.

One major advantage is that a DateOnly value is not affected by time zones. It always represents the date that was set. This ensures consistency regardless of the system’s local time or daylight-saving changes.

Another benefit is that DateOnly uses less data, which can make interactions with databases more straightforward. For example, in SQL Server, dates are often stored in a DATE type, which does not include a time component. Using DateOnly aligns better with such database types.

You can perform calculations with DateOnly by adding or subtracting days, months, or years. It also supports conversions from DateTime to DateOnly and allows for direct comparisons between dates.

TimeOnly

TimeOnly is a structure that represents a time of day without any associated date. It includes hours, minutes, seconds, and fractions of a second, making it ideal for scenarios where you only need to work with time. TimeOnly can be used instead of DateTime or TimeSpan when the date is irrelevant and no time zone information is needed.

For example, you can create a TimeOnly value from the current time using:

var timeOnly = TimeOnly.FromDateTime(DateTime.Now);

Console.WriteLine($"timeOnly: {timeOnly}"); // e.g., 19:41

This will display just the current time without any date.

TimeOnly is useful for representing concepts like opening and closing hours, or scheduling recurring daily tasks such as running a workflow at 09:00 every day or sending reminders.

TimeSpan

TimeSpan represents a duration or interval of time between two points. It can also be used to express the difference between two dates or times.

For example:

DateTime start = DateTime.Now;

DateTime end = start.AddHours(3.5);

TimeSpan difference = end - start;

Console.WriteLine(difference); // 03:30:00

TimeSpan shows the interval of 3 hours and 30 minutes between start and end.

TimeSpan is commonly used for scenarios such as calculating durations, defining opening and closing hours, or scheduling tasks and reminders that recur daily.

Source:
https://learn.microsoft.com/en-us/dotnet/standard/datetime/choosing-between-datetime
https://learn.microsoft.com/en-us/dotnet/standard/datetime/

Comments

Popular posts from this blog

Declarative Programming in Angular with Async Pipe and shareReplay

A declarative approach is a way that focuses on writing code that specifies what the application should do, rather than detailing how it should be done. For example, with the async pipe in Angular, we don’t need to write code to manually subscribe to an Observable, handle its data, and update the view. Instead, we simply specify in the template that we want the data from the Observable using the async pipe. Angular handles all the underlying processes to retrieve and display the data It's often used in reactive programming with RxJS and Angular's built-in features, such as the async pipe. export class ProductComponent { product$ = this.productService.getProduct(); constructor(private productService: ProductService) {} } The product observable will hold the product data and the async pipe in the template will automatically subscribe and unsubscribe observable <div *ngIf="product$ | async as product"> <h1>{{ product.name }}</h1> <p>{{...

Ensuring Data Integrity: The Role of Database Transactions

 1. What is database translation Database transactions are activities transferring, changing data from one consistent state to another consistent state for example in everyday life we make different kind of business transactions from buying products, changing or cancelling orders, buying tickets, etc. And all these activities involve the movement/transaction of data in the database. For example, consider a simple transaction of moving an amount of 5000 from one bank account to another. This transaction includes several steps: decrease the account balance by 5000, and then increase the other account balance by 50003. Each of these steps is a part of the transaction, and if any step fails, the entire transaction fails 2. Why it is crucial in reliable software systems? When it comes to business, whether it is personal or financial data, it is crucial to ensure the reliability and stability of data management. In our daily lives, we may frequently encounter errors related to database t...

The Developer’s Guide to Clean Code: Tips and Techniques

What is clean code? Clean code is a term used to describe code that is easy to read, understand, and maintain. It is written in a way that makes it simple, concise, and expressive. Clean code follows a set of conventions, standards, and practices that make it easy to read and follow. Here are some signs indicating that the code is not clean: 1. Poor names The name is not clear to understand, meaningless, or misleading . It doesn't reveal the intention of what it want to achieve. Consider the following examples: SqlDataReader drl; int od; void Button1_Click(); Class Pages1 In the examples above, it’s challenging to get the purpose of drl, od, or what Button1_Click() does. To enhance clarity, we can rename these identifiers as follows: SqlDataReader dataReader/reader; int overdueDays; void CheckAvailability_Click(); Class ViewCustomerPage {} Ambiguous names int? incidentNameId for instance. incidentNameId lacks clarity because if it represents the ID of an incident, then the inclu...