Postgres how to convert milliseconds to date with day light saving - postgresql

I have date time stored as milliseconds in postgres DB as bigint. I need to convert them to Date Time. Function to_timestamp() is not taking into consideration day light saving settings.
For example I have the following values in DB:
select username, created_timestamp, to_timestamp(created_timestamp/1000) from user_entity;
username | created_timestamp | to_timestamp
--------------------------+-------------------+-----------------------
user_15 | 1567066962775 | 2019-08-29 08:22:42+00
user_16 | 1581145146219 | 2020-02-08 06:59:06+00
I would like to see the following result as a daylight saving effect:
user_15 | 29.08.2019, 09:22
user_16 | 08.02.2020, 06:59

If you set timezone to the time zone in which you want to see the strings formatted, you will get the result you desire:
SET timezone = 'Europe/London';
PostgreSQL formats timestamp with time zone to the current session time zone.

Related

PostgreSQL Format Timestamp with Timezone Offset (Australia/Sydney) as JSON/String

I would like to get a JSON object in Postgres that display timestamp with Australia/Sydney timezone offset (+10:00 or +11:00 during daylight saving), for example I would like json object returned with following values:
"2021-07-31T23:59:59.123456+10:00"
"2021-01-31T23:59:59.123456+11:00"
But when I use either to_json() or to_char() the timestamp value returned is UTC with offset +00:00
select to_json(current_timestamp::timestamptz),
to_char(current_timestamp::timestamptz, 'YYYY-MM-DD"T"HH24:MI:SS:MSOF')
"2021-01-31T07:47:22.895185+00:00"
2021-01-31T07:47:22:895+00
I have tried to add "at time zone 'AEDT'" but it shifts the timestamp value and keep the offset to +00:00.
Thanks.
to_json formats according to the current time zone setting of the session. I suggest that you set the session time zone to Australia/Sydney first.
set time zone 'Australia/Sydney';
select to_json('2021-01-31T07:47:22.895+00'::timestamptz);
Yields 2021-01-31T18:47:22.895+11:00 which I guess is what you need.

Postgres times: wrong conversion with time zones

Let's assume that we have some date, for example 2018-06-25T06:00:00Z. So, in case I do the following I get the same date:
select '2018-06-25T06:00:00Z'::timestamp at time zone 'UTC';
Now, I want to get local time for this date and as I am located in Helsinki I do the following:
select '2018-06-25T06:00:00Z'::timestamp at time zone 'Europe/Helsinki';
As I know that Helsinki is currently +3 hours my expectation is to get 2018-06-25 09:00:00.000000, however, for some reason I get 2018-06-25 03:00:00.000000.
So, why it decided to deduct this 3 hours instead of adding them?
Note, that this select current_setting('timezone') returns UTC.
Your second statement interprets the given timestamp as timestamp in the time zone Europe/Helsinki and converts it to a TIMESTAMP WITH TIMEZONE. psql prints the result of the timestamp according the the SESSION TIMEZONE.
To convert your timestamp from UTC to Europe/Helsinki, you should use a timestamp with time zone:
SELECT '2018-06-25T06:00:00Z'::TIMESTAMPTZ AT TIME ZONE 'Europe/Helsinki';
This will create a timestamp and convert it to Europe/Helsinki. You may change the time zone for output with:
SET SESSION TIMEZONE TO 'Europe/Helsinki';

Postgres "time zone at" isn't respecting mountain standard time when converting

I have a column starts_at with a type of TIMESTAMP WITHOUT TIME ZONE because it's representing the time of an appointment and should not change during a DST shift.
However, our library that handles recurring appointments needs this time in UTC. I am attempting to convert starts_at to UTC, but am seeing that I'm getting times representing MDT (daylight savings time) rather than MST (standard time).
For example, take the following:
SELECT starts_at, timezone('America/Denver', starts_at) AS new_starts_at
I would expect to get the following result:
--------------------------------------------------
| starts_at | new_starts_at
--------------------------------------------------
| 2018-09-04 13:05:00 | 2018-09-04 20:05:00+00
Instead, I'm getting the following:
--------------------------------------------------
| starts_at | new_starts_at
--------------------------------------------------
| 2018-09-04 13:05:00 | 2018-09-04 19:05:00+00
new_starts_at should be returning in MST, which would be 2018-09-04 20:05:00+00. My impression was that using the Olsen timezone (America/Denver) would inform Postgres of whether or not there was a DST shift in place. If I replace America/Denver with MST, I see the correct result.
I'm sure this is just a misunderstanding of Postgres timezone types on my part. That said, thanks in advance for the education!
The expression timezone('America/Denver', starts_at) interprets starts_at as being in Denver local time, the result is a timestamp with time zone.
Now when you output that value, it is transformed to your session time zone, which is UTC.
13:05 in Denver is 19:05 in UTC, which happens to be your session time zone.
During daylight savings time, Denver is offset 6 hours from UTC.

