timestamptz format

I have searched for quite some time but I can't seem to figure it out. I can store a timestamptz with many formats and supabase will handle it and thats fine. My main problem is I want to parse the timestamptz but I dont know the exact pattern. Is it "%Y-%m-%d %H:%M:%S%.f%z" Identifiers used are defined here: https://docs.rs/chrono/latest/chrono/format/strftime/index.html It would be of great help if you could help me define it in the above defined syntax. Here is a example of how the time was stored (this was exported via csv): (with default value now) id,test,timestamp 0,[],2024-01-15 21:38:27.213678+00 (with default value (now() at time zone 'utc')) id,test,timestamp 1,[],2024-01-15 21:40:52.21653+00 //They are literally the same I am living in germany btw.
chrono::format::strftime - Rust
strftime/strptime-inspired date and time formatting syntax.
85 Replies
vick
vick2y ago
It is ISO-8601 with the addition that the T separator can be a space.
mercy
mercyOP2y ago
as far as i've read iso-8601 is pretty diverse there if rfc3339 for example which is a application ofit
vick
vick2y ago
The DB default timezone is UTC, so your two examples are identical
mercy
mercyOP2y ago
Is there a way to tell the db to use the rfc3339 timestamp when createing a timestamp?
vick
vick2y ago
Postgres is really good at timezones and timestamps. I really wish the APIs Supabase provdes would leverage them better. You can format the timestamp however you want when querying. The internal storage is irrelevent.
mercy
mercyOP2y ago
with as_char right
vick
vick2y ago
yeah.
mercy
mercyOP2y ago
now comes the next problem i havent found any good as_char conversion function to pull it of meaning to convert to the rfc3339 definition
vick
vick2y ago
Do you need to do it in the DB or your backend app server (or even the client). Javascript parses the dates no problem, for example. What are you trying to format it to be?
mercy
mercyOP2y ago
DB or backend either is fine tbh rfc3339 or rfc2822 would be cool cause would make my life easier in rust. chrono has these 2 functions:
vick
vick2y ago
RFC 3999 is suppposed to allow space instead of "T" but I cannot see that in a quick glance (found it). The timestamptz is already in this format. and maybe possibly the timezone offset in the RFC requires the ":MM" for minutes which Postgres is not providing. that would be really pedantic if your parser didn't accept that.
mercy
mercyOP2y ago
" This date/time format may be used in some environments or contexts that distinguish between the upper- and lower-case letters 'A'-'Z' and 'a'-'z' (e.g. XML). Specifications that use this format in such environments MAY further limit the date/time syntax so that the letters 'T' and 'Z' used in the date/time syntax must always be upper case. Applications that generate this format SHOULD use upper case letters. NOTE: ISO 8601 defines date and time separated by "T". Applications using this syntax may choose, for the sake of readability, to specify a full-date and full-time separated by (say) a space character." per iso its okay but the application doesnt allow it i can also custom parse
vick
vick2y ago
Quick fix: replace any space with "T"
mercy
mercyOP2y ago
i am not forced to do it or use it i can also provide my own format and it will parse it happily
vick
vick2y ago
Then just use the BNF grammar from the ISO spec 🙂
mercy
mercyOP2y ago
Thats why i originally asked for a "exact pattern. Is it "%Y-%m-%d %H:%M:%S%.f%z" Identifiers used are defined here: https://docs.rs/chrono/latest/chrono/format/strftime/index.html "
chrono::format::strftime - Rust
strftime/strptime-inspired date and time formatting syntax.
mercy
mercyOP2y ago
what is a bnf grammar
vick
vick2y ago
Well, the Z may be any timezone offset. In Postgres it won't give you Z it will give you +00 or the offset of timezone set in the client This part of the RFC:
date-fullyear = 4DIGIT
date-month = 2DIGIT ; 01-12
date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on
; month/year

date-fullyear = 4DIGIT
date-month = 2DIGIT ; 01-12
date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on
; month/year

