#Google App Maker の CalendarSample
https://developers.google.com/appmaker/samples/calendar/
このサンプルの日付フィルタ機能を試すと、指定した開始日の前日夕方のイベントも表示されます。
逆に、指定した終了日の夕方以降のイベントが取得されません。
お察しの通り、ここにはもタイムゾーンの問題がありました。
(念のため:2017年9月現在の話です。)
#原因
サーバ側で動作するスクリプトの getEvents_() を見てみると、検索範囲の開始日と終了日の時刻を 00:00 にセットしている箇所があります。
function getEvents_(query) {
var startDate = query.parameters.StartDate;
startDate.setHours(0, 0, 0, 0); //<===========attn
var endDate = query.parameters.EndDate;
endDate.setDate(endDate.getDate() + 1);
endDate.setHours(0, 0, 0, 0); //<===========attn
if (startDate.getTime() > endDate.getTime()) {
return [];
}
var results = [];
var events = CalendarApp.getDefaultCalendar().getEvents(startDate, endDate);
重要なのは、サーバー側でスクリプトが動作する際、日付はすべて太平洋標準時PST(夏ならPDT)の扱いになっているという前提です。
そのため、startDate.setHours(0, 0, 0, 0)
すると startDate
は PST/PDT 00:00、つまり日本標準時JST 17:00(夏は16:00)にセットされます。
結果、対象カレンダーの予定が JST で作成されている場合、getEvents(startDate, endDate)
は「開始日前日17:00 ~ 終了日当日17:00」(夏は 16:00)の検索結果を返してくる、というのが本現象の動きです。
#対処
× ServerScript 内で日付を PST/PDT ⇒ JST へ変換して処理する
○ ClientScript 内であらかじめクエリ時刻を 00:00 に設定する
前者は、ユーザーが JST 環境限定ならよいのですが汎用性に欠けます。
まして、サーバーが今後も PST/PDT で動作を続ける保証もありません。
一方、クライアント側スクリプトはユーザーのタイムゾーンで動作します。
そのため 00:00 のセットはユーザー側で行うのが最良です。
##初期化時の 00:00 セット
まず、サーバー側のスクリプトから setHours(0, 0, 0, 0)
をコメントアウトしておきます。
function getEvents_(query) {
var startDate = query.parameters.StartDate;
//startDate.setHours(0, 0, 0, 0); //<===========comment-out
var endDate = query.parameters.EndDate;
endDate.setDate(endDate.getDate() + 1);
//endDate.setHours(0, 0, 0, 0); //<===========comment-out
続いて、クライアント側スクリプトの loadEvents() より前に、「クエリ中の日時を 00:00 にセットし直す関数」を作成します。
本例では関数名を updateQueryDates() としましたが、お好みでどうぞ。
function updateQueryDates(){
var ds = app.datasources.Events;
//Copy dates from query params and set time to: 00:00
var sd = new Date( ds.query.parameters.StartDate.getTime() );
var ed = new Date( ds.query.parameters.EndDate.getTime() );
sd.setHours( 0, 0, 0, 0 );
ed.setHours( 0, 0, 0, 0 );
//Set back to query
ds.query.parameters.StartDate = sd;
ds.query.parameters.EndDate = ed;
}
そして、loadEvents() 内では初期化後にこの関数を呼びます。
/**
* Loads Events datasource.
*/
function loadEvents() {
var ds = app.datasources.Events;
ds.query.parameters.StartDate = new Date();
ds.query.parameters.EndDate = new Date();
updateQueryDates(); //<===========insert
ds.load();
}
ここまでで、ロード時の初回検索は正常に動作するようになるはずです。
「初期化時に 00:00 をセットすれば?何故一度保存して上書きなんて面倒なことを?」
⇒後述の onValueEdit イベントでも同関数を利用する前提で、汎用化を図った結果です。
onValueEdit での処理を採用しない場合は、loadEvents() 内で new Date() したものを直接 setHours() で下処理するだけでOKです。
##Date Box 操作時の 00:00 セット
<2017/09/22追記>
Date Box については、Date Picker からの選択時に 00:00 が自動的にセットされるので、ここから先は本来不要です。
(コメント欄参照。howdy39さんご指摘ありがとうございます。)
勝手に DateTime Picker に進化するとか、そんな将来の仕様変更を恐れる方は、適用しておくと安心でしょう。
追記ここまで>
対象の Date Box を選択して、Events > onValueEdit を、プリセットアクションの Reload Datasource からカスタムアクションに変更し、上で作成した関数を load() の前で呼ぶようにします。
updateQueryDates(); //<===========insert
widget.datasource.load();
終了日指定の Date Box にも、同様のイベント調整を行ってください。
以上でOK!
(雑感)
Googleの仕組みはすべてシリコンバレー仕様。
グローバル企業なのだから UTC にしてくれればよいのに・・・
そうすればサンプルを作るGoogle社員も、こんな問題は予め気づいてくれたことでしょうに。