Why does Postgres not recognize my DST?

In my postgresql.conf file, my default timezone is set to 'Europe/London'.
Since the DST will be switched off on 30th October, this means that now (4th October while I am writing) 'Europe/London' should correspond with '+01'.
I have a dummy table with a time column of timestamp with timezone type.
Postgres specs state that:
For timestamp with timezone, the internally stored value is always in UTC (Universal Coordinated Time, traditionally known as Greenwich Mean Time, GMT). An input value that has an explicit time zone specified is converted to UTC using the appropriate offset for that time zone. If no time zone is stated in the input string, then it is assumed to be in the time zone indicated by the system's TimeZone parameter, and is converted to UTC using the offset for the timezone zone.
As for the bold sentence, if I make this insert:
INSERT INTO dummy VALUES ('2016-12-25 12:00:00.000')
I expect it to be interpreted as a 'Europe/London' timezone, thus '2016-12-25 12:00:00.000+01'.
Therefore, when I will retrieve it, I expect to be showed that value (or, at least, the equivalent '2016-12-25 11:00:00.000+00').
Instead, if I do the query
SELECT * FROM dummy
I am returned this:
|time |
|timestamp with time zone|
--------------------------
|2016-12-25 12:00:00+00 |
I can't get the reason of this behaviour. Is the DST management of 'Europe/London' timezone wrong? Am I missing something?
If I switch to any other timezone, it always works as expected.
The timestamp you enter is interpreted in the Europe/London time zone.
On the 25th of December, London will be offset 0 hours from UTC, so noon will be at 12:00 UTC.
Time zone Europe/London is not the same as time zone +01, at least not all the time.

PostgreSQL date() with timezone