mercy
mercyOP2y ago
yes there is that problem
vick
vick2y ago
RFC 3999 says it can be either Z or the offset.
mercy
mercyOP2y ago
only replacing it with T will lead to problems i think Hmmm
vick
vick2y ago
The rust docs says %z will parse the offset, so it should work (I'm not a rust programmer, but it looks very similar to C's definition)
mercy
mercyOP2y ago
already tried it
vick
vick2y ago
looks like you want %#z to allow missing minutes
mercy
mercyOP2y ago
and i have 2 questions are minutes represented as (i.e.) +0110 or +01:10
vick
vick2y ago
what if you just made postgres return the seconds since epoch? if rust can't handle that, i'd just give up 🙂 I have not tried half-hour timezones... let me try one. India is easy.
mercy
mercyOP2y ago
I am using the timestamptz to store the time a file was last changed
vick
vick2y ago
if you're working in UTC they're the same.
mercy
mercyOP2y ago
how would i change it anyways? i havent seen any options in supabase to change it to my local timezone cet so I can ignore the minutes
vick
vick2y ago
You cannot change the core DB to any other timezone. However, when your client connects you can set that connection to whatever timezone you want.
mercy
mercyOP2y ago
i mean i would change the last modified column with a trigger or wait i cant i need to set it manuall y its application specific
vick
vick2y ago
for example, here are two representations of the same timestamp from my database in different timezones:
postgres=> select visible_from from vendor_record where vendor_record_id=1;
visible_from
-----------------------------
2024-01-15 19:47:51.7006+00
(1 row)

Time: 9.446 ms
postgres=> set timezone='Asia/Kolkata';
SET
Time: 4.693 ms
postgres=> select visible_from from vendor_record where vendor_record_id=1;
visible_from
--------------------------------
2024-01-16 01:17:51.7006+05:30
(1 row)

Time: 8.221 ms
postgres=> select visible_from from vendor_record where vendor_record_id=1;
visible_from
-----------------------------
2024-01-15 19:47:51.7006+00
(1 row)

Time: 9.446 ms
postgres=> set timezone='Asia/Kolkata';
SET
Time: 4.693 ms
postgres=> select visible_from from vendor_record where vendor_record_id=1;
visible_from
--------------------------------
2024-01-16 01:17:51.7006+05:30
(1 row)

Time: 8.221 ms
If I present a string to the DB with the "+05:30" it will do the right thing when saving it.
mercy
mercyOP2y ago
i mean i am using rust for the timestamps and i can create them to be UTC saving it to UTC
vick
vick2y ago
The DB doesn't care the timezone you specify like update foo set mytimestamp='2024-01-16 01:17:51.7006+05:30' will be identical to set mytimestamp='2024-01-15 19:47:51.7006+00' internally it will store the same instant in time.
mercy
mercyOP2y ago
aight so that will not be a concern
vick
vick2y ago
when you query it, it check "what is the timezone of the client connected to me" and formats to that timezone. As you see in my prior example where I set the client timezone to Kolkata. The defaults for everything is UTC because that's how Supabase set it up.
mercy
mercyOP2y ago
nice and the connections are UTC by default lets assume this case right this means in rust i'd have to use %:z but in the %:z there is not the option for the minutes to be missing
vick
vick2y ago
you need the %#z to make the minutes optional.
mercy
mercyOP2y ago
but %#z is the same as %z and %z is +0930
vick
vick2y ago
interesting. they don't have a %#:z variant that you want here 😦
mercy
mercyOP2y ago
yes tried both just now they dont work neither
vick
vick2y ago
if you don't explicitly set your timezone, Supabase will always return the timestamp formatted with +00
mercy
mercyOP2y ago
i think the best way would be to set the times myself (via rust) because i think when you set them with a different format and try to retrieve them theyll still remain in that format
vick
vick2y ago
just don't change your client timezone. doesn't matter the timezone you specify on insert
mercy
mercyOP2y ago
this would work for my case but i plan on giving it to people
vick
vick2y ago
You could force your client to set timezone='UTC' and then not worry about it.
mercy
mercyOP2y ago
maybe but when i tested it and i put a different format (that is still ISO 8601 compliant) for a timestamptz and the downloaded that row as csv it showed the timestamptz in the format i specified it in true but there is still the miliseconds problem lil
vick
vick2y ago
that doesn't make sense it would leave your original format. it always parses the timestamp to store in its own internal format. and formats for output format.
mercy
mercyOP2y ago
No description
vick
vick2y ago
are you looking at your CSV in Excel? there's no difference there. the fractional seconds are zero so are omitted
mercy
mercyOP2y ago
no in vscode and nvim
vick
vick2y ago
just like all the extra zeros after the 47 in the first row.
mercy
mercyOP2y ago
but i am talking about the case where there are actually fractional seconds
vick
vick2y ago
it will keep them.
mercy
mercyOP2y ago
cant define one format in rust and ship another from the db
vick
vick2y ago
up to some precision. i don't recall offhand what that precision is.
mercy
mercyOP2y ago
6 or some
vick
vick2y ago
then you have to use the to_char() function to format the timestamps to your specification.
mercy
mercyOP2y ago
yes hmmm can i encode the to_char to alway ship with UTC
vick
vick2y ago
but the input still doesn't matter as they are parsed and stored in the internal format.
mercy
mercyOP2y ago
regardless of the connection let me try it yeah youre right maybe i mixed some files up and it seemed like it it will automatically convert it I am using the postgrest-rs api to communicate to the server
vick
vick2y ago
There's no way to change the client timezone with the postgrest interface. for fun:
postgres=> select extract(epoch from visible_from) from vendor_record where vendor_record_id=1;
extract
-------------------
1705348071.700600
(1 row)

