Documentation
¶
Overview ¶
Package types provides PostgresSQL-compatible data types for SQL/JSON Path execution.
It makes every effort to duplicate the behavior of PostgreSQL JSONB dates and times in particular in order to compatibly execute date and time comparisons in SQL/JSON Path expressions.
DateTime Types ¶
Package maps the Postgres date and time types to these DateTime-implementing types:
- date: Date
- time: Time
- timetz: TimeTZ
- timestamp: Timestamp
- timestamptz: TimestampTZ
Each provides a constructor that takes a time.Time object, which defines the underlying representation. Each also provides casting functions between the types, but only for supported casts.
Time Zones ¶
Like the PostgreSQL timetz and timestamptz types, TimeTZ and TimestampTZ do not store time zone information, but an offset from UTC. Even when passed a time.Time object with a detailed location, the constructors will strip it out and retain only the offset for the time.Time value.
By default, the types package operates on and displays dates and times in the context of UTC. This affects conversion between time zone and non-time zone data types, in particular. To change the time zone in which such operations execute,
When required to operate on dates and times in the context of a time zone, the types package defaults to UTC. For example, a TimestampTZ stringifies into UTC:
offsetPlus5 := time.FixedZone("", 5*3600) timestamp := types.NewTimestampTZ( context.Background(), time.Date(2023, 8, 15, 12, 34, 56, 0, offsetPlus5), ) fmt.Printf("%v\n", timestamp) // → 2023-08-15T07:34:56+00:00
To operate in a the context of a different time zone, use ContextWithTZ to add it to the context passed to any constructor or method that takes a context:
tz, err := time.LoadLocation("America/New_York") if err != nil { log.Fatal(err) } ctx := types.ContextWithTZ(context.Background(), tz) offsetPlus5 := time.FixedZone("", 5*3600) timestamp := types.NewTimestampTZ( ctx, time.Date(2023, 8, 15, 12, 34, 56, 0, offsetPlus5), ) fmt.Printf("%v\n", timestamp) // → 2023-08-15T07:34:56+00:00
This time zone affects casts, as well, between offset-aware types (TimeTZ, TimestampTZ) and offset-unaware types (Date, Time, Timestamp). For any execution, be sure to pass the same context to all operations.
Example (NYC) ¶
Postgres:
david=# set time zone 'America/New_York'; SET david=# select jsonb_path_query_tz('"2023-08-15 12:34:56+05"', '$.timestamp_tz()'); jsonb_path_query_tz ----------------------------- "2023-08-15T12:34:56+05:00" (1 row) david=# select jsonb_path_query_tz('"2023-08-15 12:34:56+05"', '$.timestamp_tz().string()'); jsonb_path_query_tz -------------------------- "2023-08-15 03:34:56-04" (1 row)
package main import ( "context" "fmt" "log" "time" "github.com/theory/sqljson/path/types" ) func main() { tz, err := time.LoadLocation("America/New_York") if err != nil { log.Fatal(err) } ctx := types.ContextWithTZ(context.Background(), tz) offsetPlus5 := time.FixedZone("", 5*3600) timestamp := types.NewTimestampTZ( ctx, time.Date(2023, 8, 15, 12, 34, 56, 0, offsetPlus5), ) fmt.Printf("%v\n", timestamp) }
Output: 2023-08-15T12:34:56+05:00
Example (UTC) ¶
Postgres:
david=# select jsonb_path_query_tz('"2023-08-15 12:34:56+05"', '$.timestamp_tz()'); jsonb_path_query_tz ----------------------------- "2023-08-15T12:34:56+05:00" (1 row) david=# select jsonb_path_query_tz('"2023-08-15 12:34:56+05"', '$.timestamp_tz().string()'); jsonb_path_query_tz -------------------------- "2023-08-15 07:34:56+00" (1 row)
package main import ( "context" "fmt" "time" "github.com/theory/sqljson/path/types" ) func main() { offsetPlus5 := time.FixedZone("", 5*3600) ctx := types.ContextWithTZ(context.Background(), time.UTC) timestamp := types.NewTimestampTZ( ctx, time.Date(2023, 8, 15, 12, 34, 56, 0, offsetPlus5), ) fmt.Printf("%v\n", timestamp) }
Output: 2023-08-15T12:34:56+05:00
Index ¶
- Variables
- func ContextWithTZ(ctx context.Context, tz *time.Location) context.Context
- func TZFromContext(ctx context.Context) *time.Location
- type Date
- func (d *Date) Compare(u time.Time) int
- func (d *Date) GoTime() time.Time
- func (d *Date) MarshalJSON() ([]byte, error)
- func (d *Date) String() string
- func (d *Date) ToTimestamp(context.Context) *Timestamp
- func (d *Date) ToTimestampTZ(ctx context.Context) *TimestampTZ
- func (d *Date) UnmarshalJSON(data []byte) error
- type DateTime
- type Time
- type TimeTZ
- type Timestamp
- func (ts *Timestamp) Compare(u time.Time) int
- func (ts *Timestamp) GoTime() time.Time
- func (ts *Timestamp) MarshalJSON() ([]byte, error)
- func (ts *Timestamp) String() string
- func (ts *Timestamp) ToDate(context.Context) *Date
- func (ts *Timestamp) ToTime(context.Context) *Time
- func (ts *Timestamp) ToTimestampTZ(ctx context.Context) *TimestampTZ
- func (ts *Timestamp) UnmarshalJSON(data []byte) error
- type TimestampTZ
- func (ts *TimestampTZ) Compare(u time.Time) int
- func (ts *TimestampTZ) GoTime() time.Time
- func (ts *TimestampTZ) MarshalJSON() ([]byte, error)
- func (ts *TimestampTZ) String() string
- func (ts *TimestampTZ) ToDate(ctx context.Context) *Date
- func (ts *TimestampTZ) ToTime(ctx context.Context) *Time
- func (ts *TimestampTZ) ToTimeTZ(ctx context.Context) *TimeTZ
- func (ts *TimestampTZ) ToTimestamp(ctx context.Context) *Timestamp
- func (ts *TimestampTZ) UnmarshalJSON(data []byte) error
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ErrSQLType = errors.New("type")
ErrSQLType wraps errors returned by the types package.
Functions ¶
func ContextWithTZ ¶
ContextWithTZ returns a new Context that carries value tz.
Types ¶
type Date ¶
Date represents the PostgreSQL date type.
Example ¶
Postgres:
david=# set time zone 'America/New_York'; SET david=# select jsonb_path_query_tz('"2023-08-15"', '$.date()'); jsonb_path_query_tz --------------------- "2023-08-15" (1 row) david=# select jsonb_path_query_tz('"2023-08-15"', '$.timestamp()'); jsonb_path_query_tz ----------------------- "2023-08-15T00:00:00" (1 row) david=# select jsonb_path_query_tz('"2023-08-15"', '$.timestamp_tz()'); jsonb_path_query_tz ----------------------------- "2023-08-15T04:00:00+00:00" (1 row)
package main import ( "context" "fmt" "log" "time" "github.com/theory/sqljson/path/types" ) func main() { date := types.NewDate(time.Date(2023, 8, 15, 12, 34, 56, 0, time.UTC)) fmt.Printf("%v\n", date) tz, err := time.LoadLocation("America/New_York") if err != nil { log.Fatal(err) } ctx := types.ContextWithTZ(context.Background(), tz) fmt.Printf("%v\n", date.ToTimestamp(ctx)) // Difference in cast value formatting thread: // https://www.postgresql.org/message-id/flat/7DE080CE-6D8C-4794-9BD1-7D9699172FAB%40justatheory.com fmt.Printf("%v\n", date.ToTimestampTZ(ctx)) }
Output: 2023-08-15 2023-08-15T00:00:00 2023-08-15T00:00:00-04:00
func (*Date) Compare ¶
Compare compares the time instant d with u. If d is before u, it returns -1; if d is after u, it returns +1; if they're the same, it returns 0.
func (*Date) MarshalJSON ¶
MarshalJSON implements the json.Marshaler interface. The time is a quoted string in the RFC 3339 format with sub-second precision.
func (*Date) ToTimestamp ¶
ToTimestamp converts ts to *Timestamp.
func (*Date) ToTimestampTZ ¶
func (d *Date) ToTimestampTZ(ctx context.Context) *TimestampTZ
ToTimestampTZ converts d to TimestampTZ in the time zone in ctx.
type DateTime ¶
type DateTime interface { fmt.Stringer // GoTime returns the underlying time.Time object. GoTime() time.Time }
DateTime defines the interface for all date and time data types.
func ParseTime ¶
ParseTime parses src into time.Time by iterating through a list of valid date, time, and timestamp formats according to SQL/JSON standard: date, time_tz, time, timestamp_tz, and timestamp. Returns false if the string cannot be parsed by any of the formats.
We also support ISO 8601 format (with "T") for timestamps, because PostgreSQL to_json() and to_jsonb() functions use this format.
type Time ¶
Time represents the PostgreSQL time without time zone type.
Example ¶
Postgres:
david=# set time zone 'America/Phoenix'; SET david=# select jsonb_path_query_tz('"12:34:56"', '$.time()'); jsonb_path_query_tz --------------------- "12:34:56" (1 row) david=# select jsonb_path_query_tz('"12:34:56"', '$.time_tz()'); jsonb_path_query_tz --------------------- "12:34:56-07:00" (1 row)
package main import ( "context" "fmt" "log" "time" "github.com/theory/sqljson/path/types" ) func main() { aTime := types.NewTime(time.Date(2023, 8, 15, 12, 34, 56, 0, time.UTC)) fmt.Printf("%v\n", aTime) tz, err := time.LoadLocation("America/Phoenix") if err != nil { log.Fatal(err) } ctx := types.ContextWithTZ(context.Background(), tz) fmt.Printf("%v\n", aTime.ToTimeTZ(ctx)) }
Output: 12:34:56 12:34:56-07:00
func (*Time) Compare ¶
Compare compares the time instant t with u. If d is before u, it returns -1; if t is after u, it returns +1; if they're the same, it returns 0.
func (*Time) MarshalJSON ¶
MarshalJSON implements the json.Marshaler interface. The time is a quoted string using the "15:04:05.999999999" format.
func (*Time) String ¶
String returns the string representation of ts using the format "15:04:05.999999999".
func (*Time) ToTimeTZ ¶
ToTimeTZ converts t to *TimeTZ in the time zone in ctx. It works relative the current date.
func (*Time) UnmarshalJSON ¶
UnmarshalJSON implements the json.Unmarshaler interface. The time must be a quoted string in the "15:04:05.999999999" format.
type TimeTZ ¶
TimeTZ represents the PostgreSQL time with time zone type.
Example ¶
Postgres:
david=# set time zone 'UTC'; SET david=# select jsonb_path_query_tz('"12:34:56-04:00"', '$.time_tz()'); jsonb_path_query_tz --------------------- "12:34:56-04:00" (1 row) david=# select jsonb_path_query_tz('"12:34:56-04:00"', '$.time()'); jsonb_path_query_tz --------------------- "12:34:56" (1 row) david=# set time zone 'America/New_York'; SET david=# select jsonb_path_query_tz('"12:34:56-04:00"', '$.time()'); jsonb_path_query_tz --------------------- "12:34:56" (1 row)
package main import ( "context" "fmt" "log" "time" "github.com/theory/sqljson/path/types" ) func main() { tz, err := time.LoadLocation("America/New_York") if err != nil { log.Fatal(err) } timeTZ := types.NewTimeTZ(time.Date(2023, 8, 15, 12, 34, 56, 0, tz)) fmt.Printf("%v\n", timeTZ) ctx := types.ContextWithTZ(context.Background(), time.UTC) fmt.Printf("%v\n", timeTZ.ToTime(ctx)) //nolint:gosmopolitan ctx = types.ContextWithTZ(context.Background(), time.Local) fmt.Printf("%v\n", timeTZ.ToTime(ctx)) }
Output: 12:34:56-04:00 12:34:56 12:34:56
func (*TimeTZ) Compare ¶
Compare compares the time instant t with u. If d is before u, it returns -1; if t is after u, it returns +1; if they're the same, it returns 0. Note that the TZ offset contributes to this comparison; values with different offsets are never considered to be the same.
func (*TimeTZ) MarshalJSON ¶
MarshalJSON implements the json.Marshaler interface. The time is a quoted string using the "15:04:05.999999999-07:00" format.
func (*TimeTZ) String ¶
String returns the string representation of ts using the format "15:04:05.999999999-07:00".
func (*TimeTZ) UnmarshalJSON ¶
UnmarshalJSON implements the json.Unmarshaler interface. The time must be a quoted string in one of the following formats:
- 15:04:05.999999999Z07:00:00
- 15:04:05.999999999Z07:00
- 15:04:05.999999999Z07
type Timestamp ¶
Timestamp represents the PostgreSQL timestamp without time zone type.
Example ¶
Postgres:
david=# set time zone 'America/Phoenix'; SET david=# select jsonb_path_query_tz('"2023-08-15 12:34:56"', '$.timestamp()'); jsonb_path_query_tz ----------------------- "2023-08-15T12:34:56" (1 row) david=# select jsonb_path_query_tz('"2023-08-15 12:34:56"', '$.date()'); jsonb_path_query_tz --------------------- "2023-08-15" (1 row) david=# select jsonb_path_query_tz('"2023-08-15 12:34:56"', '$.time()'); jsonb_path_query_tz --------------------- "12:34:56" (1 row) david=# select jsonb_path_query_tz('"2023-08-15 12:34:56"', '$.timestamp_tz()'); jsonb_path_query_tz ----------------------------- "2023-08-15T19:34:56+00:00" (1 row)
package main import ( "context" "fmt" "log" "time" "github.com/theory/sqljson/path/types" ) func main() { ts := types.NewTimestamp(time.Date(2023, 8, 15, 12, 34, 56, 0, time.UTC)) fmt.Printf("%v\n", ts) tz, err := time.LoadLocation("America/Phoenix") if err != nil { log.Fatal(err) } ctx := types.ContextWithTZ(context.Background(), tz) fmt.Printf("%v\n", ts.ToDate(ctx)) fmt.Printf("%v\n", ts.ToTime(ctx)) // Difference in cast value formatting thread: // https://www.postgresql.org/message-id/flat/7DE080CE-6D8C-4794-9BD1-7D9699172FAB%40justatheory.com fmt.Printf("%v\n", ts.ToTimestampTZ(ctx)) }
Output: 2023-08-15T12:34:56 2023-08-15 12:34:56 2023-08-15T12:34:56-07:00
func NewTimestamp ¶
NewTimestamp coerces src into a Timestamp.
func (*Timestamp) Compare ¶
Compare compares the time instant ts with u. If ts is before u, it returns -1; if ts is after u, it returns +1; if they're the same, it returns 0.
func (*Timestamp) MarshalJSON ¶
MarshalJSON implements the json.Marshaler interface. The time is a quoted string using the "2006-01-02T15:04:05.999999999" format.
func (*Timestamp) String ¶
String returns the string representation of ts using the format "2006-01-02T15:04:05.999999999".
func (*Timestamp) ToTimestampTZ ¶
func (ts *Timestamp) ToTimestampTZ(ctx context.Context) *TimestampTZ
ToTimestampTZ converts ts to *TimestampTZ.
func (*Timestamp) UnmarshalJSON ¶
UnmarshalJSON implements the json.Unmarshaler interface. The time must be a quoted string in the "2006-01-02T15:04:05.999999999" format.
type TimestampTZ ¶
type TimestampTZ struct { // Time is the underlying time.Time value. time.Time // contains filtered or unexported fields }
TimestampTZ represents the PostgreSQL timestamp with time zone type.
Example ¶
Postgres:
david=# set time zone 'UTC'; SET david=# select jsonb_path_query_tz('"2023-08-15 12:34:56-04"', '$.timestamp_tz()'); jsonb_path_query_tz ----------------------------- "2023-08-15T12:34:56-04:00" (1 row) david=# select jsonb_path_query_tz('"2023-08-15 12:34:56-04"', '$.timestamp()'); jsonb_path_query_tz ----------------------- "2023-08-15T16:34:56" (1 row) david=# select jsonb_path_query_tz('"2023-08-15 12:34:56-04"', '$.date()'); jsonb_path_query_tz --------------------- "2023-08-15" (1 row) david=# select jsonb_path_query_tz('"2023-08-15 12:34:56-04"', '$.time()'); jsonb_path_query_tz --------------------- "16:34:56" (1 row) david=# set time zone 'America/Los_Angeles'; david=# select jsonb_path_query_tz('"2023-08-15 12:34:56-04"', '$.timestamp()'); jsonb_path_query_tz ----------------------- "2023-08-15T09:34:56" (1 row) david=# select jsonb_path_query_tz('"2023-08-15 12:34:56-04"', '$.date()'); jsonb_path_query_tz --------------------- "2023-08-15" (1 row) david=# select jsonb_path_query_tz('"2023-08-15 12:34:56-04"', '$.time()'); jsonb_path_query_tz --------------------- "09:34:56" (1 row)
package main import ( "context" "fmt" "log" "time" "github.com/theory/sqljson/path/types" ) func main() { tz, err := time.LoadLocation("America/New_York") if err != nil { log.Fatal(err) } ctx := types.ContextWithTZ(context.Background(), time.UTC) tsTZ := types.NewTimestampTZ(ctx, time.Date(2023, 8, 15, 12, 34, 56, 0, tz)) fmt.Printf("%v\n", tsTZ) fmt.Printf("%v\n", tsTZ.ToTimestamp(ctx)) fmt.Printf("%v\n", tsTZ.ToDate(ctx)) fmt.Printf("%v\n", tsTZ.ToTime(ctx)) tz, err = time.LoadLocation("America/Los_Angeles") if err != nil { log.Fatal(err) } ctx = types.ContextWithTZ(context.Background(), tz) fmt.Printf("%v\n", tsTZ.ToTimestamp(ctx)) fmt.Printf("%v\n", tsTZ.ToDate(ctx)) fmt.Printf("%v\n", tsTZ.ToTime(ctx)) }
Output: 2023-08-15T12:34:56-04:00 2023-08-15T16:34:56 2023-08-15 16:34:56 2023-08-15T09:34:56 2023-08-15 09:34:56
func NewTimestampTZ ¶
func NewTimestampTZ(ctx context.Context, src time.Time) *TimestampTZ
NewTimestampTZ creates a timestamp with time zone with src. The ctx param is used solely to determine the time zone used by TimestampTZ.String.
func (*TimestampTZ) Compare ¶
func (ts *TimestampTZ) Compare(u time.Time) int
Compare compares the time instant ts with u. If ts is before u, it returns -1; if ts is after u, it returns +1; if they're the same, it returns 0.
func (*TimestampTZ) GoTime ¶
func (ts *TimestampTZ) GoTime() time.Time
GoTime returns the underlying time.Time object.
func (*TimestampTZ) MarshalJSON ¶
func (ts *TimestampTZ) MarshalJSON() ([]byte, error)
MarshalJSON implements the json.Marshaler interface. The time is a quoted string using the "2006-01-02T15:04:05.999999999-07:00" format.
func (*TimestampTZ) String ¶
func (ts *TimestampTZ) String() string
String returns the string representation of ts in the time zone in the Context passed to NewTimestampTZ, using the format "2006-01-02T15:04:05.999999999-07:00".
func (*TimestampTZ) ToDate ¶
func (ts *TimestampTZ) ToDate(ctx context.Context) *Date
ToDate converts ts to *Date in the time zone in ctx.
func (*TimestampTZ) ToTime ¶
func (ts *TimestampTZ) ToTime(ctx context.Context) *Time
ToTime converts ts to *Time in the time zone in ctx.
func (*TimestampTZ) ToTimeTZ ¶
func (ts *TimestampTZ) ToTimeTZ(ctx context.Context) *TimeTZ
ToTimeTZ converts ts to TimeTZ in the time zone in ctx.
func (*TimestampTZ) ToTimestamp ¶
func (ts *TimestampTZ) ToTimestamp(ctx context.Context) *Timestamp
ToTimestamp converts ts to *Timestamp in the time zone in ctx.
func (*TimestampTZ) UnmarshalJSON ¶
func (ts *TimestampTZ) UnmarshalJSON(data []byte) error
UnmarshalJSON implements the json.Unmarshaler interface. The time must be a quoted string in one of the following formats:
- 2006-01-02T15:04:05.999999999Z07:00:00
- 2006-01-02T15:04:05.999999999Z07:00
- 2006-01-02T15:04:05.999999999Z07