Jeremy Davis
Jeremy Davis
Sitecore, C# and web development
Article printed from: https://blog.jermdavis.dev/posts/2023/sitecore-container-timezone

Sitecore, time zones and containers

Wherever it pops up, time-based stuff is tricky

Published 22 May 2023

To customise a very old joke, there are only two difficult issues in IT: Naming things, Time calculations, and off-by-one errors. And adding containers into the mix raises even more fun. I recently hit an issue where containerised Sitecore needed to use a different time zone to the physical servers it was hosted on. So what can be done to configure this? Here's two things that can help:

The Sitecore config

The issue of server time zones has been around a fairly long time. So Sitecore does have some configuration to try and help here. Internally it tries to manage dates and times using UTC rather than a local timezone. But that data sometimes needs converting for display. So the sitecore.config file includes a setting named ServerTimeZone to manage this, which you can patch to your required location. For me, in England, that might be:

<?xml version="1.0"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
    <sitecore>
        <settings>
            <setting name="ServerTimeZone">
                <patch:attribute name="value">GMT Standard Time</patch:attribute>
            </setting>
        </settings>
    </sitecore>
</configuration>

					

The value for the setting needs to be the string name of one of the supported Windows time zones.

If you need to debug what state a particular server is seeing in your code then TimeZone.CurrentTimeZone is your friend. That has properties for fetching the name of the zone that's being used in the current context.

So this configuration can allow your servers (or containers) to run under UTC but have the UI display your local time zone automatically. But by default that only works for code which honours that setting. Sitecore does, but if you want to do so in your code then you need to make use of the Sitecore.DateUtil helper class, which will convert UTC based on this setting:

var now = DateTime.UtcNow;
var local = Sitecore.DateUtil.ToServerTime(now);

					

The containers

By default, a container picks up the time zone of its host. So the most wide-acting way to change the timezone for your code is to set the time zone on your host. But that's not always easy to do - in fact that's exactly the issue that got me thinking about this.. So you may need other approaches.

That's all well and good for code in Sitecore, or which obeys Sitecore's patterns. But what happens if you need code outside that scope to have a different time zone to a physical Kubernetes Node VM?

Well recent versions of Windows' container runtime have your back here. The time zone setting is virtualised between a Windows container and its host's operating system. So your nodes can be set to whatever, and your containers can run with your required setting. But it requires a little extra work.

Microsoft provide a tool called tzutil.exe which is present on the Windows Server base images. This lets you view and control the current timezone state. And if you run it inside a container, then it's controlling the state of that container, not the base server.

Running tzutil /g will show you the current timezone setting. tzutil /l will display the set of zones available to choose from. And tzutil /s <name> will let you set the zone for this machine.

It's worth noting that this command is mirrored by the PowerShell Get-Timezone and Set-Timezone commands, if you prefer to use those. That does make it easy to get lists of available zones sorted by name: Get-TimeZone -ListAvailable | Select-Object id, DisplayName | Sort-Object id or by time difference: Get-TimeZone -ListAvailable | Select-Object BaseUtcOffset, id | Sort-Object BaseUtcOffset if you need them.

The documentation from Microsoft on this suggests you need to start your container and then set the timezone, rather than embedding the setting into the image itself. Running tzutil in the DockerFile that creates your image doesn't affect the state of the container when it runs. (A nice little trap for the unwary - like me)

So how can we get this right for Sitecore?

Well we've got two situations to cover here: When you start the container as a developer, and when the container starts in a "production" deployment. So that means we need to cover both the Docker and Kubernetes container start processes. The important thing to remember here is that there are different entrypoint scripts for these two scenarios. So any solution needs to make sure it will run in both...

For the particular setup I was looking at, the default entrypoint is a script called RunW3SVCService.ps1 and while the Docker entrypoint is Developer.ps1 that script ends up calling the RunW3SVCService.ps1 script anyway - so that seems like the appropriate place to make the update. The first 20-odd lines of that script are functions, but the call to tzutil can be placed after that, before the definition of the $exclusionList for environment variables:

  ...
function Handle-ValuesWithQuotes {
    param(
        [string]$Value
    )
    $value = $Value -replace "'", "''"
    $value = $value -replace '"', """"
    
    return $value 
}

Write-Host "-- Setting time zone --"
tzutil /s "GMT Standard Time"

# List of env variables which are computer specific and should be excluded from transferred env variables
$exclusionList = @{
  ...

					

(You don't need the Write-Host here, of course - but it's helpful to have some feedback in the logs that this task has happened when the container starts)

The requirement I had needed the time zone to be set to the UK no matter where this server was running. But there might be circumstances where you want to pass in this name using an environment variable to make it configurable across different environments, instead of hard-coding it like this.

But either way, these techniques let you be specific about the zone you want.

↑ Back to top