@@ -9,23 +9,23 @@ This module provides time types that supports positive and negative
99infinity, as well as some functions for converting to and from strings.
1010
1111Also, this module also contains commentary regarding postgresql's timestamp
12−types, timekeeping in general, and how it relates to postgresql-simple.
13−You can read more about PostgreSQL's date and time types at
14−<http://www.postgresql.org/docs/9.1/static/datatype-datetime.html>,
15−and the IANA timezone database at <https://en.wikipedia.org/wiki/Tz_database>.
12+types, civil timekeeping in general, and how it relates to
13+postgresql-simple. You can read more about PostgreSQL's date and time types
14+at <http://www.postgresql.org/docs/9.1/static/datatype-datetime.html>,
15+and the IANA time zone database at <https://en.wikipedia.org/wiki/Tz_database>.
1616
1717Stack Overflow also has some excellent commentary on time, if it is a
1818wiki page or a highly upvoted question and answer. If the answer regarding
19−time has not received hundreds of upvotes, then the answer is almost
20−invariably completely and painfully wrong, even if it's the chosen answer
21−or the most highly upvoted answer to a question.
19+time has not received about a hundred upvotes at least, then the answer is
20+almost invariably completely and painfully wrong, even if it's the chosen
21+answer or the most highly upvoted answer to a question.
2222
2323PostgreSQL's @timestamp with time zone@ (hereafter, @timestamptz@) can be
2424converted to Haskell's 'Data.Time.UTCTime' and 'Data.Time.ZonedTime' types,
25−because their values represent an unambiguous point in time. PostgreSQL's
26−@timestamp without time zone@ (hereafter, @timestamp@) can be converted
27−to Haskell's 'Data.Time.LocalTime', because values of these types can only be
28−disambiguated in a larger context.
25+because values of these types represent a self-contained, unambiguous point
26+in time. PostgreSQL's @timestamp without time zone@ (hereafter, @timestamp@)
27+can be converted to Haskell's 'Data.Time.LocalTime', because values of these
28+types are ambiguous by themselves, and require context to disambiguate.
2929
3030While this behavior may be superficially counterintuitive because the
3131names might suggest otherwise, this behavior is correct. In fact,
@@ -42,43 +42,53 @@ such as @+00@, @-05@, or @+05:30@. A standard time specifies an offset
4242(which may vary throughout the year, due to daylight savings) that a
4343region follows, such as Universal Coordinated Time (UTC), Eastern Standard
4444Time\/Eastern Daylight Time (EST\/EDT), or India Standard Time (IST).
45−And a timezone, much like a standard time, is a function from
45+And a time zone, much like a standard time, is a function from
4646timestamps to offsets.
4747
48−A timezone is different from a standard time because different regions
49−inside a standard time are governed by different civil authorities and
50−have different histories of time. Thus, an IANA timezone is any region
51−of the world that has had the same history of time since
52−@1970-01-01 00:00+00@.
48+A time zone is different from a standard time because different regions
49+inside a standard time can be governed by different civil authorities with
50+different laws and thus have different histories of civil time. An IANA
51+time zone is any region of the world that has had the same history of
52+civil time since @1970-01-01 00:00+00@.
5353
5454For example, as of today, both @America\/New_York@ and
5555@America\/Indiana\/Indianapolis@ are on the EST\/EDT time standard, but
5656Indiana used to be on Central Standard Time until 1942, and did not observe
5757daylight savings time (EST only) until 2006. Thus, the choice between
58−these two timezones still matters if you are dealing with timestamps
58+these two time zones still matters if you are dealing with timestamps
5959prior to 2006, and could become relevant again if (most of) Indiana
6060moves back to Central Time. (Of course, if the Central to Eastern switch
61−was the only difference, then these two timezones would be the same in
61+was the only difference, then these two time zones would be the same in
6262IANA's eyes, due to their cutoff date of 1970-01-01.)
6363
64−Getting back to the coding practicalities, PostgreSQL's @timestamptz@
65−type does not actually store an offset; rather, it uses the offset
66−provided to calculate UTC, and stores the timestamp as UTC. If an
67−offset is not provided, the given timestamp is assumed to be a local
68−time for whatever the @timezone@ variable is set to, and the IANA TZ
69−database is consulted to calculate an offset from UTC for the time in
70−question. Note that some local timestamps are ambiguous, in which
71−case PostgreSQL chooses one of the possible offsets.
72−
73−When retrieving a @timestamptz@, the backend looks at the @timezone@
64+Getting back to practicalities, PostgreSQL's @timestamptz@ type does not
65+actually store an offset; rather, it uses the offset provided to calculate
66+UTC, and stores the timestamp as UTC. If an offset is not provided, the
67+given timestamp is assumed to be a local time for whatever the @timezone@
68+variable is set to, and the IANA TZ database is consulted to calculate an
69+offset from UTC for the time in question.
70+
71+Note that while most (local timestamp, time zone) pairs correspond to exactly
72+one UTC timestamp, some correspond to two UTC timestamps, while others
73+correspond to none at all. The ambiguous case occurs when the civil time
74+is rolled back, making a calendar day longer than 24 hours. In this case,
75+PostgreSQL silently chooses the second, later possibility. The inconsistent
76+case occurs when the civil time is moved forward, making a calendar day less
77+than 24 hours. In this case, PostgreSQL silently assumes the local time
78+was read off a clock that had not been moved forward at the prescribed time,
79+and moves the clock forward for you. Thus, converting from local time
80+to UTC need not be monotonic, if these inconsistent cases are allowed.
81+
82+When retrieving a @timestamptz@, the backend looks at the @time zone@
7483connection variable and then consults the IANA TZ database to calculate
75−an offset for the timestamp in the given timezone. Note that while some
76−of the information contained in the IANA TZ database is a bit of a
77−standardized fiction, the conversion from UTC time to a
78−(local time, offset) pair is always unambiguous, and the result can
79−always be unambiguously converted back to UTC. Thus, postgresql-simple
80−can interpret this result as a 'Data.Time.ZonedTime', or use the offset to
81−convert to 'Data.Time.UTCTime'.
84+an offset for the timestamp in the given time zone.
85+
86+Note that while some of the information contained in the IANA TZ database
87+is a bit of a standardized fiction, the conversion from UTC time to a
88+(local time, offset) pair in a particular time zone is always unambiguous,
89+and the result can always be unambiguously converted back to UTC. Thus,
90+postgresql-simple can interpret such a result as a 'Data.Time.ZonedTime',
91+or use the offset to convert back to 'Data.Time.UTCTime'.
8292
8393By contrast, the @timestamp@ type ignores any offsets provided to it,
8494and never sends back an offset. Thus, postgresql-simple equates this
@@ -87,55 +97,68 @@ convert between @timestamptz@ and @timestamp@ using the @AT TIME ZONE@
8797operator, whose semantics also demonstrates that @timestamptz@ is
8898'Data.Time.UTCTime' whereas @timestamp@ is 'Data.Time.LocalTime'.
8999
90−The default default setting for the @timezone@ variable is @\'localtime\'@,
91−which normally corresponds to the server's local timezone. However,
92−this default can be modified on the server side for an entire cluster, or
93−on a per-user or per-database basis. On the client side, the timezone can
94−be set via the @PGTZ@ environment variable on the client, or be modified at
95−any time for the duration of the entire connection, just a single
96−transaction, or the execution context of a server-side function.
100+PostgreSQL's @timezone@ is a per-connection variable that by default is
101+initialized to @\'localtime\'@, which normally corresponds to the server's
102+time zone. However, this default can be modified on the server side for an
103+entire cluster, or on a per-user or per-database basis. Moreover, a client
104+can modify their instance of the variable at any time, and can apply that
105+change to the remaining duration of the connection, the current transaction,
106+or the execution context of a server-side function. In addition, upon
107+connection initialization, the libpq client checks for the existence of
108+the @PGTZ@ environment variable, and if it exists, modifies @timezone@
109+accordingly.
97110
98111With a few caveats, postgresql-simple is designed so that you can both send
99112and receive timestamps with the server and get a correct result, no matter
100113what the @timezone@ setting is. But it is important to understand the caveats:
101114
1021151. The correctness of server-side computations can depend on the @timezone@
103− setting, for example when adding an @interval@ to a @timestamptz@, or
104− when type casting between @timestamp@ and @timestamptz@.
116+ setting. Examples include adding an @interval@ to a @timestamptz@, or
117+ type casting between @timestamp@ and @timestamptz@, or applying
118+ the @DATE@ function to a @timestamptz@.
105119
106−2. The offset in a 'Data.Time.ZonedTime' result will depend on the
107− @timezone@ setting, although the result will always represent the
108− same instant in time regardless of the offset.
120+2. The (localtime, offset) pair contained in a 'Data.Time.ZonedTime' result
121+ will depend on the @timezone@ setting, although the result will always
122+ represent the same instant in time regardless of the time zone.
109123
1101243. Sending a 'Data.Time.LocalTime' and interpreting it as a @timestamptz@
111125 can be useful, as it will be converted to UTC via the tz database,
112126 but correctness will depend on the @timezone@ setting. You may prefer
113− to use an explicit @AT TIME ZONE@ instead.
127+ to use an explicit @AT TIME ZONE@ conversion instead, which would avoid
128+ this contextual dependence.
114129
115−Furthermore, although these points don't involve the @timezone@ setting,
116−they are related to the last point above:
130+Furthermore, although these following points don't involve the @timezone@
131+setting, they are related to the last point above:
117132
1181331. Sending a 'Data.Time.UTCTime' and interpreting it as a @timestamp@ can
119134 be useful. In practice, the most common context used to disambiguate
120135 @timestamp@ is that it represents UTC, and this coding technique will
121− work as expected in this context.
136+ work as expected in this situation.
122137
1231382. Sending a 'Data.Time.ZonedTime' and interpreting it as a @timestamp@ is
124139 almost always the wrong thing to do, as the offset will be ignored and
125140 discarded. This is likely to lead to inconsistencies in the database,
126− and may lead to partial data loss. Moreover, one should keep in mind
127− that "ZonedTime" is misnamed, and one should not conflate an offset
128− with a timezone.
141+ and may lead to partial data loss.
142+
143+When dealing with local timestamps that refer to the future, it is often
144+useful to store it as a local time in a @timestamp@ column and store the
145+time zone in a second column. One reason to do this is so that you can
146+convert to UTC on the fly as needed, and be protected against future changes
147+to the TZ database due to changes in local time standards. In any case,
148+'Data.Time.ZonedTime' is not suitable for this application, because despite
149+its name, it represents an offset and not a time zone. Time zones can change;
150+offsets do not. In reality, we can't convert a local timestamp that occurs
151+sufficiently far in the future to UTC, because we don't know how to do it yet.
129152
130153There are a few limitations and caveats that one might need to be aware
131154of with the current implementation when dealing with older timestamps:
132155
133−For sufficiently old timestamps in almost all timezones, the IANA TZ
156+For sufficiently old timestamps in almost all time zones, the IANA TZ
134157database specifies offsets from UTC that is not an integral number of
135158minutes. This corresponds to local mean time; that is, astronomical
136−time in the city that defines the timezone. Different timezones moved
159+time in the city that defines the time zone. Different time zones moved
137160away from local mean time to a standard time at different points in
138−history, so \"sufficiently old\" depends on the timezone in question.
161+history, so \"sufficiently old\" depends on the time zone in question.
139162
140163Thus, when retrieving a @timestamptz@ postgresql will in some cases
141164provide seconds in the offset. For example:
@@ -176,7 +199,7 @@ generate this syntax. Unfortunately this syntax isn't convenient to
176199print or especially parse.
177200
178201Also, postgresql itself cannot parse or print dates before @4714-11-24 BC@,
179− which is the Julian date on the proleptic Gregorian Calendar. Although
202+which is the Julian date on the proleptic Gregorian Calendar. Although
180203postgresql's timestamp types are perfectly capable of representing timestamps
181204nearly 300,000 years in the past, using this would require postgresql-simple
182205and other client programs to support binary parameters and results.