Move to Cloud

Specifying timezone for Azure App Service

Quite often we still want to have our service running in a specific timezone instead of UTC.

Date and time are complex concepts and there are a lot of options to deal with them. Looking at .NET you have DateTime, DateTimeOffset, TimeSpan and TimeZoneInfo, there are third-party libraries like Noda Time and a few dozen ways to serialize or store these concepts. And while there are some ‘good practices’, there is always a reason to not follow them and do something else.

Your on-premises servers usually operate in your local timezone. This simplifies a lot of work when your user base and integrations are all local and defined in the same timezone. It also requires conversions, usually to UTC, when going more global and spanning multiple timezones.

Azure services, including App Service, run in UTC by default. If your software uses local time for any reason (storing dates, parsing csv files, …) then you’ll curse after migrating your workload to the cloud. There’s the option to start messing around with timezones in code to keep everything working, but you’re also able to explicitly tell Azure App Service to operate in a specific timezone.

There are several blog posts out there describing how to do this, a major part for Windows. I used one of them in the past when I was looking for this myself, and ‘it just works’. I was surprised today to see the question in my mailbox on how to do this. Even after I formulated a short response, this person was still having some issues, so I decided to write up this blog post to cover both Windows and Linux.

Validating with code

I can say what I want, but let’s write some code to verify our changes to the App Service. First create a new .NET Core / .NET 5+ Web API project. Add or replace an endpoint in the scaffolded controller to return a plain string containing some DateTime info.

[HttpGet]
public string GetTime()
{
    StringBuilder builder = new StringBuilder();

    // Get the local time zone and the current local time and year.
    var localZone = TimeZoneInfo.Local;
    DateTime currentDate = DateTime.Now;

    // Display the names for standard time and daylight saving 
    // time for the local time zone.
    builder.AppendLine($"Standard time name: {localZone.StandardName}");
    builder.AppendLine($"Daylight saving time name: {localZone.DaylightName}");

    // Display the current date and time and show if they occur 
    // in daylight saving time.
    builder.AppendLine($"Current date and time: {currentDate:F}");
    builder.AppendLine($"Daylight saving time? {localZone.IsDaylightSavingTime(currentDate)}");

    // Get the current Coordinated Universal Time (UTC) and UTC 
    // offset.
    builder.AppendLine($"Coordinated Universal Time: {currentDate.ToUniversalTime():F}");
    builder.AppendLine($"UTC offset: {localZone.GetUtcOffset(currentDate)}");

    return builder.ToString();
}

Calling this endpoint on your local machine returns something like this:

Standard time name: Romance Standard Time
Daylight saving time name: Romance Daylight Time
Current date and time: Monday, May 24, 2021 7:43:03 PM
Daylight saving time? True
Coordinated Universal Time: Monday, May 24, 2021 5:43:03 PM
UTC offset: 02:00:00

Deploying to Azure

Create an App Service (Web App in the Marketplace), pick the right version of the runtime stack (in my case .NET 5) and the operating system (Windows or Linux). Complete the rest of the required properties and create the resource. Finally deploy from Visual Studio to your new Azure App Service.

If you have never done this before and need more details, you can follow this detailed guide.

Update: I have added Deploy to Azure buttons to my sample GitHub repo which allows you to deploy all the necessary resources with a single click.

Once the code is deployed in Azure, you can hit your endpoint on the route defined by the controller and endpoint (in case you added a subpath). In my case I renamed the controller to TimeZoneController and with the default routing conventions that means I can test at https://timezonepoc.azurewebsites.net/timezone.

Standard time name: Coordinated Universal Time
Daylight saving time name: Coordinated Universal Time
Current date and time: Monday, May 24, 2021 5:45:11 PM
Daylight saving time? False
Coordinated Universal Time: Monday, May 24, 2021 5:45:11 PM
UTC offset: 00:00:00

As you can see we have a few differences:

  • We’re in the Coordinated Universal Time timezone.
  • This also means that UTC offset is 0 hours.
  • Note that this time standard is not adjusted for daylight saving time, even though our local time is.

Fixing our timezone

Windows

Go to the App Service in the Azure Portal and navigate to Settings|Configuration. On this page add a new Application Setting with the name WEBSITE_TIME_ZONE and as value the name of your desired timezone, e.g. Romance Standard Time for Belgium. You can find a full list of available time zones in your Windows Registry under HKLM\Software\Microsoft\Windows NT\CurrentVersion\Time Zones\ or the default time zones per country on the Microsoft docs where you take the timezone column with names.

Don’t forget to save the page to persist the new Application Setting and this will trigger a recycle of the web application (resulting in a cold load). Hitting the API endpoint again should now show the correct timezone info. Even though I did specify Romance Standard Time, it correctly takes daylight saving time into account.

Standard time name: Romance Standard Time
Daylight saving time name: Romance Daylight Time
Current date and time: Monday, May 24, 2021 7:48:00 PM
Daylight saving time? True
Coordinated Universal Time: Monday, May 24, 2021 5:48:00 PM
UTC offset: 02:00:00

Linux

The process for a Linux App Service is very similar by adding a WEBSITE_TIME_ZONE Application Setting. But since your API is now running on a Linux host, it will not recognize Windows timezones. The correct values for Linux come from the tz database. From this list, take the ‘TZ database name’ column, e.g. Europe/Brussels for Belgium.

Standard time name: Central European Standard Time
Daylight saving time name: Central European Summer Time
Current date and time: Monday, 24 May 2021 19:56:56
Daylight saving time? True
Coordinated Universal Time: Monday, 24 May 2021 17:56:56
UTC offset: 02:00:00

Note here that since we’re on a Linux host, we’re not getting Romance Standard Time but Central European Standard Time as the name for the standard time. The DateTime formatting with the format string we provided is also slightly different.

Fun fact: this is the correct timezone name and is also the redirect target for Romance Standard Time on Wikipedia.

You can find this small API to test out your Azure App Service’s timezone in this GitHub repo.

Licensed under CC BY-NC-SA 4.0; code samples licensed under MIT.
Last updated on Oct 10, 2021 20:29 UTC
comments powered by Disqus
Built with Hugo - Based on Theme Stack designed by Jimmy