I'm having an issue selecting dates properly from Postgres - they are being stored in UTC, but
not converting with the Date() function properly.
Converting the timestamp to a date gives me the wrong date if it's past 4pm PST.
2012-06-21 should be 2012-06-20 in this case.
The starts_at column datatype is timestamp without time zone. Here are my queries:
Without converting to PST timezone:
Select starts_at from schedules where id = 40;
starts_at
---------------------
2012-06-21 01:00:00
Converting gives this:
Select (starts_at at time zone 'pst') from schedules where id = 40;
timezone
------------------------
2012-06-21 02:00:00-07
But neither convert to the correct date in the timezone.
Basically what you want is:
$ select starts_at AT TIME ZONE 'UTC' AT TIME ZONE 'US/Pacific' from schedules where id = 40
I got the solution from this article is below, which is straight GOLD!!! It explains this non-trivial issue very clearly, give it a read if you wish to understand pstgrsql TZ management better.
Expressing PostgreSQL timestamps without zones in local time
Here is what is going on. First you should know that 'PST timezone is 8 hours behind UTC timezone so for instance Jan 1st 2014, 4:30 PM PST (Wed, 01 Jan 2014 16:00:30 -0800) is equivalent to Jan 2nd 2014, 00:30 AM UTC (Thu, 02 Jan 2014 00:00:30 +0000). Any time after 4:00pm in PST slips over to the next day, interpreted as UTC.
Also, as Erwin Brandstetter mentioned above, postresql has two type of timestamps data type, one with a timezone and one without.
If your timestamps include a timezone, then a simple:
$ select starts_at AT TIME ZONE 'US/Pacific' from schedules where id = 40
will work. However if your timestamp is timezoneless, executing the above command will not work, and you must FIRST convert your timezoneless timestamp to a timestamp with a timezone, namely a UTC timezone, and ONLY THEN convert it to your desired 'PST' or 'US/Pacific' (which are the same up to some daylight saving time issues. I think you should be fine with either).
Let me demonstrate with an example where I create a timezoneless timestamp. Let's assume for convenience that our local timezone is indeed 'PST' (if it weren't then it gets a tiny bit more complicated which is unnecessary for the purpose of this explanation).
Say I have:
$ select timestamp '2014-01-2 00:30:00' AS a, timestamp '2014-01-2 00:30:00' AT TIME ZONE 'UTC' AS b, timestamp '2014-01-2 00:30:00' AT TIME ZONE 'UTC' AT TIME ZONE 'PST' AS c, timestamp '2014-01-2 00:30:00' AT TIME ZONE 'PST' AS d
This will yield:
"a"=>"2014-01-02 00:30:00" (This is the timezoneless timestamp)
"b"=>"2014-01-02 00:30:00+00" (This is the UTC TZ timestamp, note that up to a timezone, it is equivalent to the timezoneless one)
"c"=>"2014-01-01 16:30:00" (This is the correct 'PST' TZ conversion of the UTC timezone, if you read the documentation postgresql will not print the actual TZ for this conversion)
"d"=>"2014-01-02 08:30:00+00"
The last timestamp is the reason for all the confusion regarding converting timezoneless timestamp from UTC to 'PST' in postgresql. When we write:
timestamp '2014-01-2 00:30:00' AT TIME ZONE 'PST' AS d
We are taking a timezoneless timestamp and try to convert it to 'PST TZ (we indirectly assume that postgresql will understand that we want it to convert the timestamp from a UTC TZ, but postresql has plans of its own!). In practice, what postgresql does is it takes the timezoneless timestamp ('2014-01-2 00:30:00) and treats it as if it WERE ALREADY a 'PST' TZ timestamp (i.e: 2014-01-2 00:30:00 -0800) and converts that to UTC timezone!!! So it actually pushes it 8 hours ahead instead of back! Thus we get (2014-01-02 08:30:00+00).
Anyway, this last (un-intuitive) behavior is the cause of all confusion. Read the article if you want a more thorough explanation, I actually got results which are a bit different then their on this last part, but the general idea is the same.
I don't see the exact type of starts_at in your question. You really should include this information, it is the key to the solution. I'll have to guess.
PostgreSQL always stores UTC time for the type timestamp with time zone internally. Input and output (display) are adjusted to the current timezone setting or to the given time zone. The effect of AT TIME ZONE also changes with the underlying data type. See:
Ignoring time zones altogether in Rails and PostgreSQL
If you extract a date from type timestamp [without time zone], you get the date for the current time zone. The day in the output will be the same as in the display of the timestamp value.
If you extract a date from type timestamp with time zone (timestamptz for short), the time zone offset is "applied" first. You still get the date for the current time zone, which agrees with the display of the timestamp. The same point in time translates to the next day in parts of Europe, when it is past 4 p.m. in California for instance. To get the date for a certain time zone, apply AT TIME ZONE first.
Therefore, what you describe at the top of the question contradicts your example.
Given that starts_at is a timestamp [without time zone] and the time on your server is set to the local time. Test with:
SELECT now();
Does it display the same time as a clock on your wall? If yes (and the db server is running with correct time), the timezone setting of your current session agrees with your local time zone. If no, you may want to visit the setting of timezone in your postgresql.conf or your client for the session. Details in the manual.
Be aware that the timezone offset used the opposite sign of what's displayed in timestamp literals. See:
Peculiar time zone handling in a Postgres database
To get your local date from starts_at just
SELECT starts_at::date
Tantamount to:
SELECT date(starts_at)
BTW, your local time is at UTC-7 right now, not UTC-8, because daylight savings time is in effect (not among the brighter ideas of the human race).
Pacific Standard TIME (PST) is normally 8 hours "earlier" (bigger timestamp value) than UTC (Universal Time Zone), but during daylight saving periods (like now) it can be 7 hours. That's why timestamptz is displayed as 2012-06-21 02:00:00-07 in your example. The construct AT TIME ZONE 'PST' takes daylight saving time into account. These two expressions yield different results (one in winter, one in summer) and may result in different dates when cast:
SELECT '2012-06-21 01:00:00'::timestamp AT TIME ZONE 'PST'
, '2012-12-21 01:00:00'::timestamp AT TIME ZONE 'PST'
I know this is an old one but You may want to consider using AT TIME ZONE "US/Pacific" when casting to avoid any PST/PDT issues. So
SELECT starts_at::TIMESTAMPTZ AT TIME ZONE "US/Pacific"
FROM schedules
WHERE ID = '40';