There are no bulletproof solutions here.
My first advice: never rely on the default timezone of the server.
My second advice: choose between timestamp
-timestamptz
according to the (predominant) semantics of the data.
In more detail:
PostgresSQL has two timestamp variants, confusingly named TIMESTAMP WITHOUT TIMEZONE (timestamp)
and TIMESTAMP WITH TIMEZONE (timestamptz)
. Actually, neither stores a timezone, nor even an offset. Both datatypes occupy the same width (4 bytes), and their difference is subtle - and, worse, can bite you if you don't fully understand them and your server changes the timezone. My sanity ruleset is:
Use TIMESTAMP WITH TIMEZONE (timestamptz)
for storing events that are predominantly related to the "physical" time, for which you are mainly interested in querying whether event 1
was before event 2
(regardless of timezones), or computing time intervals (in "physical units", eg, seconds; not in "civil" units as days-months, etc). The typical example are record creation/modification time - what one usually means by the word "Timestamp".
Use TIMESTAMP WITHOUT TIMEZONE (timestamp)
for storing events for which the relevant information is the "civil time" (that is, the fields {year-month-day hour-min-sec}
as a whole), and the queries involve calendar calculations. In this case, you would store here only the "local time", i.e., the date-time relative to some unspecified (irrelevant, or implied, or stored somewhere else) timezone.
The second option makes you easier to query for, say, "all events that happened on day '2013-01-20'" (in each corresponding region/country/timezone) - but makes it more difficult to query for "all events that ocurred (physically) before a reference event" (unless we know they are in the same timezone). You choose.
If you need the full thing, neither is enough, you need either to store the timezone or the offset in an additional field. Another option, which wastes a few bytes but can be more efficient for queries is to store both fields.
See also this answer.