Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>The data type <a href="https://www.postgresql.org/docs/current/interactive/datatype-datetime.html" rel="noreferrer"><strong><code>timestamp</code></strong></a> is the short name for <strong><code>timestamp without time zone</code></strong>.<br> The other option <a href="https://www.postgresql.org/docs/current/interactive/datatype-datetime.html" rel="noreferrer"><strong><code>timestamptz</code></strong></a> is short for <strong><code>timestamp with time zone</code></strong>.</p> <p><code>timestamptz</code> is the <em>preferred</em> type in the date/time family, literally. It has <code>typispreferred</code> set in <a href="https://www.postgresql.org/docs/current/static/catalog-pg-type.html" rel="noreferrer"><code>pg_type</code></a>, which can be relevant:</p> <ul> <li><a href="https://stackoverflow.com/questions/14113469/generating-time-series-between-two-dates-in-postgresql/46499873#46499873">Generating time series between two dates in PostgreSQL</a></li> </ul> <h3>Epoch</h3> <p><strong><em>Internally</em></strong>, timestamps are stored as a count from an <a href="https://en.wikipedia.org/wiki/Epoch_%28reference_date%29" rel="noreferrer">epoch</a>. Postgres uses an epoch of the first moment of the first day of the year 2000 in UTC, that is, 2000-01-01T00:00:00Z. Eight <a href="https://en.wikipedia.org/wiki/Octet_%28computing%29" rel="noreferrer">octets</a> are used to store the count number. Depending on a compile time option, that number is either:</p> <ul> <li>An <strong>8-byte integer</strong> (default), with 0 to 6 digits of a fractional second</li> <li><strike>A floating point number (deprecated), with 0 to 10 digits of a fractional second, where precision quickly degrades for values further away from epoch.</strike><br> Modern Postgres installations use the 8-byte integer.</li> </ul> <p>Note that Postgres does <em>not</em> use <a href="https://en.wikipedia.org/wiki/Unix_time" rel="noreferrer">Unix time</a>. Postgres’ epoch is first moment of 2000-01-01 rather than Unix’ 1970-01-01. While Unix time has a resolution of whole seconds, Postgres keeps fractions of seconds. </p> <h3><code>timestamp</code></h3> <p>If you define a data type <strong><code>timestamp</code></strong> <code>[without time zone]</code> you are telling Postgres: "I am not providing a time zone explicitly, assume the current time zone instead. Postgres saves the timestamp <strong>as is</strong> - <em>ignoring</em> a time zone modifier if you should add one!</p> <p>When you later display that <code>timestamp</code>, you get back what you entered literally. With the same time zone setting all is fine. If the time zone setting for the session changes, so does the meaning of the <code>timestamp</code> - the <em>value</em> stays the same.</p> <h3><code>timestamptz</code></h3> <p>Handling of <strong><code>timestamp with time zone</code></strong> is subtly different. <a href="https://www.postgresql.org/docs/current/interactive/datatype-datetime.html#AEN5714" rel="noreferrer">I quote the manual here</a>:</p> <blockquote> <p>For <code>timestamp with time zone</code>, the internally stored value is <strong>always in UTC</strong> (Universal Coordinated Time ...)</p> </blockquote> <p>Bold emphasis mine. The <strong>time zone itself is never stored</strong>. It is an input modifier used to compute the according UTC timestamp, which is stored - or and output modifier used to compute the local time to display - with appended time zone offset. If you don't append an offset for <code>timestamptz</code> on input, the current time zone setting of the session is assumed. All computations are done with UTC timestamp values. If you have to (or may have to) deal with more than one time zone, use <code>timestamptz</code>.</p> <p>Clients like psql or pgAdmin or any application communicating via <a href="https://www.postgresql.org/docs/current/static/libpq.html" rel="noreferrer">libpq</a> (like Ruby with the pg gem) are presented with the timestamp plus offset for the <em>current time zone</em> or according to a <em>requested</em> time zone (see below). It is always the <em>same point in time</em>, only the display format varies. Or, <a href="https://www.postgresql.org/docs/current/interactive/datatype-datetime.html#DATATYPE-TIMEZONES" rel="noreferrer">as the manual puts it</a>:</p> <blockquote> <p>All timezone-aware dates and times are stored internally in UTC. They are converted to local time in the zone specified by the <a href="https://www.postgresql.org/docs/current/static/runtime-config-client.html#GUC-TIMEZONE" rel="noreferrer">TimeZone</a> configuration parameter before being displayed to the client.</p> </blockquote> <p>Consider this simple example (in psql):</p> <pre> db=# SELECT timestamptz '2012-03-05 20:00<b>+03</b>'; timestamptz ------------------------ 2012-03-05 18:00:00<b>+01</b> </pre> <p>Bold emphasis mine. <strong><em>What happened here?</em></strong><br> I chose an arbitrary time zone offset <code>+3</code> for the input literal. To Postgres, this is just one of many ways to input the UTC timestamp <code>2012-03-05 17:00:00</code>. The result of the query is <em>displayed</em> for the current time zone setting <em>Vienna/Austria</em> in my test, which has an offset <code>+1</code> during winter and <code>+2</code> during summer time: <code>2012-03-05 18:00:00+01</code>, because it falls into winter time.</p> <p>Postgres has already forgotten how this value has been entered. All it remembers is the value and the data type. Just like with a decimal number. <code>numeric '003.4'</code>, <code>numeric '3.40'</code> or <code>numeric '+3.4'</code> - all result in the exact same internal value.</p> <h3><code>AT TIME ZONE</code></h3> <p>As soon as you get a grasp on this logic, you can do anything you want. All that's missing now, is a tool to interpret or represent timestamp literals according to a specific time zone. That's where the <a href="https://www.postgresql.org/docs/current/interactive/functions-datetime.html#FUNCTIONS-DATETIME-ZONECONVERT" rel="noreferrer"><strong><code>AT TIME ZONE</code></strong></a> construct comes in. There are two different use cases. <code>timestamptz</code> is converted to <code>timestamp</code> and vice versa.</p> <p>To enter the UTC <code>timestamptz</code> <code>2012-03-05 17:00:00+0</code>:</p> <pre><code>SELECT timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC' </code></pre> <p>... which is equivalent to:</p> <pre><code>SELECT timestamptz '2012-03-05 17:00:00 UTC' </code></pre> <p>To display the same point in time as EST <code>timestamp</code> (Eastern Standard Time):</p> <pre><code>SELECT timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC' AT TIME ZONE 'EST' </code></pre> <p>That's right, <code>AT TIME ZONE 'UTC'</code> <em>twice</em>. The first one interprets the <code>timestamp</code> value as (given) UTC timestamp returning the type <code>timestamptz</code>. The second one converts the <code>timestamptz</code> to the <code>timestamp</code> in the given time zone 'EST' - what a clock in the time zone EST displays at this unique point in time.</p> <h3>Examples</h3> <pre><code>SELECT ts AT TIME ZONE 'UTC' FROM ( VALUES (1, timestamptz '2012-03-05 17:00:00+0') , (2, timestamptz '2012-03-05 18:00:00+1') , (3, timestamptz '2012-03-05 17:00:00 UTC') , (4, timestamp '2012-03-05 11:00:00' AT TIME ZONE '+6') , (5, timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC') , (6, timestamp '2012-03-05 07:00:00' AT TIME ZONE 'US/Hawaii') -- ① , (7, timestamptz '2012-03-05 07:00:00 US/Hawaii') -- ① , (8, timestamp '2012-03-05 07:00:00' AT TIME ZONE 'HST') -- ① <strike>, (9, timestamp '2012-03-05 18:00:00+1')</strike> -- ② loaded footgun! ) t(id, ts); </code></pre> <p>Returns 8 (or 9) <em>identical</em> rows with a timestamptz columns holding the same UTC timestamp <code>2012-03-05 17:00:00</code>. The 9th row sort of happens to work in my time zone, but is an evil trap. See below.</p> <p>① Rows 6 - 8 with time zone <strong><em>name</em></strong> and time zone <strong><em>abbreviation</em></strong> for Hawaii time are subject to DST (daylight saving time) and might differ, though not currently. A time zone name like <code>'US/Hawaii'</code> is aware of DST rules and all historic shifts automatically, while an abbreviation like <code>HST</code> is just a dumb code for a fixed offset. You may need to append a different abbreviation for summer / standard time. The <em>name</em> correctly interprets <em>any</em> timestamp at the given time zone. An <em>abbreviation</em> is cheap, but needs to be the right one for the given timestamp:</p> <ul> <li><a href="https://stackoverflow.com/questions/12042506/time-zone-names-with-identical-properties-yield-different-result-when-applied-to">Time zone names with identical properties yield different result when applied to timestamp</a></li> </ul> <p><sub><em>Daylight Saving Time is not among the brightest ideas humanity ever came up with.</em></sub></p> <p>② Row 9, marked as <em>loaded footgun</em> works <em>for me</em>, but only by coincidence. If you explicitly cast a literal to <code>timestamp [without time zone]</code>, <a href="https://www.postgresql.org/docs/current/interactive/datatype-datetime.html#AEN5700" rel="noreferrer">any time zone offset is ignored</a>! Only the bare timestamp is used. The value is then automatically coerced to <code>timestamptz</code> in the example to match the column type. For this step, the <code>timezone</code> setting of the current session is assumed, which happens to be the same time zone <code>+1</code> in my case (Europe/Vienna). But probably not in your case - which will result in a different value. In short: Don't cast <code>timestamptz</code> literals to <code>timestamp</code> or you lose the time zone offset.</p> <h2>Your questions</h2> <blockquote> <p>User stores a time, say March 17, 2012, 7pm. I don't want timezone conversions or the timezone to be stored.</p> </blockquote> <p>Time zone itself is never stored. Use one of the methods above to enter a UTC timestamp.</p> <blockquote> <p>I only use the users specified time zone to get records 'before' or 'after' the current time in the users local time zone.</p> </blockquote> <p>You can use one query for all clients in different time zones.<br> For absolute global time:</p> <pre><code>SELECT * FROM tbl WHERE time_col &gt; (now() AT TIME ZONE 'UTC')::time </code></pre> <p>For time according to the local clock:</p> <pre><code>SELECT * FROM tbl WHERE time_col &gt; now()::time </code></pre> <p>Not tired of background information, yet? <a href="https://www.postgresql.org/docs/current/interactive/datatype-datetime.html" rel="noreferrer">There is more in the manual.</a></p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload