0

I get the dates in a YYYYMMDD format. I need to convert this column to the timestamp with specific timezone. What is the best/easiest way to do that?

So if I have 20210101 I want to get 2021-01-01 00:00:00.000000 in my TZ.

Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
botafogo
  • 189
  • 7
  • 1
    [to_char(your_timestamp_column AT TIME ZONE 'MST', 'YYYY-MM-DD HH24:MI:SS.US')](https://www.postgresql.org/docs/14/functions-formatting.html) You can swap out 'MST' for your [desired timezone](https://www.postgresql.org/docs/14/functions-datetime.html#FUNCTIONS-DATETIME-ZONECONVERT). – Zegarek Dec 03 '21 at 11:27
  • 1
    `dates in a YYYYMMDD format` dates have no format, they're binary data. PostgreSQL [has a proper date type](https://www.postgresql.org/docs/9.1/datatype-datetime.html) that's only 4 bytes, far smaller than the 8 bytes used by that *string*. Use the correct data type. If you had a real date you'd only need to use `mydate AT TIME ZONE ...` – Panagiotis Kanavos Dec 03 '21 at 11:31
  • @PanagiotisKanavos good point. What I suggested returns desired output specified in the question, but most likely it is in fact the case that they want to parse `text` type date to proper `date` type, specifying timestamp upon import. My function would come in later, when they wish to get it back in that specified format. – Zegarek Dec 03 '21 at 11:38
  • Assuming the text can be parsed, or that it's in the format the developer assumes and not in `mmddyyyy` as someone asked for earlier today. There's no real benefit to storing dates as text and a *lot* of downsides – Panagiotis Kanavos Dec 03 '21 at 11:39

4 Answers4

2

Strictly speaking, your request is an oxymoron:

I want to get 2021-01-01 00:00:00.000000 in my TZ.

You show a timestamp (timestamp without time zone) literal, which is completely orthogonal to (and ignorant of) the concept of time zones.

But you want it "in my TZ", which would imply to a timestamptz (timestamp with time zone) value, where the corresponding literal includes a time offset like: 2021-01-01 00:00:00.000000+01.

Since the format YYYYMMDD is unambiguous ISO format, you can cast to date or timestamp directly, safely. A cast to timestamp assumes the time component 00:00 automatically. Produces your desired timestamp '2021-01-01 00:00'.

SELECT '20211203'::timestamp;

If you want the result type timestamp, we are done here.

If you want the result type timestamptz, there is a quick-and-dirty shortcut:

SELECT '20211203'::timestamptz;

The current time zone setting is assumed for the type cast. But this introduces a dependency on a runtime settings. Notoriously unreliable, only advisable for situations where you can be certain of the current setting ...

The sure and generally advisable way is to define the target time zone with the AT TIME ZONE construct explicitly. Say, your timezone is 'Europe/Vienna':

SELECT '20211203'::timestamp AT TIME ZONE 'Europe/Vienna';

Use a time zone name. Time zone abbreviations are treacherous for input conversion and may fail for daylight saving time (DST) or other bureaucratic nonsense. 'CET' (Central European Time) is appropriate for timestamps during "standard time". During DST periods, you'd have to use 'CEST' (Central European Summer Time).

DST is utter nonsense, but some countries, including the EU, still haven't managed to get rid of it.

db<>fiddle here - note that dbfiddle runs with time zone GB by default.

Now you have the timestamptz value representing the start of the day (00:00) in your given time zone. Don't be fooled by the display of timestamptz values. That's always adjusted to the current time zone setting, but it always represents that unique point in time, just with different ways to display it.

You do understand that the time zone itself is never stored in a timestamptz value, right? Even though timestamp with time zone sounds like it might. See:

To force a certain display use to_char() or some other functions to generate the desired string. There is a dedicated page Data Type Formatting Functions in the manual.

Related:

Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
1

This is a conversion to timestamp with time zone. You can format it to whatever text representation you wish. My timezone is East European Time, EET (GMT + 2), so

select to_date('20211203', 'YYYYMMDD')::timestamp at time zone 'EET';
-- 2021-12-03 00:00:00.000 +0200
Stefanov.sm
  • 11,215
  • 2
  • 21
  • 21
  • Hey, thanks for the reply. I'm on UCT (my DB settings), and executing your code I get: '2021-12-02 22:00:00.000000 +00:00' But what I want to get is always midnight, no matter which Zone I'm converting to. I want to get exactly what you did get: '-- 2021-12-03 00:00:00.000 +0200' – botafogo Dec 03 '21 at 13:50
  • Can you give me an example pls. – Stefanov.sm Dec 03 '21 at 13:52
  • @botafogo Check your `datestyle` and `timezone` settings. If you wish to see timestamps in `EET` for some reason, use `set timezone='EET';` and try that query again. In case you need it, `to_char()` equivalent for reading in that specific format later is `select to_char(your_timestamp_column, 'YYYY-MM-DD HH24:MI:SS.US TZHTZM');` – Zegarek Dec 03 '21 at 15:00
  • 1
    @botafogo Two things appeared to me. First, you might be mislead in thinking that "+0200" is a part of what you're saving to the database - it's actually only added on output as part of the format. Second, you might confuse what that `+0200` is: it does not come from the timestamp itself saying "this timestamp comes from a zone +2h away", it's just added to indicate "as seen in time zone shifted +2h0min from UTC". – Zegarek Dec 03 '21 at 15:46
0

The question seems to assume that timestamps can be formatted on write and that they retain time zone information. They can't and they don't, which splits the problem in two:

1: How to interpret text-based timestamps.

create table test_timestamps as 
select ('20211203'||'Japan')::timestamptz as "your_timestamp";

PostgreSQL saves that in binary format in UTC so the entire idea of formatting and time zone doesn't apply to a stored timestamp type data. It's only formatted on read and converted to a different time zone if required.

2: How to get them in a specific format on read.

select to_char(
         "your_timestamp" AT TIME ZONE 'UTC', 
         'YYYY-MM-DD HH24:MI:SS.US TZHTZM') 
from test_timestamps;

where at time zone is only necessary if you want it to return the timestamp as observed elsewhere.

Zegarek
  • 6,424
  • 1
  • 13
  • 24
  • @ErwinBrandstetter The results of your `select`s do depend on `TimeZone` setting. You can see the author getting `2021-12-02 22:00:00.000000 +00:00` while the other got `2021-12-03 00:00:00.000 +0200`. If all you do is execute the expression in psql or PGAdmin, it effectively displays the result, not only compute it - you'll find I referred to their specific context when using that word as well as 'depending'. If you state what is backwards in my answer, I'll be glad to correct it - after all, this is an attempt to clarify things for both myself and others. – Zegarek Dec 04 '21 at 09:03
  • @ErwinBrandstetter I already clarified I had referred to a `select` issued in a repl as a means to get a value displayed. The part about my understanding of how `at time zone` works is your assumption about things I did not state nor imply - I encourage you to explain what gave you that impression, and I don't see why I shouldn't edit my answer accordingly. The last sentence is correct; if you want get the timestamp in the format requested by the author, you don't need `at time zone` in the query proposed, unless you wish to get it converted to another time zone. Among **what** "others"? – Zegarek Dec 04 '21 at 15:39
  • @ErwinBrandstetter I don't think it was, but now you certainly did lead it there, especially after deleting your comments. Know that I edited my answer to swap out "displays" for "returns" for clarity, just in case. I'm genuinely thankful for the time you spent on your answers and your critique of mine, and wish you a nice day as well. – Zegarek Dec 04 '21 at 16:07
-1

To keep from over complicating things:

create table dt_test (id integer, ts_fld timestamp, tsz_fld timestamp with time zone);

insert into dt_test values (1, '20211203'::date, '20211203'::date);

 select * from dt_test ;
 id |       ts_fld        |        tsz_fld         
----+---------------------+------------------------
  1 | 2021-12-03 00:00:00 | 2021-12-03 00:00:00-08

A date will be taken as Midnight for timestamp purposes. So either just use it as date knowing it will be Midnight or do the explicit cast:

select '20211203'::date::timestamp, '20211203'::date::timestamptz;
      timestamp      |      timestamptz       
---------------------+------------------------
 2021-12-03 00:00:00 | 2021-12-03 00:00:00-08

--Which can be shortened to:

select '20211203'::timestamp, '20211203'::timestamptz;
      timestamp      |      timestamptz       
---------------------+------------------------
 2021-12-03 00:00:00 | 2021-12-03 00:00:00-08

Depending on whether you want to retain the time zone offset or not. For portability 2021-12-03 00:00:00-08 would be the better choice.

Adrian Klaver
  • 15,886
  • 2
  • 17
  • 28