I got a PostgreSQL database in which I have a date field in the invoices table:
Column | Type |
payment_date | timestamp with time zone |
The server is located at GMT-5, as you can see:
$date
Tue Jan 22 17:33:01 EST 2019
Getting the GMT time via a PostgreSQL command gives me:
select current_timestamp at time zone 'GMT';
timezone
----------------------------
2019-01-22 22:33:01.087354
The problem is when I do an insertion/update:
update invoices set payment_date = current_timestamp at time zone 'GMT'
Then, when I get the query result...
select payment_date from invoices
it gives me:
2019-01-22 22:33:01.087354-05
Wrong! should be giving me 2019-01-22 22:33:01.087354-00
What am I doing wrong?
Your field payment_date is of the (correct) type timestamp with time zone, so the timestamp without time zone that is the result of the AT TIME ZONE operation is converted using your session time zone.
PostgreSQL's timestamp with time zone data type does not store time zone information, it stores an UTC timestamp that is converted to the session time zone on display.
You are best advised to store the timestamp as timestamp with time zone and convert it to the desired time zone when you display it.
Related
I have data in GMT timezone. I want to convert and display them in 'America/New_York'.
This is the code I use:
(d.start_time) AT TIME ZONE 'America/New_York'
It returns let's say 2021-10-25T09:30:00.000Z.
9:30 is the correct time. But why is it a UTC time zone timestamp instead of 'America/New_York'?
I tried various variations of with/without time zone and ::timestamp/::timestamptz but I probably doesn't understand something very basic in here...
What should I do differently so that it's valid 9:30am timestamp in 'America/New_York' timezone?
The result is correct: 2021-10-25T09:30:00.000 is a timestamp without time zone, and it is not a UTC timestamp, but local time in New York City.
If you want the timestamp to be displayed with the time zone offset of New York City, you have to do something different:
SET timezone = 'America/New_York';
SELECT current_timestamp;
current_timestamp
═══════════════════════════════
2021-10-11 12:45:49.881037-04
(1 row)
Then PostgreSQL will display timestamp with time zone values with the offset you want.
I have a table with a time column defined as
time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
my database and the server have the timezone UTC
Tue Apr 20 18:22:50 UTC 2021
When I ran the following query
SELECT id, time AT TIME ZONE 'utc' AS "TZ: UTC", time AT TIME ZONE 'Europe/Berlin' AS "TZ: Berlin" FROM orders ORDER BY id DESC;
results to
id | TZ: UTC | TZ: Berlin
-----+------------------------------+------------------------------
600 | 2021-04-20 18:18:07.11372+00 | 2021-04-20 16:18:07.11372+00
I would have expected in the TZ: Berlin column is
2021-04-20 20:18:07.11372+00.
When I run the query
SELECT id, time AT TIME ZONE 'utc' AS "TZ: UTC", time AT TIME ZONE 'UTC+2' AS "TZ: UTC+2" FROM orders ORDER BY id DESC
which results to the expected
id | TZ: UTC | TZ: UTC+2
-----+------------------------------+------------------------------
600 | 2021-04-20 18:18:07.11372+00 | 2021-04-20 20:18:07.11372+00
At this time of the year (summer) the timezones UTC+2 and Europe/Berlin are identical.
So what am I missing?
Kind regards
PS: The Version is
$ postgres -V postgres
postgres (PostgreSQL) 12.6
When I run
SELECT NOW() at time zone 'Europe/Berlin';
I get
timezone
----------------------------
2021-04-20 20:41:57.155641
Adding AT TIME ZONE to a timestamp without time zone merely adds the time zone to the timestamp. In your case, the time was stored as:
2021-04-20 18:18:07.11372
Adding the UTC time zone didn't change the hour portion of the time because it matches your session time zone.
Adding the Europe/Berlin time zone had the following effect:
2021-04-20 18:18:07.11372 = the time in Berlin, so we subtract two hours to get the time in UTC.
The case of UTC+2 is even more confusing. I suggest reading more about the posix time specifications.
Note this important bit:
The positive sign is used for zones west of Greenwich.
It worked as you expected, but for the wrong reason.
This might make it more clear:
SELECT t,
t at time zone 'UTC' as utc,
t at time zone 'Europe/Berlin' as berlin,
t at time zone 'UTC+2' as somewhere_west_of_utc,
t at time zone 'UTC-2' as berlinish
FROM (
VALUES ('2021-04-20T12:00:00'::timestamp)
) as v(t);
t | utc | berlin | somewhere_west_of_utc | berlinish
---------------------+------------------------+------------------------+------------------------+------------------------
2021-04-20 12:00:00 | 2021-04-20 12:00:00+00 | 2021-04-20 10:00:00+00 | 2021-04-20 14:00:00+00 | 2021-04-20 10:00:00+00
(1 row)
Again, I would strongly advise you to use timestamp with time zone where all the timestamps will be stored as UTC.
If you execute AT TIME ZONE on a timestamp without time zone, you get the absolute time when a clock in that time zone shows that time.
When a clock in Berlin shows 18:00, it is 16:00 UTC.
I have a server that gives me some information grouped by day, so I tried to use date_trunc(), but because of the timezone I know that 2020-06-05 21:00:00 in my DB is actually 2020-06-06 00:00:00.
So if I just use date_trunc, I got 2020-06-05 00:00:00, but I need 2020-06-06 00:00:00.
I'm trying this:
select tm , date_trunc('day', (tm) at time zone '+3')
from scm.tbl
where (tm BETWEEN '2020-06-05 15:00:00+00:00:00' AND '2020-06-08 20:59:00+00:00:00')
order by tm
And I have this:
2020-06-05 17:59:59 | 2020-06-05 00:00:00
2020-06-05 18:00:10 | 2020-06-06 00:00:00
At 18:00 the date became 2020-06-06, but it shouldn't. Why does it? What am I doing wrong here?
The problem is that AT TIME ZONE will convert a timestamp without time zone into a timestamp with time zone, and that is again handled according to your current timezone setting.
So you need to use AT TIME ZONE twice, first to interpret the timestamp in the correct time zone, and then to extract what an UTC clock would show at that time:
SELECT tm, date_trunc('day', tm AT TIME ZONE '+3' AT TIME ZONE 'UTC')
FROM scm.tbl
WHERE (tm BETWEEN '2020-06-05 15:00:00+00:00:00' AND '2020-06-08 20:59:00+00:00:00')
ORDER BY tm;
I found my answer here timezone aware date_trunc function
When I saw this in the answer:
timestamp with time zone '2001-01-1 00:00:00+0100' at time zone '-02'
It's because tm is +03:00:00 and I'm adding it again...
So what I can do is:
date_trunc('day', tm at time zone '0')
I guess there's must be better way to do it, without '0'
I am experimenting with time zones.
My postgres table created as:
Table "public.xx"
Column | Type | Collation | Nullable | Default
--------+-----------------------------+-----------+----------+---------
dtz | timestamp with time zone | | |
dt | timestamp without time zone | | |
The server timezone is 'UTC'. I know this from show timezone;
Then I insert data:
insert into xx values( TIMESTAMP WITH TIME ZONE '2018-08-01 13:00:00+3', TIMESTAMP WITH TIME ZONE '2018-08-01 13:00:00+3' );
INSERT 0 1
tucha=> select * from xx;
dtz | dt
------------------------+---------------------
2018-08-01 10:00:00+00 | 2018-08-01 10:00:00
(1 row)
Results are easy to understand: the dates are stored in UTC thus 3 is subtracted.
Also notice that dtz have +00
Now when I use at time zone
tucha=> select dtz at time zone 'UTC', dt at time zone 'UTC' from xx;
timezone | timezone
---------------------+------------------------
2018-08-01 10:00:00 | 2018-08-01 10:00:00+00
(1 row)
The +00 is added for field which has no timezone and viceversa: no +00 for dtz field.
Why this happened?
a short answer is - because it is desined this way:
https://www.postgresql.org/docs/current/static/functions-datetime.html#FUNCTIONS-DATETIME-ZONECONVERT
timestamp without time zone AT TIME ZONE zone returns timestamp
with time zone
timestamp with time zone AT TIME ZONE zone returns
timestamp without time zone
Longer:
as you said your client works in UTC timezone and server always stores timestamptz in UTC, thus select times match (you use UTC everywhere).
first select timestamptz is shown with +00 because this is timezone aware field - and it shows you the time zone. dt knows nothing of time zones, thus shows none...
Now when you use AT TIME ZONE you ask to show timestamps at SOME SPECIFIC time zone, thus time zone aware data type shows you the time for specific zone (adding hours and so), but after "moving" time it can't be reused as time zone aware (because it's not server UTC time anymore), thus it hides TZ. Opposite the time that was not aware of time zone, when displayed at some specific time zone, gains that "awareness" and deducting hours it shows +00 so you would know you operated on TZ not aware time stamp. I think that is the logic here.
I have found good article how to work with timezones (RU):
So – timestamp at time zone gives timestamptz which represents the moment in time when local time at given zone was as given.
and timestamptz at time zone gives timestamp, which shows what the time was in given timezone at given point in time.
I have date and time fields in my table. Both are set in local server time.
Is it possible to cast both fields as a single UTC ISO timestamp?
Just add the two:
SELECT date_col + time_col AS timestamp_col
The type timestamp [without time zone] is stored as UTC timestamp internally anyway. Only the display is adjusted to the time zone setting of your session. If you need to display the timestamp as UTC timestamp, use the AT TIME ZONE construct:
SELECT timestamp_col AT TIME ZONE 'UTC';
Note that this returns a timestamp with time zone when applied to a timestamp.
Ample details:
Ignoring timezones altogether in Rails and PostgreSQL
For example, to display the timestamp as timestamptz in Moscow:
SELECT (date_col + time_col) AT TIME ZONE 'Europe/Moscow' AS tstz_col