気まま研究所ブログ

ITとバイク、思ったことをてきとーに書きます。

Docker Ubuntu環境のASP.Netでタイムゾーンが取れない問題

自宅のサーバーを入れ替える際についでにDockerに全部入れちゃえと思い必死の思いで移行したと思えばまさかのASP.Netでタイムゾーンが取得できない罠にドハマリすることに。
あんまり情報が無かったので詰まったものの、結果的にICUとtzdataのパッケージが不足していただけでした。

現状

C#のコードでタイムゾーンを取得する際にFindSystemTimeZoneByIdメソッドを使うと思います。

TimeZoneInfo.FindSystemTimeZoneById("Asia/Tokyo");

しかしながら、DockerのUbuntuを使うとこれが動きません。

The time zone ID 'Asia/Tokyo' was not found on the local computer.

と言われて処理が止まります。
じゃあ逆にTokyo Standard Timeなら?と思うとこれもまたダメ。
どうやらタイムゾーンIDが間違っているというより、タイムゾーンの情報自体がシステムから取得できないようです。
素のUbuntuでは普通に動くので全く持って意味が不明。

解決策

先に解決策を。 執筆当時で.Net 8以降のバージョンならUbuntuなど使わずに公式のイメージを使うほうが楽です。

FROM mcr.microsoft.com/dotnet/aspnet:8.0

しかしながら、.Netはサポート終了と同時にdockerhubから消えてしまうのかそのうち取得できなくなるので、例えば現時点で.Net 6はサポートが切れてて取得できません。
なので.Net 6等を使うためにUbuntuベースでイメージを作る場合は最低限のパッケージしか入っていないので以下のlibicuとtzdataパッケージのインストールが必要です。

FROM ubuntu:24.04

RUN apt update -y
RUN apt install -y libicu74
RUN DEBIAN_FRONTEND=noninteractive apt install -y tzdata

...

libicuはUbuntuバージョンで変わるので、以下を参考にインストールしてください。
見つからないときはapt-cache search libicuで。
Ubuntu 22.04: libicu70
Ubuntu 24.04: libicu74
Ubuntu 25.04: libicu76

原因と究明

一番初めはDocker ComposeにてUbuntu 24.04でASP.Netのアプリケーションをセットアップしたところ何故か起動できなくて、別のアプリで使っていた環境変数「DOTNET_SYSTEM_GLOBALIZATION_INVARIANT 1」をつけて起動していました。

しかしながら、DOTNET_SYSTEM_GLOBALIZATION_INVARIANT 1はカルチャデータが使えないのでタイムゾーンは当然取れません。

じゃあ「DOTNET_SYSTEM_GLOBALIZATION_INVARIANT 0」にしたところ

ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT 0
Process terminated. Couldn't find a valid ICU package installed on the system. Please install libicu (or icu-libs) using your package manager and try again. Alternatively you can set the configuration flag System.Globalization.Invariant to true if you want to run with no globalization support. Please see https://aka.ms/dotnet-missing-libicu for more information.

と怒られてアプリケーションが起動しません。
要するにICUパッケージを入れろと言われているので指示通りにICUを入れてみる。

RUN apt install -y libicu74

すると今度はアプリケーションの起動はするものの、タイムゾーンを取得するコードを踏むとAsia/Tokyoが無いと言われます。

The time zone ID 'Asia/Tokyo' was not found on the local computer.

公式ページを見るとどうやらICUの他にtzdataが必要なよう
しかしながらDockerのUbuntuイメージには最小限しか入っていないためかどうやらICUもtzdataも入っていません。
なのでtzdataを入れてみます。

RUN DEBIAN_FRONTEND=noninteractive apt install -y tzdata

tzdataは対話式に設定を行うのでビルド時に止まってしまうので対話を制限するDEBIAN_FRONTEND=noninteractiveが必要です。
そのままビルドしてコンテナを再作成するとタイムゾーンが取れました。

ここでの一番のハマりポイントはdocker compose execで入れてしまうのので試しにtzdataをインストールしても何も解決しないところ。
どうやらアプリケーション起動時にtzdataのタイムゾーンを読み込むようなので、docker compose execで中にはいってtzdataをインストールしてもうまく動作しません