From 4c3b3f5df20c768f836c029e4794980ecc20f052 Mon Sep 17 00:00:00 2001 From: Colin Adams Date: Mon, 15 May 2023 10:03:08 -0700 Subject: [PATCH] Update format when casting datetimes to strings --- internal/encoder.go | 28 ++++++++++++++++++++++++++- internal/function_datetime.go | 2 +- internal/function_time.go | 4 ++-- query_test.go | 36 +++++++++++++++++++++++++++++++++++ 4 files changed, 66 insertions(+), 4 deletions(-) diff --git a/internal/encoder.go b/internal/encoder.go index 3be191e..a4218bc 100644 --- a/internal/encoder.go +++ b/internal/encoder.go @@ -378,7 +378,25 @@ func CastValue(t types.Type, v Value) (Value, error) { return nil, err } return FloatValue(f64), nil - case types.STRING, types.ENUM: + case types.STRING: + switch v.(type) { + // If this is coming from a date/time, the format is slightly different + // than when just writing the value out as a string. + case DateValue: + return convertTimeValueToStringWithFormat(v, "2006-01-02") + case DatetimeValue: + return convertTimeValueToStringWithFormat(v, "2006-01-02 15:04:05.999999") + case TimeValue: + return convertTimeValueToStringWithFormat(v, "15:04:05.999999") + case TimestampValue: + return convertTimeValueToStringWithFormat(v, "2006-01-02 15:04:05.999999-07") + } + s, err := v.ToString() + if err != nil { + return nil, err + } + return StringValue(s), nil + case types.ENUM: s, err := v.ToString() if err != nil { return nil, err @@ -507,6 +525,14 @@ func CastValue(t types.Type, v Value) (Value, error) { return nil, fmt.Errorf("unsupported cast %s value", t.Kind()) } +func convertTimeValueToStringWithFormat(v Value, format string) (Value, error) { + valueTime, err := v.ToTime() + if err != nil { + return nil, err + } + return StringValue(valueTime.UTC().Format(format)), nil +} + func ValueFromGoValue(v interface{}) (Value, error) { if isNullValue(v) { return nil, nil diff --git a/internal/function_datetime.go b/internal/function_datetime.go index aaf525e..95fac0a 100644 --- a/internal/function_datetime.go +++ b/internal/function_datetime.go @@ -100,7 +100,7 @@ func DATETIME(args ...Value) (Value, error) { } return DatetimeValue(t.In(loc)), nil } - return DatetimeValue(t), nil + return DatetimeValue(t.UTC()), nil } return nil, fmt.Errorf("DATETIME: first argument must be DATE or TIMESTAMP type") } diff --git a/internal/function_time.go b/internal/function_time.go index e0a291b..b9213e5 100644 --- a/internal/function_time.go +++ b/internal/function_time.go @@ -57,13 +57,13 @@ func TIME(args ...Value) (Value, error) { } return TimeValue(t.In(loc)), nil } - return TimeValue(t), nil + return TimeValue(t.UTC()), nil case DatetimeValue: t, err := args[0].ToTime() if err != nil { return nil, err } - return TimeValue(t), nil + return TimeValue(t.UTC()), nil } return nil, fmt.Errorf("TIME: invalid first argument type %T", args[0]) } diff --git a/query_test.go b/query_test.go index 31d13f3..90bb831 100644 --- a/query_test.go +++ b/query_test.go @@ -3706,6 +3706,12 @@ SELECT date, EXTRACT(ISOYEAR FROM date), EXTRACT(YEAR FROM date), EXTRACT(MONTH expectedRows: [][]interface{}{{"2008"}}, }, + { + name: "cast date as string", + query: `SELECT CAST(DATE("2022-08-01 06:47:51.123456-07:00") AS STRING)`, + expectedRows: [][]interface{}{{"2022-08-01"}}, + }, + { name: "last_day", query: `SELECT LAST_DAY(DATE '2008-11-25') AS last_day`, @@ -3876,6 +3882,16 @@ SELECT date, EXTRACT(ISOYEAR FROM date), EXTRACT(YEAR FROM date), EXTRACT(MONTH query: `SELECT FORMAT_DATETIME("%E4Y", DATETIME "2008-12-25 15:30:12.345678")`, expectedRows: [][]interface{}{{"2008"}}, }, + { + name: "cast datetime as string", + query: `SELECT CAST(DATETIME(TIMESTAMP("2022-08-01 06:47:51.123456-07:00")) AS STRING)`, + expectedRows: [][]interface{}{{"2022-08-01 13:47:51.123456"}}, + }, + { + name: "cast date as datetime", + query: `SELECT CAST(DATE("1987-01-25") AS DATETIME)`, + expectedRows: [][]interface{}{{"1987-01-25T00:00:00"}}, + }, { name: "parse datetime", query: `SELECT PARSE_DATETIME("%a %b %e %I:%M:%S %Y", "Thu Dec 25 07:30:00 2008")`, @@ -3952,6 +3968,21 @@ SELECT date, EXTRACT(ISOYEAR FROM date), EXTRACT(YEAR FROM date), EXTRACT(MONTH query: `SELECT FORMAT_TIME("%E*S", TIME "15:30:12.345678")`, expectedRows: [][]interface{}{{"12.345678"}}, }, + { + name: "cast time as string", + query: `SELECT CAST(TIME("2022-08-01 06:47:51.123456-04:00") AS STRING)`, + expectedRows: [][]interface{}{{"10:47:51.123456"}}, + }, + { + name: "cast time with timezone as string", + query: `SELECT CAST(TIME("2022-08-01 06:47:51.123456-04:00", "America/Los_Angeles") AS STRING)`, + expectedRows: [][]interface{}{{"03:47:51.123456"}}, + }, + { + name: "cast time from datetime as string", + query: `SELECT CAST(TIME(DATETIME(TIMESTAMP("2022-08-01 06:47:51.123456-04:00"))) AS STRING)`, + expectedRows: [][]interface{}{{"10:47:51.123456"}}, + }, { name: "parse time with %I:%M:%S", query: `SELECT PARSE_TIME("%I:%M:%S", "07:30:00")`, @@ -4094,6 +4125,11 @@ SELECT date, EXTRACT(ISOYEAR FROM date), EXTRACT(YEAR FROM date), EXTRACT(MONTH query: `SELECT FORMAT_TIMESTAMP("%Ez", TIMESTAMP "2008-12-25 15:30:12.345678+00")`, expectedRows: [][]interface{}{{"+00:00"}}, }, + { + name: "cast timestamp as string", + query: `SELECT CAST(TIMESTAMP("2022-08-01 06:47:51.123456-07:00") AS STRING);`, + expectedRows: [][]interface{}{{"2022-08-01 13:47:51.123456+00"}}, + }, { name: "parse timestamp with %a %b %e %I:%M:%S %Y", query: `SELECT PARSE_TIMESTAMP("%a %b %e %I:%M:%S %Y", "Thu Dec 25 07:30:00 2008")`,