Now() without timezone - postgresql

I have a column added_at of type timestamp without time zone. I want it's default value to be the current date-time but without time zone. The function now() returns a timezone as well.
How do I solve that problem?

SELECT now()::timestamp;
The cast converts the timestamptz returned by now() to the corresponding timestamp in your time zone - defined by the timezone setting of the session. That's also how the standard SQL function LOCALTIMESTAMP is implemented in Postgres.
If you don't operate in multiple time zones, that works just fine. Else switch to timestamptz for added_at. The difference?
Ignoring time zones altogether in Rails and PostgreSQL
BTW, this does exactly the same, just more noisy and expensive:
SELECT now() AT TIME ZONE current_setting('timezone');

Well you can do something like:
SELECT now() AT TIME ZONE current_setting('TimeZone');
SELECT now() AT TIME ZONE 'Europe/Paris';
SELECT now() AT TIME ZONE 'UTC';
Not sure how that makes any sense for a column "added_at". You almost always want an absolute timestamp (timestamp with time zone) not a floating one.
Edit responding to points below:
Yes, should use timestamp with time zone (absolute time) unless you have a good reason not to.
The client timezone is given by SHOW TimeZone or current_setting(...) as shown above.
Do take some time to skim the manuals - they cover all this quite well.

"Current Date/Time":
CURRENT_TIME and CURRENT_TIMESTAMP deliver values with time zone; LOCALTIME and LOCALTIMESTAMP deliver values without time zone.

New, and Native Answer in 2020
In PostgreSQL, If you only want the current date-time by calling CURRENT_TIMESTAMP() without time zone, and fractional digits in the seconds field which come after the decimal point of the seconds field?
(Tested on PostgreSQL v12.4)
Then use this:
SELECT CURRENT_TIMESTAMP(0)::TIMESTAMP WITHOUT TIME ZONE;
If you define your column's data type as timestamp (not as timestamptz), then you can store the timestamp without time zone, in that case you don't neet to add TIMESTAMP WITHOUT TIME ZONE
Like this:
CREATE TABLE foo (created timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP(0))
In the above function, 0 is passed to get rid of the fractional digits in the seconds field.

If your application doesn't care about timezone, you can use SELECT LOCALTIMESTAMP for it.
Ex:
SELECT LOCALTIMESTAMP
-- Result: 2023-01-30 17:43:33.628952

Related

how does Postgres handle timezone comparison?

Seeking help understanding Postgres timezone conversion and comparison
query
http://sqlfiddle.com/#!17/9eecb/79009
SELECT
now()
, ( now() at time zone 'AEDT' ) AS now_AEDT
, ( ( now() at time zone 'AEDT' ) at time zone 'AEDT' ) AS now_AEDT_AEDT
, ( now() at time zone 'UTC' ) + interval '1' second
> ( now() at time zone 'AEDT' ) AS should_be_true
Result
now : 2021-07-30T01:03:10.707834Z (i assume system is UTC?)
now_AEDT : 2021-07-30T12:03:10.707834Z
now_AEDT_AEDT : 2021-07-30T01:03:10.707834Z (why is this different?)
should_be_true : false (after adding a second; this should be true, no?)
questions / confusions
why does double-setting the timezone change the time? (now_AEDT_AEDT)
how are timestamps with timezones handled in comparison? It seems the zone is ignored and just the values are compared?
"at time zone" does opposite things when applied to timestamptz versus a timestamp. So applying it twice in a row just gives you back the original. First it does something, then it undoes it.
When applied to timestamptz, it converts the time to look like what it would be expressed as in the indicated time zone, and datatypes it as a timestamp without timezone (except in sqlfiddle, where it seems to do something slightly different, but without changing the overall effect). When applied to a timestamp without timezone, it assumes that that time expressed was already in the indicated time zone, and converts it back to the system time with it datatyped as timestamptz.
how are timestamps with timezones handled in comparison? It seems the zone is ignored and just the values are compared?
You aren't comparing timestamps with timezones. You are comparing timestamps without timezones.
select pg_typeof(now() at time zone 'AEDT');
pg_typeof
-----------------------------
timestamp without time zone
So yes, it ignores the timezones in your comparison, because they are no longer there anymore.

PostgreSQL "date at time zone" unexpected behaviour

