Azure App Service PremiumでUKのローカル日付・言語設定が反映されない問題の完全対策|WEBSITE_TIME_ZONEとen‑GBカルチャの正しい設定

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_ZONEGMT Standard TimeWindows のタイムゾーン ID を使用。英国の夏時間(BST)も自動反映。
App Service(Linux・内蔵ランタイム)WEBSITE_TIME_ZONEEurope/LondonIANA(tzdb)形式を使用。イメージに tzdata が必要な場合あり。
App Service(Linux・コンテナ)TZ または WEBSITE_TIME_ZONEEurope/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 ワーカーの例)

  1. 対象 Web アプリ > 構成 > アプリケーション設定。
  2. 新規追加:WEBSITE_TIME_ZONE = GMT Standard Time。
  3. 保存後にアプリを再起動。

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)を不変の規約にする。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次