UK South の Premium App Service Plan(専用 VM)で動かしているのに、日付や数値の表示が en‑US のまま変わらず困っていませんか。結論から言うと、OS レベルのカルチャは変更できません。変えられるのはアプリ単位のタイムゾーンだけです。本稿では誤解しやすい仕様の整理、具体的な対処(.NET/Node.js/Java/Python/インフラ定義)、そして運用で事故を防ぐ設計指針までを実務目線で深掘りします。
前提と結論:Premium(専用 VM)でも OS ロケールは固定
Premium App Service Plan は「専用のワーカー VM(Dedicated)」で動作しますが、これはユーザーが OS を自由に構成できることを意味しません。App Service の OS は Azure が共通イメージで管理しており、既定カルチャは en‑US、システム時刻は UTC です。Windows/Linux いずれのワーカーでも、コントロール可能なのはアプリ単位のタイムゾーンのみで、OS レベルの地域設定(カルチャ、日付書式、小数点や通貨記号)は変更できません。
UK South を選んでもカルチャは UK にならない理由
リージョン(例:UK South)は、あくまで物理的配置・ネットワーク遅延・データ所在地などに関わる属性であり、App Service の OS カルチャを左右しません。Premium で専用 VM を確保できても、OS ロケールはサービスの一貫性のため Azure 側で固定されます。
何が変更できて、何ができないのか
| 対象 | 可否 | 設定方法 | 備考 |
|---|---|---|---|
| OS のカルチャ(en‑GB/en‑US 等) | 不可 | ― | Premium でも固定。小数点・通貨記号・日付書式は既定で en‑US 準拠。 |
| アプリのタイムゾーン | 可 | 環境変数 WEBSITE_TIME_ZONE(Windows: 例「GMT Standard Time」 / Linux: 例「Europe/London」) | DateTime.Now などのオフセットが変わる。カルチャは変わらない。 |
| アプリのカルチャ(表示・解析) | 可 | アプリコードで明示(例:.NET の CultureInfo) | UI、API、ログ、DB I/O すべてで明示を徹底する。 |
| 作成時にロケール指定 | 不可 | ― | Portal/ARM/Bicep/Terraform にロケール指定プロパティなし(執筆時点)。 |
タイムゾーンは変えられるが、カルチャは変わらない
UK のオフセットだけ合わせたい場合は、Web アプリのアプリケーション設定に WEBSITE_TIME_ZONE を追加します。これで DateTime.Now(.NET)や TimeZoneInfo.Local の基準が UK になります。ただし日付の文字列表現や数値のフォーマットは en‑US のままなので、表示・解析は必ずアプリ側でカルチャを明示してください。
Windows と Linux での指定値の違い
| ワーカー種別 | 推奨キー | 設定値の例 | 注意点 |
|---|---|---|---|
| App Service(Windows) | WEBSITE_TIME_ZONE | GMT Standard Time | Windows のタイムゾーン ID を使用。英国の夏時間(BST)も自動反映。 |
| App Service(Linux・内蔵ランタイム) | WEBSITE_TIME_ZONE | Europe/London | IANA(tzdb)形式を使用。イメージに tzdata が必要な場合あり。 |
| App Service(Linux・コンテナ) | TZ または WEBSITE_TIME_ZONE | Europe/London | コンテナ側で tzdata をインストール。ベース OS に依存。 |
アプリ側でのカルチャ制御(実装例)
UK(en‑GB)の書式とルールで動かしたいなら、プロセス既定カルチャと UI カルチャの両方を明示します。さらに、外部とのデータ交換は ISO‑8601(UTC)で統一すると事故が激減します。
.NET 6+(ASP.NET Core)の推奨実装
// Program.cs
using System.Globalization;
using Microsoft.AspNetCore.Localization;
var builder = WebApplication.CreateBuilder(args);
// 1) 既定カルチャを en-GB に固定
var uk = CultureInfo.GetCultureInfo("en-GB");
CultureInfo.DefaultThreadCurrentCulture = uk;
CultureInfo.DefaultThreadCurrentUICulture = uk;
// 2) リクエストローカリゼーション(将来の多言語化にも備える)
builder.Services.Configure(options =>
{
options.DefaultRequestCulture = new RequestCulture(uk);
options.SupportedCultures = new[] { uk };
options.SupportedUICultures = new[] { uk };
});
var app = builder.Build();
app.UseRequestLocalization();
// 3) Date/Number formatting は ToString("O") や明示的フォーマッタに集約
// 例: ISO-8601 でログ・API・DB へ
// dt.ToString("O", CultureInfo.InvariantCulture); // 2025-01-23T12:34:56.789Z
app.MapGet("/", () =>
{
var now = DateTimeOffset.Now;
return new
{
now_local = now.ToString("O", CultureInfo.InvariantCulture),
sample_currency = (1234.56m).ToString("C", uk),
currentCulture = CultureInfo.CurrentCulture.Name,
currentUICulture = CultureInfo.CurrentUICulture.Name
};
});
app.Run();
.NET(非 Web / 既存フレームワーク)
var uk = new System.Globalization.CultureInfo("en-GB");
System.Globalization.CultureInfo.CurrentCulture = uk;
System.Globalization.CultureInfo.CurrentUICulture = uk;
// スレッド生成が多い場合は DefaultThreadCurrentCulture 系を推奨
Node.js(フル ICU 前提)
Node.js はグローバル既定カルチャの切替が弱いため、toLocaleString 等で毎回ロケールを明示します。金額や日付のフォーマットは専用ヘルパに集約しましょう。
// 共通フォーマッタ(en-GB)
export const fmtDate = (d) => new Date(d).toLocaleString("en-GB", {
year: "numeric", month: "2-digit", day: "2-digit",
hour: "2-digit", minute: "2-digit", second: "2-digit",
hour12: false, timeZone: "Europe/London" // サーバーの TZ に依存しない
});
export const fmtMoney = (n) => new Intl.NumberFormat("en-GB", {
style: "currency", currency: "GBP", minimumFractionDigits: 2
}).format(n);
// 例
console.log(fmtDate(Date.now()));
console.log(fmtMoney(1234.56));
Java
import java.time.*;
import java.time.format.*;
import java.util.*;
public class LocaleUk {
public static void main(String[] args) {
// 既定ロケール/タイムゾーンを UK に固定
Locale.setDefault(Locale.UK);
TimeZone.setDefault(TimeZone.getTimeZone("Europe/London"));
ZonedDateTime now = ZonedDateTime.now(ZoneId.of("Europe/London"));
DateTimeFormatter f = DateTimeFormatter.ofPattern("dd/MM/uuuu HH:mm:ss").withLocale(Locale.UK);
System.out.println(now.format(f)); // 31/01/2025 23:59:59
System.out.println(OffsetDateTime.now(ZoneOffset.UTC)); // ISO-8601
}
}
Python
Python の locale は OS に依存します。Linux コンテナでは en_GB.UTF-8 を、Windows ワーカーでは English_United Kingdom.1252 を指定する必要があります。可搬性のため、画面表示のみロケール、I/O は ISO‑8601 と Invariant を推奨します。
import locale
from datetime import datetime, timezone
# 画面用(失敗したらフォールバック)
for cand in ["en_GB.UTF-8", "English_United Kingdom.1252", "en_GB"]:
try:
locale.setlocale(locale.LC_ALL, cand)
break
except locale.Error:
pass
print(datetime.now().strftime("%d/%m/%Y %H:%M:%S")) # 表示
print(datetime.now(timezone.utc).isoformat()) # I/O
インフラ定義:Portal / ARM / Bicep / Terraform の設定例
Azure Portal(Windows ワーカーの例)
- 対象 Web アプリ > 構成 > アプリケーション設定。
- 新規追加:
WEBSITE_TIME_ZONE=GMT Standard Time。 - 保存後にアプリを再起動。
ARM テンプレート(抜粋)
{
"type": "Microsoft.Web/sites",
"apiVersion": "2022-09-01",
"name": "[parameters('siteName')]",
"location": "[resourceGroup().location]",
"properties": {
"siteConfig": {
"appSettings": [
{ "name": "WEBSITE_TIME_ZONE", "value": "GMT Standard Time" }
]
}
}
}
Bicep(抜粋)
resource site 'Microsoft.Web/sites@2022-09-01' = {
name: siteName
location: resourceGroup().location
properties: {
siteConfig: {
appSettings: [
{ name: 'WEBSITE_TIME_ZONE', value: 'GMT Standard Time' }
]
}
}
}
Terraform(Windows / Linux)
# Windows ワーカー
resource "azurerm_windows_web_app" "app" {
name = "uk-web"
resource_group_name = azurerm_resource_group.rg.name
location = azurerm_resource_group.rg.location
service_plan_id = azurerm_service_plan.asp.id
app_settings = {
WEBSITE_TIME_ZONE = "GMT Standard Time"
}
}
# Linux ワーカー(内蔵ランタイム)
resource "azurerm_linux_web_app" "app" {
name = "uk-web-linux"
resource_group_name = azurerm_resource_group.rg.name
location = azurerm_resource_group.rg.location
service_plan_id = azurerm_service_plan.asp_linux.id
app_settings = {
WEBSITE_TIME_ZONE = "Europe/London"
}
}
カルチャを上書きし忘れたときの障害パターン
| 症状 | 原因 | 対策 |
|---|---|---|
| 「01/02/2025」を 2 月 1 日と解釈してしまう | 既定カルチャが en‑US(MM/dd/yyyy) | ParseExact("dd/MM/yyyy") を使用、または en-GB を明示 |
| £ 記号が $ として表示される / 通貨桁区切りが「,」と「.」で逆 | 数値フォーマットが en‑US のまま | ToString("C", CultureInfo("en-GB")) / Intl.NumberFormat("en-GB",{currency:"GBP"}) |
| サマリー CSV / ログを Excel で開くと日付が壊れる | カルチャ依存の短い日付書式 | ISO‑8601(UTC)で出力、列に型を付ける、ヘッダーに明示 |
| 時差が 1 時間ずれる | 夏時間(BST)未考慮 | GMT Standard Time または Europe/London を使用し DST を自動反映 |
設計ガイドライン:事故を「仕組み」で防ぐ
- 不変ルール 1:保存と通信は UTC・ISO‑8601
DB、メッセージ、ログ、外部 API では UTC のyyyy-MM-ddTHH:mm:ss.fffZを使用。タイムゾーンは表示直前に適用。 - 不変ルール 2:UI と表示はカルチャ明示
金額・日付はロケールを引数で指定するヘルパに集約(重複コードを禁止)。 - 不変ルール 3:パースは常に厳格
ParseExact/TryParseExactを使用し、期待フォーマットを固定。失敗時のエラーハンドリングも標準化。 - 不変ルール 4:テストデータは曖昧日付を含める
「01/02/2025」(日付と月の逆転ケース)、「29/03/2025 01:30」(BST 切替日時)などを必ず含める。 - 不変ルール 5:アプリ起動でカルチャを 1 回だけ設定
フレームワークのブートストラップで既定カルチャを設定し、スレッド生成・非同期コンテキストでも継承されるようにする。
ミドルウェア/ヘルパのサンプル(ASP.NET Core)
public sealed class UkCultureMiddleware
{
private readonly RequestDelegate _next;
private static readonly CultureInfo Uk = CultureInfo.GetCultureInfo("en-GB");
public UkCultureMiddleware(RequestDelegate next) => _next = next;
public async Task Invoke(HttpContext context)
{
var originalCulture = CultureInfo.CurrentCulture;
var originalUi = CultureInfo.CurrentUICulture;
try
{
CultureInfo.CurrentCulture = Uk;
CultureInfo.CurrentUICulture = Uk;
await _next(context);
}
finally
{
CultureInfo.CurrentCulture = originalCulture;
CultureInfo.CurrentUICulture = originalUi;
}
}
}
// Program.cs
app.UseMiddleware();
SQL とフォーマットの落とし穴
- SQL パラメータを使う:文字列連結で日付を渡さない。
- スタイル 126(ISO)を活用:
CONVERT(varchar(33), GETUTCDATE(), 126)など。 - Always UTC:DB は常に UTC で格納。表示の層でローカル化。
ログと監査のベストプラクティス
- タイムスタンプ:UTC(ミリ秒)+リクエスト ID。例:
2025-02-01T12:34:56.789Z req=... user=... - カルチャのメタ情報:ログの先頭に
culture=en-GB、tz=Europe/Londonを出力。 - 構造化ログ:数値は
.、日時は ISO‑8601。Excel 前提の CSV を避け、JSON/NDJSON を基本に。
App Service(Windows / Linux / コンテナ)別の実務的勘所
| 形態 | タイムゾーン | カルチャ | 注意点 |
|---|---|---|---|
| Windows(内蔵スタック) | WEBSITE_TIME_ZONE(例:GMT Standard Time) | アプリで明示(en-GB) | Kudu/コンソールで tzutil /g は参照用。OS ロケール自体は不可変。 |
| Linux(内蔵スタック) | WEBSITE_TIME_ZONE(例:Europe/London) | アプリで明示(en-GB) | イメージによっては tzdata の有無を確認。 |
| Linux(コンテナ) | TZ または WEBSITE_TIME_ZONE | アプリで明示(en-GB) | Dockerfile で RUN apk add --no-cache tzdata 等を追加。 |
よくある Q&A
アプリをスロットにデプロイしたらタイムゾーンが戻った
スロットごとにアプリ設定が独立します。WEBSITE_TIME_ZONE を本番・ステージング両方に設定してください。
WEBSITE_TIME_ZONE を設定したのに DateTime.Now が変わらない
アプリの再起動が必要です。スケール操作(イン/アウト)後に新規ワーカーが割り当てられた場合も、適用状態を確認しましょう。
カルチャをコントローラで設定しているのに背景処理で壊れる
バックグラウンドジョブ(Queue/Timer/HostedService など)は Web 要求のパイプライン外で動くため、起動時に既定カルチャを設定してください。スレッド単位での上書きは漏れやすく、重大な不整合を招きます。
検証用スクリプト(C#):UK 設定が本当に効いているか
Console.WriteLine($"Local: {DateTimeOffset.Now:O}");
Console.WriteLine($"UTC : {DateTimeOffset.UtcNow:O}");
Console.WriteLine($"TZ : {TimeZoneInfo.Local.Id}");
Console.WriteLine($"Culture: {System.Globalization.CultureInfo.CurrentCulture.Name} / UI: {System.Globalization.CultureInfo.CurrentUICulture.Name}");
期待する出力例(Windows ワーカー):TZ: GMT Standard Time、Culture: en-GB / UI: en-GB(カルチャはアプリ側で設定した場合)。
運用チェックリスト
- Portal 構成:
WEBSITE_TIME_ZONEをスロット含めて設定済みか。 - アプリ起動時:既定
CultureInfoを en‑GB にしているか。 - フォーマット:表示は
en-GB、I/O はInvariant + ISO‑8601を徹底しているか。 - テスト:夏時間切替と曖昧日付を回帰テストに含めているか。
- ログ:
cultureとtzをメタとして出力しているか。
まとめ
OS レベルの地域設定は固定で変更不可。Premium(専用 VM)でもこの原則は変わりません。変更できるのはタイムゾーンのみで、WEBSITE_TIME_ZONE により UK の時差・夏時間を正しく反映できます。ただし日付書式・数値・通貨などのカルチャはアプリで明示しなければなりません。特に I/O は ISO‑8601(UTC)へ統一し、UI と解析だけを en‑GB にする設計が最も堅牢です。作成時にロケール指定プロパティは提供されていないため、アプリ層でのカルチャ制御が唯一かつ確実な対策になります。
付録:サンプル方針テンプレート
// 1) インフラ
// - WEBSITE_TIME_ZONE= (Windows: "GMT Standard Time" / Linux: "Europe/London")
// - スロットにも同一設定
// 2) アプリ(起動時)
// - CultureInfo.DefaultThreadCurrentCulture = en-GB
// - CultureInfo.DefaultThreadCurrentUICulture = en-GB
// - RequestLocalization を有効化(ASP.NET Core)
// 3) フォーマット規約
// - 表示: en-GB
// - I/O(保存/通信/ログ): ISO-8601 (UTC, "O")
// - ParseExact/TryParseExact 強制
// 4) テスト
// - 01/02/2025, 29/03/2025 01:30 などのケース
// - スロット切替/スケール操作後の再検証
// 5) 監査
// - culture=en-GB, tz=Europe/London をログに明記
本稿の方針を適用すれば、「UK South なのに en‑US で動いてしまう」問題に対し、再発しない形で根本から統制できます。特に、カルチャはアプリで設定、I/O は不変の ISO‑8601、タイムゾーンはアプリ設定で制御の三点をチーム標準に落とし込みましょう。
FAQ(追加の実務 Tips)
関数アプリ(Functions)でも同じですか?
同様です。WEBSITE_TIME_ZONE の扱いは App Service と同様で、OS ロケールは変更不可。トリガーの時刻解釈は WEBSITE_TIME_ZONE に依存するため、ジョブの実行時刻に注意してください。
WebJobs / バックグラウンドプロセスは?
同じワーカー上で動作するため、WEBSITE_TIME_ZONE の影響を受けます。ただし、カルチャは各プロセスで明示が必要です。
ユーザーのブラウザ言語が多様な場合はどうする?
サーバー既定は en‑GB としつつ、UI は Accept-Language に応じて切替可能にします。サーバー内の書式や I/O 規約は不変に保ちましょう。
「GMT Standard Time」と「UTC」の違いは?
GMT Standard Time は英国のタイムゾーン ID(DST を含む)で、夏季は UTC+1(BST) になります。UTC は常にオフセット 0、DST なしです。ビジネス要件が「英国の営業時間に合わせる」なら前者を、ロギングや監査など世界共通で扱う値には後者を用います。
結論の再掲
- OS レベルの地域設定は固定で変更不可。
- タイムゾーンだけは
WEBSITE_TIME_ZONEで調整可能。 - カルチャ差異はアプリ層で明示的に設定して吸収する。
- I/O は ISO‑8601(UTC)を不変の規約にする。

コメント