Standardize DateTime Handling To UTC In Dot Net Core 9
Osama Nasir
Senior Dot Net Developer | Back-End Developer | .NET | ASP.NET CORE | ASP.NET MVC | React | Entity Framework | Angular | SQL | Azure | AWS
Use UTC in Your Models
Modify your model properties to always store and retrieve DateTime in UTC:
public class Order
{
public int Id { get; set; }
private DateTime _orderDate;
public DateTime OrderDate
{
get => _orderDate; // Always return in UTC
set => _orderDate = DateTime.SpecifyKind(value, DateTimeKind.Utc).ToUniversalTime();
}
}
Best Practices
? Use DateTime.UtcNow instead of DateTime.Now in your application logic:
DateTime nowUtc = DateTime.UtcNow;
? Convert user-supplied dates to UTC before processing:
var userInput = DateTime.Parse("2025-03-10T15:30:00"); var utcTime = DateTime.SpecifyKind(userInput, DateTimeKind.Utc).ToUniversalTime();
?? Why? This ensures that all dates in your application are internally stored and used in UTC.
2?? Force UTC in Entity Framework (Data Layer)
Use EF Core interceptors or hooks to automatically convert all DateTime fields to UTC before saving them in the database.
Using EF Core SaveChanges() Hook
Modify AppDbContext to convert all DateTime properties to UTC before saving:
public class AppDbContext : DbContext
{
public override int SaveChanges()
{
ConvertDatesToUtc();
return base.SaveChanges();
}
public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
{
ConvertDatesToUtc();
return base.SaveChangesAsync(cancellationToken);
}
private void ConvertDatesToUtc()
{
foreach (var entry in ChangeTracker.Entries())
{
foreach (var property in entry.Properties)
{
if (property.Metadata.ClrType == typeof(DateTime) && property.CurrentValue != null)
{
property.CurrentValue = DateTime.SpecifyKind((DateTime)property.CurrentValue, DateTimeKind.Utc).ToUniversalTime();
}
}
}
}
}
will now be stored in UTC format, regardless of how developers enter the data.
3?? Enforce UTC for API Requests & Responses (Presentation Layer)
When working with Web APIs, you need to ensure that all DateTime values sent/received are in UTC.
Use a Global JSON DateTime Converter
Create a custom JsonConverter to serialize and deserialize DateTime in UTC:
public class UtcDateTimeConverter : JsonConverter<DateTime>
{
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return DateTime.SpecifyKind(reader.GetDateTime(), DateTimeKind.Utc);
}
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToUniversalTime().ToString("o")); // ISO 8601 format
}
}
Register it in Program.cs:
builder.Services.AddControllers() .AddJsonOptions(options => { options.JsonSerializerOptions.Converters.Add(new UtcDateTimeConverter()); });
? Ensures that all API responses return DateTime in UTC ? Ensures that all incoming requests automatically convert dates to UTC
4?? Global Middleware to Enforce UTC (Middleware Layer)
You can create a custom middleware that converts all DateTime properties in API requests to UTC.
public class UtcEnforcementMiddleware
{
private readonly RequestDelegate _next;
public UtcEnforcementMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
if (context.Request.HasJsonContentType())
{
context.Request.EnableBuffering();
var body = await new StreamReader(context.Request.Body).ReadToEndAsync();
context.Request.Body.Position = 0;
if (!string.IsNullOrEmpty(body) && body.Contains("DateTime"))
{
// Convert all DateTime fields to UTC
body = Regex.Replace(body, @"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{3,7})?Z?",
match => DateTime.Parse(match.Value).ToUniversalTime().ToString("o"));
}
}
await _next(context);
}
}
Register it in Program.cs:
app.UseMiddleware<UtcEnforcementMiddleware>();
? Ensures that all API requests containing DateTime fields automatically get converted to UTC
5?? Enforce UTC in SQL Server (Database Layer)
If you're using SQL Server, you can store all DateTime values in UTC format by default.
Set Default UTC Column Values
ALTER TABLE Orders ADD CONSTRAINT DF_Orders_CreatedAt DEFAULT GETUTCDATE() FOR CreatedAt;
? Ensures database-generated timestamps (like CreatedAt) are always in UTC
6?? Time Zone Handling for Users
If your application supports users in different time zones, store everything in UTC and convert to the user’s local time when displaying data.
Convert UTC to Local Time for Display
public static DateTime ConvertUtcToLocal(DateTime utcDate, string timeZoneId) { var timeZone = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId); return TimeZoneInfo.ConvertTimeFromUtc(utcDate, timeZone); }
? Stores data in UTC but converts for user display
Summary of Best Approaches
?? Domain Layer – Use DateTime.UtcNow ? Ensures UTC usage in business logic ? Requires manual enforcement in every instance
?? Data Layer – EF Core SaveChanges Hook ? Automatically converts DateTimes before saving ? Works only with EF Core
?? Presentation Layer – Custom JSON DateTime Converter ? Ensures API request/response is always in UTC ? Does not enforce UTC storage in the database
?? Middleware Layer – UTC Enforcement Middleware ? Converts all incoming and outgoing DateTimes automatically ? Slightly complex implementation
?? Database Layer – SQL Server GETUTCDATE() ? Ensures all database timestamps are in UTC ? Does not enforce UTC in API processing
Best Approach for a Large-Scale Application