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 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);
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