The behavior of the at time zone operator on the date type is not as expected in PostgreSQL.
set time zone 'UTC';
select
'2021-01-03'::date at time zone 'America/Chicago' as "value",
pg_typeof('2021-01-03'::date at time zone 'America/Chicago') as "type";
x
value
type
Expected
2021-01-03 06:00:00+00
timestamp with time zone
Actual
2021-01-02 18:00:00
timestamp without time zone
DB Fiddle
Tested on PostgreSQL server versions 10, 11, 12, and 13.
This behavior is very strange to me, and I have not been able to find anywhere it is described or discussed. It seems PSQL is implicitly casting date to timestamptz (using the system time zone) and then applying the at time zone operator. I know that the at time zone operator is only defined (in the docs) for the timestamp(tz) and time(tz) types. However, I would have assumed PSQL would implicitly cast date to timestamp and not timestamptz because:
It is a "safe" conversion (no need to assume a time zone). Simply append 00:00:00.
It results in a timestamptz which one would expect when using the at time zone operator on a type that does not have a time zone.
The current behavior simply doesn't make sense to me and I can't think of a use case that would benefit from it. You are asking for the timestamp with time zone for a date at a particular time zone and instead you get a timestamp of the date at the system time zone localized to the given time zone.
Of course, a simple workaround is to cast to timestamp first:
set time zone 'UTC';
select '2021-01-03'::date::timestamp at time zone 'America/Chicago';
I was hoping someone could shed some light on this. What am I missing? Perhaps some overarching rule about how date is implicitly cast to timestamp(tz)? Is this behavior documented somewhere?
Thanks!
Mike
You ask "why". The reason is that AT TIME ZONE does not operate on date, so PostgreSQL invokes an implicit type cast to convert it to a different data type. Lacking an explicit directive, PostgreSQL opts for the preferred data type for datetime, which happens to be timestamp with time zone.
The data type resolution rules for operators are documented here, and the preferred type for a type category can be found in the typispreferred and typcategory columns of the pg_type system catalog.
As per the Postgresql documentation:
The AT TIME ZONE converts time stamp without time zone to/from time stamp with time zone, and time values to different time zones
Here in first query, you are trying to apply at time zone to date type, not to time stamp
set time zone 'UTC';
select
'2021-01-03'::date at time zone 'America/Chicago' as "value",
pg_typeof('2021-01-03'::date at time zone 'America/Chicago') as "type";
which is eventually gets ignored, and you will get a timestamp without timezone
But in second query,
set time zone 'UTC';
select '2021-01-03'::date::timestamp at time zone 'America/Chicago';
You are casting the date to time stamp first and then converting it to desired time zone,
which gives you the expected result of time stamp with time zone.
Hope this clears your doubt.

date_trunc at time zone with original timestamptz

I have a single timestamptz that I want to date_trunc so it removes the hours:
2019-01-01T17:43-03 => 2019-01-01T00:00-03.
However, because date_trunc removes the timezone, I need to do it like this:
date_trunc('day', '2019-01-01T17:43-03'::timestamptz) at time zone '-03'
However, I do not want to hardcode the time zone, since the query is run with timestamptz in many different timezones (these are input to the query and not stored). So I want the timezone to be extracted from the original timestamp. I tried to do something like this, but it does not work:
date_trunc('day', '2019-01-01T17:43-03'::timestamptz) at time zone EXTRACT(...)
Related, I am trying to extract the timezone from a timestamptz, but just getting 0.
SELECT EXTRACT(timezone FROM TIMESTAMP WITH TIME ZONE '2019-01-01T00:00+03')
0
Can anybody help me with this?
I believe you may have a misconception about how timestamps and timezones are stored in PostgreSQL (because you seem to expect the -03 to be preserved when calling date_trunc and that you "want the timezone to be extracted from the original timestamp"). According to the documentation:
When a timestamp with time zone value is output, it is always converted from UTC to the current timezone zone, and displayed as local time in that zone. To see the time in another time zone, either change timezone or use the AT TIME ZONE construct (see Section 9.9.3).
Therefore, the statement that "different clients [that] have timestampts in many different timezones," while true, are all translated to UTC for storage, and then displayed in your local timezone (or the timezone you specify) for output. As such, calling date_trunc() will essentially truncate the UTC timestamp, and if you want it displayed in a specific timezone, you will need to add the AT TIME ZONE clause.
UPDATE: An example is here:
edb=# select date_trunc('day', '2019-01-01T17:43-03'::timestamptz) ;
date_trunc
------------------------
2019-01-01 00:00:00+00
(1 row)
edb=# set timezone to 'US/Pacific';
SET
edb=# select date_trunc('day', '2019-01-01T17:43-03'::timestamptz) ;
date_trunc
------------------------
2019-01-01 00:00:00-08
(1 row)
As you can see, date_trunc will append the timezone that I defineā€”it is not omitted.

