diff --git a/datastore/load.go b/datastore/load.go index 821331151a8..86315019bbc 100644 --- a/datastore/load.go +++ b/datastore/load.go @@ -369,7 +369,7 @@ func setVal(v reflect.Value, p Property) (s string) { if ok { s := micros / 1e6 ns := micros % 1e6 - v.Set(reflect.ValueOf(time.Unix(s, ns))) + v.Set(reflect.ValueOf(time.Unix(s, ns).In(time.UTC))) break } x, ok := pValue.(time.Time) @@ -536,7 +536,7 @@ func propToValue(v *pb.Value) (interface{}, error) { case *pb.Value_DoubleValue: return v.DoubleValue, nil case *pb.Value_TimestampValue: - return time.Unix(v.TimestampValue.Seconds, int64(v.TimestampValue.Nanos)), nil + return time.Unix(v.TimestampValue.Seconds, int64(v.TimestampValue.Nanos)).In(time.UTC), nil case *pb.Value_KeyValue: return protoToKey(v.KeyValue) case *pb.Value_StringValue: diff --git a/datastore/load_test.go b/datastore/load_test.go index 518fa61c10b..3e69d5638ae 100644 --- a/datastore/load_test.go +++ b/datastore/load_test.go @@ -569,6 +569,38 @@ func TestLoadToInterface(t *testing.T) { } } +// Expect Local times to be represented in UTC +func TestTimezone(t *testing.T) { + src := &pb.Entity{ + Key: keyToProto(testKey0), + Properties: map[string]*pb.Value{ + "Time": {ValueType: &pb.Value_TimestampValue{TimestampValue: ×tamppb.Timestamp{Seconds: 1605504600}}}, + }, + } + + dst := &struct{ Time time.Time }{ + Time: time.Time{}, + } + want := &struct{ Time time.Time }{ + Time: time.Unix(1605504600, 0).In(time.UTC), + } + + err := loadEntityProto(dst, src) + if err != nil { + t.Fatalf("loadEntityProto: %v", err) + } + + if diff := testutil.Diff(dst, want); diff != "" { + t.Fatalf("Mismatch: got - want +\n%s", diff) + } + // Also, the Zones need to be compared as comparing times will not detect this difference. + dstZone, _ := dst.Time.Zone() + wantZone, _ := want.Time.Zone() + if diff := testutil.Diff(dstZone, wantZone); diff != "" { + t.Fatalf("Mismatch: got - want +\n%s", diff) + } +} + func TestAlreadyPopulatedDst(t *testing.T) { testCases := []struct { desc string