Time: 4.629 ms
postgres=>
\q
[Vicks-M1-Air]% date -r 1705348071
Mon Jan 15 14:47:51 EST 2024
[Vicks-M1-Air]% date -r 1705348071 -u
Mon Jan 15 19:47:51 UTC 2024
postgres=> select extract(epoch from visible_from) from vendor_record where vendor_record_id=1;
extract
-------------------
1705348071.700600
(1 row)

Time: 4.629 ms
postgres=>
\q
[Vicks-M1-Air]% date -r 1705348071
Mon Jan 15 14:47:51 EST 2024
[Vicks-M1-Air]% date -r 1705348071 -u
Mon Jan 15 19:47:51 UTC 2024
representation doesn't matter. it is all the same instant in time.
mercy
mercyOP2y ago
so wait i shouldnt worry about it right
vick
vick2y ago
every language can handle an epoch time integer, so that is always my go-to when I have to worry about parsing dates.
mercy
mercyOP2y ago
meaning i can ignore the + and everything that comes after it
vick
vick2y ago
i would document the heck out of that assumption, but it should hold.
mercy
mercyOP2y ago
why dont we all use it
vick
vick2y ago
because humans can't parse it. and of course the 2038 problem 🙂
mercy
mercyOP2y ago
true
vick
vick2y ago
so maybe don't use it.
mercy
mercyOP2y ago
wait right yes back to to_char
vick
vick2y ago
👍
mercy
mercyOP2y ago
do you maybe know that wait
vick
vick2y ago
I don't know off hand.
mercy
mercyOP2y ago
it doesnt matter anyways
vick
vick2y ago
more fun. Pg can deal with 2038+
postgres=> select extract(epoch from '2044-01-15 19:47:51.7006+00'::timestamptz) from vendor_record where vendor_record_id=1;
extract
-------------------
2336500071.700600
postgres=> select extract(epoch from '2044-01-15 19:47:51.7006+00'::timestamptz) from vendor_record where vendor_record_id=1;
extract
-------------------
2336500071.700600
mercy
mercyOP2y ago
too dangerous
vick
vick2y ago
yeah. i agree.
mercy
mercyOP2y ago
i am using the rest api so i think i have to pack this up into a rpc
vick
vick2y ago
yes, you will have to.
mercy
mercyOP2y ago
and on insert i can use any iso8601 compliant format (or so i hope)
vick
vick2y ago
insert can parse a lot of arbitrary time-looking strings. but some may be ambiguous, so stick to the ISO format for best results.
mercy
mercyOP2y ago
thank you i think i found a good solution switch the rust time library For anyone wondering why my solution was: I switched to the time create and replaced the space between date and time with a "T" and afterwards parsed it with let date = OffsetDateTime::parse("2044-01-15T19:47:51.213424+01", &Iso8601::DEFAULT); Does the trick quite reliably

Did you find this page helpful?