Use EF Core to easily save dates as UTC & show in local time

Published: Tuesday 22 April 2025

Having trouble with time zones in your ASP.NET Core application?

If you use Entity Framework Core you can easily save dates as UTC in the database and then convert them back to your local time zone in your ASP.NET Core application.

This is particularly crucial if your application spans multiple time zones.

Introducing ValueConverters

A ValueConverter allows you to translate an entity's value to a provider like a SQL Server database and then translate it back to the model for an ASP.NET Core application.

This is perfect for what we want as we can convert a time to UTC when saving it in the database and then convert it back to the local time zone when using it in the application.

How to create a ValueConverter

To make this work you need to create a new class, import the Microsoft.EntityFrameworkCore.Storage.ValueConversion namespace and then inherit the ValueConverter class.

The ValueConverter class expects a Model and Provider generic type. As we are expecting a DateTime type in the database and the application we can include that for both of them.

Then you need to override one of the ValueConverter base constructors to do the conversion. This expects two parameters:

  • The first parameter is the ExpressionFrom value and this is how you save the value to the database. We convert the DateTime value to universal time.
  • The other parameter is the ExpressionTo value and this is how it is displayed in the application. You have to specify that the DateTime kind is UTC as that is how it's saved in the database. Then it's a case of converting it to your local time zone.

Here is the code for our DateTimeUtcConverter ValueConverter.

// DateTimeUtcConverter.cs
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;

public class DateTimeUtcConverter : ValueConverter<DateTime, DateTime>
{
	public DateTimeUtcConverter() : base(
		d => d.ToUniversalTime(), 
		d => DateTime.SpecifyKind(d, DateTimeKind.Utc).ToLocalTime()
	)
	{

	}
}

How to apply it to a property

We have a Product entity set up in our DbContext which looks like this:

// Product.cs
public class Product
{
    public int Id { get; set; }

    public string Name { get; set; } = string.Empty;

    public DateTime Created { get; set; }
}

When creating a new Product entity in the database, if we were to set the Created property to  DateTime.Now, it would save the DateTime in the database using the application's local time zone. This would become problematic if you ever changed the time zone for your application.

But configuring the Product entity allows us to specify the DateTimeUtcConverter class in the HasConverter extension method. This ensures that the Created property will be stored as UTC in the database, and converted back to the local time zone when used in the application.

// ProductConfiguration.cs
public class ProductConfiguration : IEntityTypeConfiguration<Product>
{
    public void Configure(EntityTypeBuilder<Product> builder)
    {
        builder.HasKey(s => s.Id);

        builder.Property(s => s.Created)
            .HasColumnName("CreatedUtc")
            .HasColumnType("datetime")
            .HasConversion(new DateTimeUtcConverter());
    }
}

What about applying it for all DateTime's?

If you have multiple DateTime properties that need to be stored as UTC in the database, it could be time consuming to add the converter to each of the properties. As well as that, you're reliant on future code deployments where DateTime properties are added to include the converter as part of the configuration.

However, there is a way you can apply it to all DateTime properties in your DbContext. To do that, you need to override the OnModelCreating method in your DbContext, create a new instance of DateTimeUtcConverter and loop through each entity and each of its properties. You can apply your DateTimeUtcConverter instance to that property if its CLR property is a DateTime type.

// EfCoreDbContext.cs
public class EfCoreDateTimeDbContext : DbContext
{
	...

	/// <inheritdoc/>
	protected override void OnModelCreating(ModelBuilder modelBuilder)
	{
		...

		var dateTimeUtcConverter = new DateTimeUtcConverter();

		foreach (var entityType in modelBuilder.Model.GetEntityTypes())
		{
			foreach (var property in entityType.GetProperties())
			{
				if (property.ClrType == typeof(DateTime))
				{
					property.SetValueConverter(dateTimeUtcConverter);
				}
			}
		}
		
		base.OnModelCreating(modelBuilder);
	}

}

Nullable DateTime's

If you have nullable DateTime properties you won't be able to use the DateTimeUtcConverter as it is. You'll need to create a new ValueConverter.

We have created a new DateTimeUtcNullableConverter class which once again inherits the ValueConverter class. But for the two generic types, we've provided nullable DateTime types for both the Model and Provider.

We are also checking whether the date has a value when providing the ExpressionFrom and ExpressionTo parameters and passing back the original instance if it doesn't.

// DateTimeUtcNullableConverter.cs
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;

public class DateTimeUtcNullableConverter
    : ValueConverter<DateTime?, DateTime?>
{
	public DateTimeUtcNullableConverter() : base(
		d => d.HasValue ? d.Value.ToUniversalTime() : d,
		d => d.HasValue ? DateTime.SpecifyKind(d.Value, DateTimeKind.Utc).ToLocalTime() : d)
	{

	}
}

If you want to apply it to all nullable DateTime properties in your DbContext, you can override the OnModelCreating method in the DbContext and check if the CLR type is a nullable DateTime.

// EfCoreDateTimeDbContext.cs
public class EfCoreDateTimeDbContext : DbContext
{
	...
	protected override void OnModelCreating(ModelBuilder modelBuilder)
	{
		var dateTimeUtcConverter = new DateTimeUtcConverter();
		var dateTimeUtcNullableConverter = new DateTimeUtcNullableConverter();

		foreach (var entityType in modelBuilder.Model.GetEntityTypes())
		{
			foreach (var property in entityType.GetProperties())
			{
				if (property.ClrType == typeof(DateTime))
				{
					property.SetValueConverter(dateTimeUtcConverter);
				}
				if (property.ClrType == typeof(DateTime?))
				{
					property.SetValueConverter(dateTimeUtcNullableConverter);
				}
			}
		}
	}
}

Watch the video

Watch our video where we show you how to add the DateTimeUtcConverter class to your DbContext and give you a demo on how it works. You'll have an understanding of how the date is stored in the database and how it's displayed in the application.