postgresql-simple icon indicating copy to clipboard operation
postgresql-simple copied to clipboard

Parse Error on old ZonedTimes

Open lpsmith opened this issue 11 years ago • 1 comments

Issues #122, #123, and #124 that affected UTCTime also applies to ZonedTime.

Unfortunately, the time package's conception of TimeZone is very much deficient. TimeZone is defined as:

data TimeZone = TimeZone {
    -- | The number of minutes offset from UTC. Positive means local time will be later in the day than UTC.
    timeZoneMinutes :: Int,
    -- | Is this time zone just persisting for the summer?
    timeZoneSummerOnly :: Bool,
    -- | The name of the zone, typically a three- or four-letter acronym.
    timeZoneName :: String
}

With some of the commonly used operations:

data ZonedTime where
   ZonedTime :: LocalTime -> TimeZone -> ZonedTime

utcToZonedTime :: TimeZone -> UTCTime -> ZonedTime
utcToLocalZonedTime :: UTCTime -> IO ZonedTime

Especially in light of IANA's timezone database, the problem with TimeZone is that its name is completely inappropriate because it assumes timezones represent a fixed offset. Also, it assumes that offsets are a whole number of minutes. It would be far more appropriate to call this type a OffsetUTC. I can't say I'm a fan of the name for ZonedTime, but it isn't as bad (it's at least par for the course, compared to other software). In any case, I'll use LocalTimeWithOffset to distinguish the concepts:

data OffsetUTC = OffsetUTC { offsetUTCSeconds :: Int }  {- and maybe some other fields as well -}

data LocalTimeWithOffset = LocalTimeWithOffset LocalTime OffsetUTC

Then, an actual TimeZone would be some type that supports, at the very least, the following operations:

utcToLocalTimeWithOffset :: TimeZone -> UTCTime -> LocalTimeWithOffset
getLocalTimeWithOffset  :: TimeZone -> LocalTime -> LocalTimeWithOffset
getTimeZone :: String -> IO TimeZone

I admit the names need to be shorter and more pithy. The timezone-series and timezone-olson packages are a simple proof-of-concept implementation of proper time zones, although it's not at all an efficient representation.

However, I digress a bit; the issue is how should we deal with a (LocalTime, OffsetUTC) pair coming from PostgreSQL that's actually being converted to a ZonedTime, and the offset is not a whole number of minutes?

What isn't obvious about the existing interface is that it does support very limited access to the IANA timezone database, depending on the underlying operating system. In particular, utcToLocalZonedTime basically implements utcToLocalTimeWithOffset, except that it's limited to querying the local time zone that is set on the operating system, and is an IO action. For example, on Ubuntu 14.04:

$ ghci-6.8.3
GHCi, version 6.8.3: http://www.haskell.org/ghc/  :? for help
> import Data.Time
> utcToLocalZonedTime (read "1883-11-18 17:44:59")
1883-11-18 11:59:59 LMT
> utcToLocalZonedTime (read "1883-11-18 17:59:59")
1883-11-18 12:14:59 LMT
> utcToLocalZonedTime (read "1883-11-18 18:00:00")
1883-11-18 12:00:00 CST

In any case, the time package simply rounds the offset to the nearest minute towards negative infinity. This probably wasn't exactly an intended behavior, my guess is that the author simply assumed the offset would always be a whole number of minutes.

If postgresql-simple should handle this case instead of throwing an exception, then should we emulate this behavior, or might it be better to use a different rounding rule for conversions to ZonedTime?

lpsmith avatar Nov 14 '14 08:11 lpsmith

It turns out there is the tz package for full tz conversions on Hackage already, and it looks like it's not too unreasonable.

lpsmith avatar Jul 14 '15 04:07 lpsmith