Skip to content

UTCTime/LocalTime vs timestamptz/timestamp #1

@lpsmith

Description

@lpsmith

When you are dealing with binary formats, postgresql timestamp values (with or without time zone) just acts as a store of 8-byte values, independent of timezones:

$ ghci-7.6.3
GHCi, version 7.6.3: http://www.haskell.org/ghc/  :? for help
> import Database.PostgreSQL.LibPQ 
> let showResult m = m >>= maybe (return Nothing) (\r -> getvalue r 0 0)
> c <- connectdb ""

> showResult $ exec c "show integer_datetimes"
Just "on"

> exec c "set timezone to 'utc'"
Just (Result 0x00007eff8c0092f0)

> showResult $ execParams c "select $1" [Just (Oid 1184, "\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL", Binary)] Binary
Just "\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL"

> showResult $ execParams c "select $1" [Just (Oid 1184, "\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL", Binary)] Text
Just "2000-01-01 00:00:00+00"

> showResult $ execParams c "select $1" [Just (Oid 1184, "2000-01-01 00:00:00+00", Text)] Binary
Just "\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL"

> showResult $ execParams c "select $1" [Just (Oid 1184, "1999-12-31 19:00:00-05", Text)] Binary
Just "\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL"

> showResult $ execParams c "select $1 = $2" [Just (Oid 1184, "1999-12-31 19:00:00-05", Text), Just (Oid 1184, "2000-01-01 00:00:00+00", Text)] Text
Just "t"


> exec c "set timezone to 'America/New_York'"
Just (Result 0x00007eff940083b0)

> showResult $ execParams c "select $1" [Just (Oid 1184, "\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL", Binary)] Binary
Just "\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL"

> showResult $ execParams c "select $1" [Just (Oid 1184, "\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL", Binary)] Text
Just "1999-12-31 19:00:00-05"

> showResult $ execParams c "select $1" [Just (Oid 1184, "2000-01-01 00:00:00+00", Text)] Binary
Just "\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL"

> showResult $ execParams c "select $1" [Just (Oid 1184, "1999-12-31 19:00:00-05", Text)] Binary
Just "\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL"

> showResult $ execParams c "select $1 = $2" [Just (Oid 1184, "1999-12-31 19:00:00-05", Text), Just (Oid 1184, "2000-01-01 00:00:00+00", Text)] Text
Just "t"

However, there's still a semantics to each of the types, and hasql-postgres has the semantics precisely wrong:

> showResult $ execParams c "select $1" [Just (Oid 1114, "1999-12-31 19:00:00-05", Text)] Text
Just "1999-12-31 19:00:00"

> showResult $ execParams c "select $1" [Just (Oid 1114, "1999-12-31 19:00:00-05", Text)] Binary
Just "\255\255\255\251\207\GS\204\NUL"

> showResult $ execParams c "select $1 = $2" [Just (Oid 1114, "1999-12-31 19:00:00-05", Text), Just (Oid 1114, "2000-01-01 00:00:00+00", Text)] Text
Just "f"

Yeah, that doesn't look like a UTCTime to me. When a backend is using the integer_datetime format, a timestamptz is an 8-byte signed integer of microseconds from 2000-01-01 00:00:00+00, whereas a timestamp is an 8-byte signed integer of microseconds from somewhere between 2000-01-01 00:00:00-24 and 2000-01-01 00:00:00+24, and this ambiguity can only be resolved in some larger context.

In short, UTCTime really should map to timestamp with time zone, whereas LocalTime really should map to timestamp. And, in the vast majority of circumstances, timestamp with time zone is the correct choice, and one should always include a offset from UTC when using textual formats, as postgresql-simple's ToField instances for UTCTime and ZonedTime do.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions