5

I'm hesitant to revisit this overly discussed topic, but I created a set of tables to store data and many of the tables included a field named "createdate" designated as "timestamp without time zone". The date/time values fed to these by the code base are always in UTC. It is intended that the UI will be able to control how the data is presented to the user, so some setting would dictate to convert to time zone in the UI.

Some of these timestamps will be for reports, so to display info for the end user. Other times, the values will be used to determine nightly jobs run against the data.

This is a typical is a multi-tenant cloud hosted system with clients across different time zones. The server should never be moved, although I suppose changing hosting zones is a very remote possibility. It was written on the .net platform. Not using noda time, just the built in DateTime stuff (for now).

The docs are pretty clear how the timestamp with time zone stores the info: https://www.postgresql.org/docs/current/datatype-datetime.html

This answer also has good background on the difference in the two main timestamp data types: https://stackoverflow.com/a/14616640/1905693

This answer also has some good info, but is Java-oriented: What is the most recommended way to store time in PostgreSQL using Java?

And Josh Berkus has a dated article that was helpful: https://it.toolbox.com/blogs/josh-berkus/zone-of-misunderstanding-092811

It seems most of these recommend the timestamp with time zone, but in my case, is timestamp without time zone appropriate?

If I did want to rely on pg to do the conversion, would the AT TIME ZONE clause be ok?

