Skip to content

Instantly share code, notes, and snippets.

@tomoemon
Created October 16, 2020 09:01
Show Gist options
  • Save tomoemon/ebce01ce82a6c4e1e390b649d0d36bc1 to your computer and use it in GitHub Desktop.
Save tomoemon/ebce01ce82a6c4e1e390b649d0d36bc1 to your computer and use it in GitHub Desktop.
appengine/datastore と cloud/datastore の timezone に関する挙動を確認する
package main
import (
"context"
"fmt"
"net/http"
"time"
cldatastore "cloud.google.com/go/datastore"
"google.golang.org/appengine"
aedatastore "google.golang.org/appengine/datastore"
"google.golang.org/appengine/log"
)
type User struct {
Name string
CreatedAt time.Time
}
var jstLoc *time.Location
var pstLoc *time.Location
func init() {
var err error
jstLoc, err = time.LoadLocation("Asia/Tokyo")
if err != nil {
panic(err)
}
pstLoc, err = time.LoadLocation("America/Dawson")
if err != nil {
panic(err)
}
}
func main() {
http.HandleFunc("/appengine", hookError(func(ctx context.Context) ([]string, error) {
var outputs []string
k := aedatastore.NewKey(ctx, "User", "id-appengine", 0, nil)
put := &User{
Name: "appengine",
CreatedAt: time.Date(2020, 1, 2, 1, 2, 3, 0, jstLoc),
}
if _, err := aedatastore.Put(ctx, k, put); err != nil {
return nil, err
}
got := new(User)
if err := aedatastore.Get(ctx, k, got); err != nil {
return nil, err
}
outputs = append(outputs, fmt.Sprintf("put: %+v, loc: %#v", put, put.CreatedAt.Location()))
outputs = append(outputs, fmt.Sprintf("got: %+v, loc: %#v", got, got.CreatedAt.Location()))
return outputs,nil
}))
http.HandleFunc("/cloud", hookError(func(ctx context.Context) ([]string, error) {
var outputs []string
client, err := cldatastore.NewClient(ctx, cldatastore.DetectProjectID)
if err != nil {
return nil, err
}
defer func(){
client.Close()
}()
k := cldatastore.NameKey("User", "id-cloud", nil)
put := &User{
Name: "cloud",
CreatedAt: time.Date(2020, 1, 2, 1, 2, 3, 0, pstLoc),
}
if _, err := client.Put(ctx, k, put); err != nil {
return nil, err
}
got := new(User)
if err := client.Get(ctx, k, got); err != nil {
return nil, err
}
outputs = append(outputs, fmt.Sprintf("put: %+v, loc: %#v", put, put.CreatedAt.Location()))
outputs = append(outputs, fmt.Sprintf("got: %+v, loc: %#v", got, got.CreatedAt.Location()))
return outputs, nil
}))
appengine.Main()
}
func hookError(f func (ctx context.Context) ([]string, error)) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
ctx := appengine.NewContext(r)
outputs, err := f(ctx)
if err != nil {
log.Errorf(ctx, "error: %+v", err)
http.Error(w, err.Error(), 500)
return
}
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
for _, o:= range outputs {
fmt.Fprintf(w, "%s\n", o)
}
}
}
@tomoemon
Copy link
Author

tomoemon commented Oct 16, 2020

結果まとめ

  • datastore に entity を Put した際、元の timezone が保存されているかどうかは不明(検証しようがない)
  • appengine/datastore, cloud/datastore のいずれかを使って datastore から time.Time 型の値を取得すると timezone は UTC になる
  • GCP Console の Datastore ページで time.Time 型の値を表示するときのタイムゾーンは、クライアント端末のシステムタイムゾーンによって決まる

環境

runtime: go111
datastore location: nam5 (us-central)

/appengine 呼び出し結果

appengine/datastore を使って entity を put し、同じ entity を get したときの両者の値を確認する

put: &{Name:appengine CreatedAt:2020-01-02 01:02:03 +0900 JST}, loc: &time.Location{name:"Asia/Tokyo", zone:[]time.zone{time.zone{name:"LMT", offset:33539, isDST:false}, time.zone{name:"JDT", offset:36000, isDST:true}, time.zone{name:"JST", offset:32400, isDST:false}, time.zone{name:"JST", offset:32400, isDST:false}}, tx:[]time.zoneTrans{time.zoneTrans{when:-2147483648, index:0x3, isstd:false, isutc:false}, time.zoneTrans{when:-683802000, index:0x1, isstd:false, isutc:false}, time.zoneTrans{when:-672310800, index:0x2, isstd:false, isutc:false}, time.zoneTrans{when:-654771600, index:0x1, isstd:true, isutc:true}, time.zoneTrans{when:-640861200, index:0x2, isstd:false, isutc:false}, time.zoneTrans{when:-620298000, index:0x1, isstd:false, isutc:false}, time.zoneTrans{when:-609411600, index:0x2, isstd:false, isutc:false}, time.zoneTrans{when:-588848400, index:0x1, isstd:false, isutc:false}, time.zoneTrans{when:-577962000, index:0x2, isstd:false, isutc:false}}, cacheStart:-577962000, cacheEnd:9223372036854775807, cacheZone:(*time.zone)(0xc00013a7c0)}

