3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Azure Function(C#)の動作環境のタイムゾーンとWEBSITE_TIME_ZONEで変更できるか調査

Last updated at Posted at 2022-01-18

背景

掲題の件について、AppServiceのドキュメントでは以下のようにあります。

アプリケーション設定 WEBSITE_TIME_ZONE で指定ができる。設定値はOSで異なります。

さて、App Serviceの上で動作しているAzure Functionではどうなのかというと、同様にドキュメントがありますが、Linuxの従量課金はサポートしていないとのこと。

では確認してみましょう、というのがこの記事です。AzureではデフォルトのタイムゾーンはUTCになっているとはよく聞きますが、そこもついでに。

Azure Functionの環境情報

  • OS: Windows/Linux
  • ランタイム: .NET Core 3.1 (C#)
  • プラン: 従量課金/Premium
  • リージョン: 東日本

先に結果

ドキュメント通り、Linuxの従量課金のみWEBSITE_TIME_ZONEの設定が効かないことがわかりました。(2022/01/18)

デフォルトのタイムゾーンはUTCになっているようです。
WEBSITE_TIME_ZONE設定は、Windowsの従量課金、WindowsのPremium、LinuxのPremiumでできます。
またリージョンは関係ありませんでした。

Region OS Plan WEBSITE_TIME_ZONE "timezone(Id)"
東日本 Linux 従量課金 (なし) Etc/UTC
東日本 Linux 従量課金 Asia/Tokyo Etc/UTC
東日本 Windows 従量課金 (なし) UTC
東日本 Windows 従量課金 Tokyo Standard Time Tokyo Standard Time
東日本 Linux Premium (なし) Etc/UTC
東日本 Linux Premium Asia/Tokyo Asia/Tokyo
東日本 Windows Premium (なし) UTC
東日本 Windows Premium Tokyo Standard Time Tokyo Standard Time

検証

検証用コード

func newでC#用のHTTP Triggerのテンプレートを作り、少し書き換えてJSONを返すようにしました。

using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System.Collections.Generic;

namespace az_function_datetime
{
    public static class ViewDateTime
    {
        [FunctionName("ViewDateTime")]
        public static async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
            ILogger log)
        {
            log.LogInformation("C# HTTP trigger function processed a request.");

            var now = DateTime.Now;
            var utcNow = DateTime.UtcNow;
            var timezone = TimeZoneInfo.Local;

            var envWebsiteTimeZone = Environment.GetEnvironmentVariable("WEBSITE_TIME_ZONE") ?? "(none)";

            return new OkObjectResult(new Dictionary<string, object>{
                ["now"] = now,
                ["utcNow"] = utcNow,
                ["timezone"] = timezone,
                ["envWebsiteTimeZone"] = envWebsiteTimeZone
            });
        }
    }
}

func startでローカル実行(システム時間=日本)

{
  "now": "2022-01-18T13:17:10.6813074+09:00",
  "utcNow": "2022-01-18T04:17:10.681313Z",
  "timezone": {
    "Id": "Tokyo Standard Time",
    "DisplayName": "(UTC+09:00) 大阪、札幌、東京",
    "StandardName": "東京 (標準時)",
    "DaylightName": "東京 (夏時間)",
    "BaseUtcOffset": "09:00:00",
    "AdjustmentRules": null,
    "SupportsDaylightSavingTime": false
  }
}

まぁ、概ね期待通りの動作ですね。

各環境検証結果

このレスポンスをOS * Plan * 設定有無の8通りでレスポンスがどうなかったを調べたのが以下です。
思ったよりtimezoneinfoがノイズになってしまったので、gistにまとめました。結果は最初に書いた通りです。

そういえば、ランタイム毎の違いはあるんでしょうか?私はAzure FunctionではC#で生きていくと決めたので調査はしません・・・

蛇足: C#のDateTime型はめんどくさい

C#のDateTime型はtickと呼ばれる西暦0001年1月1日00:00:00からの100マイクロ秒単位の経過時間(62bit)と、DateTimeKind(2bit)で日時を表現します。

タイムゾーンやオフセットなどの情報は持っていないため、Kind値が異なるDateTime型同士は正しく比較できません。(DateTimeOffset型はオフセットがありオフセットを考慮した比較も可能です。)

その代わり、DateTimeKindで「2=Local(システム時間)」「1=UTC」「0=Unspecified(その他)」の3つの区分値を使い、何の時間かを表現しています。
DateTime.Nowで得られる日時は「Local」になります。DateTime.UtcNowで得られる日時は「Utc」になります。UTCはわかりますね。Localは何でしょうか。
例えばOSなどのシステムのタイムゾーンが日本時間なら、「Local」は日本時間と見なされ、UTC時間よりも9時間(32400秒)進んでいることになります。Kindが異なる場合はこの差を足し引きしてLocalとUtcを変換できるようになっています。
実際、DateTime.Now.Ticks - DateTime.UtcNow.Ticksとすると、約324000000000Tickが得られます。(同じ時間なはずですが、そういう仕組みになっています。)
このおかげでLocalかUTCでの間であれば、DateTime.ToUniversalTimeDateTime.ToLocalTimeを使うことで相互の変換は正しく行えます。

しかし時間はシステム時間とUTC時間だけではありません。
なので、TimeZoneInfoConvertTimeを使うことで任意のタイムゾーンの変換をサポートしています。あるタイムゾーンに変換を行うと、システムorUTC→あるタイムゾーンの時差の差を元にしてTicksを再計算し、新しいDateTimeを返します。しかし、その瞬間DateTimeKindはUnspecifiedになります。
そして、ここから先は自己責任の世界となり、責任をもって「あるタイムゾーンのDatetime」として運用しなければなりません。
このDateTimeをうっかり、Utc時間やLocal時間に変換すると予期しない結果になります。これを戻すには、またあるタイムゾーンを使ってTimezoneInfo.ConvertTimeToUtcで一旦Utc時間に変換することです。

こんな感じでC#のDateTimeは小難しい事情を抱えています。わかればなんてことはないんですが、DateTimeという1個の型みてそこまで見抜くのは至難の業だと思います。もしUnspecifiedなDateTimeを見かけたら不幸に思いましょう。

というわけで(Unspecifiedの下りは全く関係なかったですが)、C#の「システム時間」は動作環境によるため、Azure Functionでの挙動を調べてみたかったということでした。

C#のDateTimeに疲れたら、Nodatimeを使ってみるといいかもしれません。

(C#に限らず時間の扱いは言語、フォーマット、システムどれをとっても面倒なので別途まとめたい。素晴らしい記事は既にたくさんあるけども。)

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?