From an overall system architecture, is relying on the UI to change presentation a common and reasonable approach? (yeah, this one may be too opinion-flavored for SO's format)

Matthew Allen
  • 538
  • 2
  • 7
  • 14
  • Use timestamptz, it is rooted to UTC. timestamp is not so it depends on external information to be useful. As long as the external information does not change that is not a problem, if it does change then you are up a creek. – Adrian Klaver Jul 24 '20 at 18:51

3 Answers3

11

Not a moment

As others said, TIMESTAMP WITHOUT TIME ZONE is the wrong data type.

That type holds only a date with time-of-day, for example noon on January 21st of 2021. But we cannot know if that means noon in Tokyo Japan, noon in Toulouse France, or noon in Toledo Ohio US — three very different moments, several hours apart from one another. So this type cannot represent a moment, is not a specific moment on the timeline.

The TIMESTAMP WITHOUT TIME ZONE type is good for three kinds of use-cases:

  • Representing multiple moments all known the same in their locality. For example, Acme Corp orders the manager at each factory in Delhi, Düsseldorf, and Detroit, to make an announcement in two days at their local time of noon.
  • Representing a date and time-of-day where the intended time zone is unknown. I consider this faulty data that should be rejected. But if you insist on writing it to the database, this type would be appropriate.
  • Booking future appointments where we want to keep the time-of-day even if those pesky politicians change the offset of the time zone(s) in their jurisdiction. These political changes happen surprisingly often. So book an appointment using two columns: TIMESTAMP WITHOUT TIME ZONE in one, and the name of the intended time zone in another. Time zones are named with Continent/Region format such as Africa/Tunis. At runtime, when you need a moment for calendaring, apply the time zone to the date and time to dynamically determine a moment according to the now-current time zone rules. In Noda Time, you would retrieve a LocalDateTime and time zone, to produce a ZonedDateTime for calendaring.

Moments

When you care about moments, specific points on the timeline, use TIMESTAMP WITH TIME ZONE.

In Postgres and many other databases, this type has a bit of a misnomer. The time zone is not actually saved with the date and time. Instead, upon submission to the database, any indicator of time zone or offset-from-UTC is used to adjust to UTC (an offset of zero hours-minutes-seconds). That UTC value is what Postgres writes to the database. And UTC is what Postgres always retrieves from the database.

Beware: Some middleware and tooling has the well-intentioned but very confusing anti-feature of dynamically applying a default time zone to the retrieved value, adjusting from UTC to that time zone. This creates the illusion of that time zone having been saved with the data. But, no, not so. A TIMESTAMP WITH TIME ZONE column stores only UTC values.

Use case examples:

  • Tracking the moment when a database record was created, modified, or deleted.
  • Logging for debugging or sysadmin work.
  • Tracking when a critical contract is signed or comes into effect.
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • Your use cases are exactly what I'm looking for, Basil. I'm still a bit unclear what timezonetz would do for me in my specific case where the date/times being submitted to my tables are already in UTC. The plan is for the UI to allow the end user to specify what time zone they want their data to be displayed in. I'm not sure if that is easier than having it done in pg or not, though, so perhaps that should be our design discussion as opposed to using timezonetz vs timezone in the db. – Matthew Allen Jul 26 '20 at 16:29
  • 1
    @MatthewAllen If you store your UTC moments in `TIMESTAMP WITHOUT TIME ZONE`, you are throwing away vital information. You would be discarding the fact that the date and time are in UTC — and *you’ve gained nothing*. Yes, you could trust that programmers and users would assume the retrieved date and time is in UTC. But they might just as well assume the date and time was meant for their own time zone, or the time zone of the company’s headquarters. Why make them play that guessing game? If you use `TIMESTAMP WITH TIME ZONE`, then everybody knows the date and time are in UTC, with no guessing. – Basil Bourque Jul 26 '20 at 17:32
  • And yes, adjusting from UTC to a particular time zone for the convenient reading by the user should be treated like localization, a matter for the user-interface coding, not the database or backend coding. If the time zone is dictated by the business rules rather user’s convenience, say a contract requires all date-times be stated in `America/New_York` zone, then the opposite applies. – Basil Bourque Jul 26 '20 at 17:38
  • I think the thing that makes this most clear for me is when you said timestamptz means they will know for sure the data stored is UTC, whereas timezone they would just have to assume all layers followed the design to use raw UTC. Really in the end it doesn't cost anything for me to just use WITH TIMEZONE anyhow. I'll start migrating my database (still early design phase). Thanks to you and Erwin for the patience and input. – Matthew Allen Jul 26 '20 at 19:28
  • 1
    @MatthewAllen For your .Net programming, I recommend the Noda Time library. This project is a port of the highly successful Joda-Time project, a wisely designed date-time handling library, the predecessor to the *java.time* classes built into Java 8 and later. So you can search Stack Overflow for either Joda-Time and/or *java.time* to learn the concepts for working in Noda Time. – Basil Bourque Jul 26 '20 at 21:43
7

Like you have seen repeatedly in the sources you quote, timestamp with time zone (timestamptz) is the more appropriate choice in all cases where different time zones may be involved.

Internally, storage is the same. But with timestamptz, Postgres already knows where timestamp value applies, while it is just oblivious in this regard with timestamp without time zone (timestamp).

Note that the given time zone is not stored. That's a common misunderstanding. It's only used to compute UTC time. See:

AT TIME ZONE is the SQL construct to use to get the corresponding time for a given time zone.

SELECT my_timestamptz AT TIME ZONE 'US/Hawaii';

With timestamp it gets awkward:

SELECT my_timestamp AT TIME ZONE 'UTC' AT TIME ZONE 'US/Hawaii';

Also, when using timestamp any appended time zone information in literals is ignored, which is typically not what you want.

See:

Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
  • In my case the code devs have decided that all times sent to my database are already in UTC, so I wouldn't know the time zone anyway. The user can open their data in whatever time zone they dictate the UI to convert it to. I do see, though, if I wanted to assume the time zone conversion task in the database, that timezonetz is more appropriate. Perhaps my issue should be more of an architecture design issue to that matter. Thanks, Erwin, I always appreciate reading your thorough pg responses. – Matthew Allen Jul 26 '20 at 16:24
  • @MatthewAllen No, using `TIMESTAMP WITH TIME ZONE` to store your UTC date-time value has *nothing* to do with later adjusting into other time zones and whether you do so in database or in the app. The issue is capturing correct and full information in your official record-keeping. If you have a date and time intended for a zone or offset (zero offset for UTC in your case), then you write to a `TIMESTAMP WITH TIME ZONE` column to capture all three facts (date, time, offset). There is no upside, no benefit, to writing to `TIMESTAMP WITHOUT TIME ZONE` to store only 2 of your 3 pieces of info. – Basil Bourque Jul 26 '20 at 17:51
1

I usually set the timezone of the role I am using?

ALTER ROLE my_db_user IN DATABASE my_database
    SET "TimeZone" TO 'UTC';

Seems to help getting the DateTime correctly.

Rui Lima
  • 7,185
  • 4
  • 31
  • 42