Convert DateTime to user's time zone with server-side Blazor

  • Gérald Barré

When you display DateTime data to a user, you may want to convert the date to the user's time zone. With server-side Blazor, the code is executed on the server, so DateTime.Now corresponds to the time zone of the server instead of the user's time zone. Let's see how we can get the user's time zone and change the date accordingly.

To get the current time zone you need to execute code on the client machine. In a web browser, you can use the JS function getTimezoneOffset() to get the current offset from UTC in minutes. You cannot call this function directly from Blazor, so you need to create a JavaScript function to call it. Then, you can call this function from Blazor using JSInterop.

Add a JavaScript file named wwwroot/BlazorInterop.js with the following content:

function blazorGetTimezoneOffset() {
    return new Date().getTimezoneOffset();
}

Then, open the file Pages/_Host.cshtml and add a reference to the JavaScript file:

@page "/"
@namespace UserTimeZone.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@{
    Layout = null;
}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <base href="~/" />
</head>
<body>
    <app>
        <component type="typeof(App)" render-mode="ServerPrerendered" />
    </app>

    <script src="~/BlazorInterop.js"></script>
    <script src="_framework/blazor.server.js"></script>
</body>
</html>

We can now create a service that will handle the time zone adjustments. Add a new file named TimeZoneService.cs with the following content:

using System;
using System.Threading.Tasks;
using Microsoft.JSInterop;

namespace UserTimeZone
{
    public sealed class TimeZoneService
    {
        private readonly IJSRuntime _jsRuntime;

        private TimeSpan? _userOffset;

        public TimeZoneService(IJSRuntime jsRuntime)
        {
            _jsRuntime = jsRuntime;
        }

        public async ValueTask<DateTimeOffset> GetLocalDateTime(DateTimeOffset dateTime)
        {
            if (_userOffset == null)
            {
                int offsetInMinutes = await _jsRuntime.InvokeAsync<int>("blazorGetTimezoneOffset");
                _userOffset = TimeSpan.FromMinutes(-offsetInMinutes);
            }

            return dateTime.ToOffset(_userOffset.Value);
        }
    }
}

The next step is to declare the service in the dependency injection (DI) container. Open Startup.cs and add the following line:

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddRazorPages();
            services.AddServerSideBlazor();

            // We want one instance per client, so it's a scoped service.
            services.AddScoped<TimeZoneService>();
        }

Finally, we can use the service to display a date:

@page "/"
@inject TimeZoneService TimeZoneService

<h1>Current time</h1>

<p>Now (UTC):   @DateTimeOffset.UtcNow.ToString("T")</p>
<p>Now (local): @now.ToString("T")</p>

@code {
    DateTimeOffset now;

    protected override async Task OnInitializedAsync()
    {
        // TODO: Require server render mode while instantiating the component to execute JavaScript in OnInitializedAsync.
        // In _Host.cshtml: <component type="typeof(App)" render-mode="Server" />
        now = await TimeZoneService.GetLocalDateTime(DateTimeOffset.UtcNow);
    }
}

Do you have a question or a suggestion about this post? Contact me!

Follow me:
Enjoy this blog?Buy Me A Coffee