日付の扱い
サーバースクリプトで model から取得した日付(DateTime 型)は、実行環境やユーザー設定に関係なく UTC になります。一方、日付を書き込むときは実行ユーザーのタイムゾーンの値をセットする必要があります。このページでは、その理由と、取得した日付をまとめて日本時間に変換する方法を紹介します。
タイムゾーンの仕組み全体(DB・API・混在環境)は タイムゾーンの考え方 で詳しく扱っています。
取得した日付が UTC になる理由
サーバースクリプトの実行には ClearScript が使われており、.NET の DateTime と JavaScript の Date は EnableDateTimeConversion フラグで自動変換されます。この変換は双方向ですが情報が落ちるもので、ClearScript のリファレンスには次のように書かれています。
Specifies that the script engine is to perform automatic conversion between .NET DateTime objects and JavaScript Date objects. This conversion is bidirectional and lossy. A DateTime object constructed from a JavaScript Date object always represents a Coordinated Universal Time (UTC) and has its Kind property set to Utc.
どこでどのタイムゾーンになるか
| 実行環境 | データベース | ユーザー設定 | API Get/Set | スクリプト Get/Set | サーバースクリプト Get | サーバースクリプト Set |
|---|---|---|---|---|---|---|
| JST | JST | JST | JST | JST | UTC | JST |
| UTC | UTC | JST | JST | JST | UTC | JST |
- API Get/Set のタイムゾーンは、API キーに紐づくユーザーのタイムゾーンと同じです。
- スクリプト Get/Set とサーバースクリプト Set のタイムゾーンは、実行したユーザーのタイムゾーンと同じです。
日本向けに運用している場合、サーバースクリプトで取得した日付だけが UTC になるため、使う前に JST へ変換する必要があります。
model の日付をまとめて JST に変換する
model を直接書き換えるのではなく、日付系のプロパティだけを変換して model_jst という別の変数に入れます。条件は「画面表示の前」や「行表示の前」に設定します。
const regex = new RegExp('(^Date|Time$)');
let model_jst = {};
for (const [key, value] of Object.entries(model)) {
if (regex.test(key) && value) {
model_jst[key] = value.toLocaleString("ja-JP", {
timeZone: "JST"
});
}
}DateA〜DateZ、Date001〜Date999、UpdatedTime、CreatedTime のうち、画面に表示されているもの(= model に含まれているもの)だけが model_jst にセットされます。表示されていない項目も扱いたい場合は、view.AlwaysGetColumns で対象を追加すればそのまま使えます。
context.Log で変換前後を出力すると次のようになります。
model:CreatedTime/Thu Dec 05 2024 06:19:08 GMT+0000 (Coordinated Universal Time)
model:UpdatedTime/Thu Dec 05 2024 06:19:08 GMT+0000 (Coordinated Universal Time)
model:DateY/Wed Dec 04 2024 15:00:00 GMT+0000 (Coordinated Universal Time)
model:DateZ/Wed Dec 04 2024 15:00:00 GMT+0000 (Coordinated Universal Time)
model:Date001/Wed Dec 04 2024 15:00:00 GMT+0000 (Coordinated Universal Time)
model:Date002/Wed Dec 04 2024 15:00:00 GMT+0000 (Coordinated Universal Time)
model_jst:CreatedTime/2024/12/5 15:19:08
model_jst:UpdatedTime/2024/12/5 15:19:08
model_jst:DateY/2024/12/5 0:00:00
model_jst:DateZ/2024/12/5 0:00:00
model_jst:Date001/2024/12/5 0:00:00
model_jst:Date002/2024/12/5 0:00:00拡張サーバースクリプトで全サイトに適用する
サイトごとに追加する代わりに、拡張サーバースクリプトとして組み込むこともできます。
{
"Name": "modelのタイムゾーンを変換する",
"BeforeOpeningPage": true,
"BeforeOpeningRow": true,
"Body": "// Write an arbitrary script."
}let model_jst = {};
let success_model_jst = false;
//エラーが出ると面倒なのでtry-catchで囲っておく
try {
const regex = new RegExp('(^Date|Time$)');
for (const [key, value] of Object.entries(model)) {
if (regex.test(key) && value) {
model_jst[key] = value.toLocaleString("ja-JP", {
timeZone: "JST"
});
}
}
success_model_jst = true;
} catch(e) {
success_model_jst = false;
context.Error(e.stack);
}拡張サーバースクリプトでエラーが出ると処理全体が止まるため、try-catch で囲んでいます。catch で受け取る JavaScript のエラーオブジェクトのスタックトレースは小文字の e.stack です(e.Stack と書くと undefined になり、エラーメッセージが空になります)。context.Error() はメッセージを受け取ってエラーを設定するメソッドです(ServerScriptModelContext.cs)。変換が成功したかどうかは success_model_jst(true / false)で判定できます。
異なるタイムゾーンのユーザーが混在する場合
context.UserId で実行ユーザーの ID を取得し、API の users/get でそのユーザーの TimeZone と Language を取得すれば対応できます(サーバースクリプトに user というオブジェクトは無いため、user.UserId と書くと動きません)。ただしロケールはプリザンターでは管理していないため、Language から推定してセットする必要があります。
実行ユーザーの言語とタイムゾーンは、API を使わずに context.Language と context.TimeZoneInfo でも取得できます。確認したソースでは、context.TimeZoneInfo は .NET の TimeZoneInfo を ToString() した表示名((UTC+09:00) ... の形式)で、toLocaleString の timeZone にそのまま渡せる IANA 形式(Asia/Tokyo など)ではありません(ServerScriptModel.cs)。
関連ページ
- タイムゾーンの考え方
- 拡張サーバースクリプトとバックグラウンドサーバースクリプト
- $ps.file と添付ファイル(同じ手法で添付ファイル項目を変換する例)