Converting UTC time in local time in PostgreSQL 8.3

I run a Postgres 8.3 database where times seem to be stored in UTC without time zone.
I am trying to display in local time but not with '+01' suffix :
With select scheduled_start_ts I get :
2014-01-20 05:01:35.663
With select scheduled_start_ts at time zone 'MET' :
2014-01-20 05:01:35.663+01
I would like to get "2014-01-20 06:01:35.663" which is in local time.
The database I am using cannot be modified and I am not allowed to modify how data are stored.
If you want to format times, use the to_char function. See formatting functions in the docs.
regress=> SELECT to_char(
(TIMESTAMP '2014-01-20 05:01:35.663' AT TIME ZONE 'UTC')
AT TIME ZONE 'MET',
'YYYY-MM-DD HH:MI:SS'
);
to_char
---------------------
2014-01-20 06:01:35
(1 row)
The (TIMESTAMP 'xxx' AT TIME ZONE 'UTC') gets me a timestamptz with the correct time, by re-interpreting the TIMESTAMP as being in UTC. The second AT TIME STAMP instead converts the timestamptz into a timestamp in timezone MET. This then gets formatted.
Whatever the SQL standards committe were smoking when they designed this, I never, ever, ever want to be anywhere near it.

Using current time in UTC as default value in PostgreSQL

I have a column of the TIMESTAMP WITHOUT TIME ZONE type and would like to have that default to the current time in UTC. Getting the current time in UTC is easy:
postgres=# select now() at time zone 'utc';
timezone
----------------------------
2013-05-17 12:52:51.337466
(1 row)
As is using the current timestamp for a column:
postgres=# create temporary table test(id int, ts timestamp without time zone default current_timestamp);
CREATE TABLE
postgres=# insert into test values (1) returning ts;
ts
----------------------------
2013-05-17 14:54:33.072725
(1 row)
But that uses local time. Trying to force that to UTC results in a syntax error:
postgres=# create temporary table test(id int, ts timestamp without time zone default now() at time zone 'utc');
ERROR: syntax error at or near "at"
LINE 1: ...int, ts timestamp without time zone default now() at time zo...
A function is not even needed. Just put parentheses around the default expression:
create temporary table test(
id int,
ts timestamp without time zone default (now() at time zone 'utc')
);
Still another solution:
timezone('utc', now())
Wrap it in a function:
create function now_utc() returns timestamp as $$
select now() at time zone 'utc';
$$ language sql;
create temporary table test(
id int,
ts timestamp without time zone default now_utc()
);
What about
now()::timestamp
If your other timestamp are without time zone then this cast will yield the matching type "timestamp without time zone" for the current time.
I would like to read what others think about that option, though. I still don't trust in my understanding of this "with/without" time zone stuff.
EDIT:
Adding Michael Ekoka's comment here because it clarifies an important point:
Caveat. The question is about generating default timestamp in UTC for
a timestamp column that happens to not store the time zone (perhaps
because there's no need to store the time zone if you know that all
your timestamps share the same). What your solution does is to
generate a local timestamp (which for most people will not necessarily
be set to UTC) and store it as a naive timestamp (one that does not
specify its time zone).
These are 2 equivalent solutions:
(in the following code, you should substitute 'UTC' for zone and now() for timestamp)
timestamp AT TIME ZONE zone - SQL-standard-conforming
timezone(zone, timestamp) - arguably more readable
The function timezone(zone, timestamp) is equivalent to the SQL-conforming construct timestamp AT TIME ZONE zone.
Explanation:
zone can be specified either as a text string (e.g., 'UTC') or as an interval (e.g., INTERVAL '-08:00') - here is a list of all available time zones
timestamp can be any value of type timestamp
now() returns a value of type timestamp (just what we need) with your database's default time zone attached (e.g. 2018-11-11T12:07:22.3+05:00).
timezone('UTC', now()) turns our current time (of type timestamp with time zone) into the timezonless equivalent in UTC.
E.g., SELECT timestamp with time zone '2020-03-16 15:00:00-05' AT TIME ZONE 'UTC' will return 2020-03-16T20:00:00Z.
Docs: timezone()
Function already exists:
timezone('UTC'::text, now())