got: &{Name:appengine CreatedAt:2020-01-01 16:02:03 +0000 UTC}, loc: &time.Location{name:"UTC", zone:[]time.zone(nil), tx:[]time.zoneTrans(nil), cacheStart:0, cacheEnd:0, cacheZone:(*time.zone)(nil)}

/cloud 呼び出し結果

cloud/datastore を使って entity を put し、同じ entity を get したときの両者の値を確認する
(datastore console 上の表示が、保存時の timezone に関係するかどうかを調べるため、保存する entity の timezone を JST 以外にしている)

put: &{Name:cloud CreatedAt:2020-01-02 01:02:03 -0800 PST}, loc: &time.Location{name:"America/Dawson", zone:[]time.zone{time.zone{name:"LMT", offset:-33460, isDST:false}, time.zone{name:"YDT", offset:-28800, isDST:true}, time.zone{name:"YST", offset:-32400, isDST:false}, time.zone{name:"YWT", offset:-28800, isDST:true}, time.zone{name:"YPT", offset:-28800, isDST:true}, time.zone{name:"YDDT", offset:-25200, isDST:true}, time.zone{name:"PST", offset:-28800, isDST:false}, time.zone{name:"PDT", offset:-25200, isDST:true}, time.zone{name:"MST", offset:-25200, isDST:false}}, tx:[]time.zoneTrans{time.zoneTrans{when:-2147483648, index:0x2, isstd:false, isutc:false}, time.zoneTrans{when:-1632056400, index:0x1, isstd:false, isutc:false}, time.zoneTrans{when:-1615125600, index:0x2, isstd:false, isutc:false}, time.zoneTrans{when:-1596978000, index:0x1, isstd:false, isutc:false}, time.zoneTrans{when:-1583164800, index:0x2, isstd:true, isutc:true}, time.zoneTrans{when:-880203600, index:0x3, isstd:false, isutc:false}, time.zoneTrans{when:-769395600, index:0x4, isstd:false, isutc:false}, time.zoneTrans{when:-765381600, index:0x2, isstd:false, isutc:false}, time.zoneTrans{when:-147884400, index:0x5, isstd:false, isutc:false}, time.zoneTrans{when:-131554800, index:0x2, isstd:false, isutc:false}, time.zoneTrans{when:120646800, index:0x6, isstd:false, isutc:false}, time.zoneTrans{when:325677600, index:0x7, isstd:false, isutc:false}, time.zoneTrans{when:341398800, index:0x6, isstd:false, isutc:false}, time.zoneTrans{when:357127200, index:0x7, isstd:false, isutc:false}, time.zoneTrans{when:372848400, index:0x6, isstd:false, isutc:false}, time.zoneTrans{when:388576800, index:0x7, isstd:false, isutc:false}, time.zoneTrans{when:404902800, index:0x6, isstd:false, isutc:false}, time.zoneTrans{when:420026400, index:0x7, isstd:false, isutc:false}, time.zoneTrans{when:436352400, index:0x6, isstd:false, isutc:false}, time.zoneTrans{when:452080800, index:0x7, isstd:false, isutc:false}, time.zoneTrans{when:467802000, index:0x6, isstd:false, isutc:false}, time.zoneTrans{when:483530400, index:0x7, isstd:false, isutc:false}, time.zoneTrans{when:499251600, index:0x6, isstd:false, isutc:false}, time.zoneTrans{when:514980000, index:0x7, isstd:false, isutc:false}, time.zoneTrans{when:530701200, index:0x6, isstd:false, isutc:false}, time.zoneTrans{when:544615200, index:0x7, isstd:false, isutc:false}, time.zoneTrans{when:562150800, index:0x6, isstd:false, isutc:false}, time.zoneTrans{when:576064800, index:0x7, isstd:false, isutc:false}, time.zoneTrans{when:594205200, index:0x6, isstd:false, isutc:false}, time.zoneTrans{when:607514400, index:0x7, isstd:false, isutc:false}, time.zoneTrans{when:625654800, index:0x6, isstd:false, isutc:false}, time.zoneTrans{when:638964000, index:0x7, isstd:false, isutc:false}, time.zoneTrans{when:657104400, index:0x6, isstd:false, isutc:false}, time.zoneTrans{when:671018400, index:0x7, isstd:false, isutc:false}, time.zoneTrans{when:688554000, index:0x6, isstd:false, isutc:false}, time.zoneTrans{when:702468000, index:0x7, isstd:false, isutc:false}, time.zoneTrans{when:720003600, index:0x6, isstd:false, isutc:false}, time.zoneTrans{when:733917600, index:0x7, isstd:false, isutc:false}, time.zoneTrans{when:752058000, index:0x6, isstd:false, isutc:false}, time.zoneTrans{when:765367200, index:0x7, isstd:false, isutc:false}, time.zoneTrans{when:783507600, index:0x6, isstd:false, isutc:false}, time.zoneTrans{when:796816800, index:0x7, isstd:false, isutc:false}, time.zoneTrans{when:814957200, index:0x6, isstd:false, isutc:false}, time.zoneTrans{when:828871200, index:0x7, isstd:false, isutc:false}, time.zoneTrans{when:846406800, index:0x6, isstd:false, isutc:false}, time.zoneTrans{when:860320800, index:0x7, isstd:false, isutc:false}, time.zoneTrans{when:877856400, index:0x6, isstd:false, isutc:false}, time.zoneTrans{when:891770400, index:0x7, isstd:false, isutc:false}, time.zoneTrans{when:909306000, index:0x6, isstd:false, isutc:false}, time.zoneTrans{when:923220000, index:0x7, isstd:false, isutc:false}, time.zoneTrans{when:941360400, index:0x6, isstd:false, isutc:false}, time.zoneTrans{when:954669600, index:0x7, isstd:false, isutc:false}, time.zoneTrans{when:972810000, index:0x6, isstd:false, isutc:false}, time.zoneTrans{when:986119200, index:0x7, isstd:false, isutc:false}, time.zoneTrans{when:1004259600, index:0x6, isstd:false, isutc:false}, time.zoneTrans{when:1018173600, index:0x7, isstd:false, isutc:false}, time.zoneTrans{when:1035709200, index:0x6, isstd:false, isutc:false}, time.zoneTrans{when:1049623200, index:0x7, isstd:false, isutc:false}, time.zoneTrans{when:1067158800, index:0x6, isstd:false, isutc:false}, time.zoneTrans{when:1081072800, index:0x7, isstd:false, isutc:false}, time.zoneTrans{when:1099213200, index:0x6, isstd:false, isutc:false}, time.zoneTrans{when:1112522400, index:0x7, isstd:false, isutc:false}, time.zoneTrans{when:1130662800, index:0x6, isstd:false, isutc:false}, time.zoneTrans{when:1143972000, index:0x7, isstd:false, isutc:false}, time.zoneTrans{when:1162112400, index:0x6, isstd:false, isutc:false}, time.zoneTrans{when:1173607200, index:0x7, isstd:false, isutc:false}, time.zoneTrans{when:1194166800, index:0x6, isstd:false, isutc:false}, time.zoneTrans{when:1205056800, index:0x7, isstd:false, isutc:false}, time.zoneTrans{when:1225616400, index:0x6, isstd:false, isutc:false}, time.zoneTrans{when:1236506400, index:0x7, isstd:false, isutc:false}, time.zoneTrans{when:1257066000, index:0x6, isstd:false, isutc:false}, time.zoneTrans{when:1268560800, index:0x7, isstd:false, isutc:false}, time.zoneTrans{when:1289120400, index:0x6, isstd:false, isutc:false}, time.zoneTrans{when:1300010400, index:0x7, isstd:false, isutc:false}, time.zoneTrans{when:1320570000, index:0x6, isstd:false, isutc:false}, time.zoneTrans{when:1331460000, index:0x7, isstd:false, isutc:false}, time.zoneTrans{when:1352019600, index:0x6, isstd:false, isutc:false}, time.zoneTrans{when:1362909600, index:0x7, isstd:false, isutc:false}, time.zoneTrans{when:1383469200, index:0x6, isstd:false, isutc:false}, time.zoneTrans{when:1394359200, index:0x7, isstd:false, isutc:false}, time.zoneTrans{when:1414918800, index:0x6, isstd:false, isutc:false}, time.zoneTrans{when:1425808800, index:0x7, isstd:false, isutc:false}, time.zoneTrans{when:1446368400, index:0x6, isstd:false, isutc:false}, time.zoneTrans{when:1457863200, index:0x7, isstd:false, isutc:false}, time.zoneTrans{when:1478422800, index:0x6, isstd:false, isutc:false}, time.zoneTrans{when:1489312800, index:0x7, isstd:false, isutc:false}, time.zoneTrans{when:1509872400, index:0x6, isstd:false, isutc:false}, time.zoneTrans{when:1520762400, index:0x7, isstd:false, isutc:false}, time.zoneTrans{when:1541322000, index:0x6, isstd:false, isutc:false}, time.zoneTrans{when:1552212000, index:0x7, isstd:false, isutc:false}, time.zoneTrans{when:1572771600, index:0x6, isstd:false, isutc:false}, time.zoneTrans{when:1583661600, index:0x8, isstd:false, isutc:false}}, cacheStart:1583661600, cacheEnd:9223372036854775807, cacheZone:(*time.zone)(0xc0003a6d60)}

got: &{Name:cloud CreatedAt:2020-01-02 09:02:03 +0000 UTC}, loc: &time.Location{name:"Local", zone:[]time.zone{time.zone{name:"UTC", offset:0, isDST:false}}, tx:[]time.zoneTrans{time.zoneTrans{when:-9223372036854775808, index:0x0, isstd:false, isutc:false}}, cacheStart:-9223372036854775808, cacheEnd:9223372036854775807, cacheZone:(*time.zone)(0xc00000c2e0)}

@tomoemon
Copy link
Author

datastore console 上の表示

image

@tomoemon
Copy link
Author

PC のタイムゾーンをウランバートル標準時に変えてから再度 datastore console を表示した結果

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment