Thread: to_timestamp alternatives

to_timestamp alternatives

From
gkhan
Date:
Hi. I have a practical need to convert some badly-formatted date/times into
'timestamp without time zone' data types.  Like other scientists, I try to
avoid timezone problems by sticking to UTC and using the 'timestamp without
time zone' data type whenever possible.

In this case, I used the to_timestamp() function as follows:
SELECT to_timestamp('09.03.2014'||' '||lpad('3:00:00',8,'0'),'DD.MM.YYYY
HH24:MI:SS')

...but I discovered that the command above gives me the same result one hour
earlier:
SELECT to_timestamp('09.03.2014'||' '||lpad('2:00:00',8,'0'),'DD.MM.YYYY
HH24:MI:SS').

That's because to_timestamp was silently converting into my local time zone
(UTC -7), even though I was putting the result into a 'timestamp without
time zone' variable.  Like commenters on the thread "to_timestamp() and
timestamp without time zone", I consider the silent conversion to be bad
behavior, or at least I wish that the documentation warned the user more
clearly, and I would greatly prefer a function that just dealt in UTC.

In the recent thread "BUG #12739: to_timestamp function conver string to
time incorrectly", tom lane suggests avoiding to_timestamp().  However, I
don't see an easy way to get around it in my case.  Can anyone suggest a
good alternative?  Please note that I want to avoid relying on global
variables such as 'SET TIMEZONE = ...' if possible, since those just
introduce more potential for confusion, IMHO.

Thanks!



--
View this message in context: http://postgresql.nabble.com/to-timestamp-alternatives-tp5879723.html
Sent from the PostgreSQL - general mailing list archive at Nabble.com.


Re: to_timestamp alternatives

From
Adrian Klaver
Date:
On 12/31/2015 12:30 PM, gkhan wrote:
> Hi. I have a practical need to convert some badly-formatted date/times into
> 'timestamp without time zone' data types.  Like other scientists, I try to
> avoid timezone problems by sticking to UTC and using the 'timestamp without
> time zone' data type whenever possible.
>
> In this case, I used the to_timestamp() function as follows:
> SELECT to_timestamp('09.03.2014'||' '||lpad('3:00:00',8,'0'),'DD.MM.YYYY
> HH24:MI:SS')
>
> ...but I discovered that the command above gives me the same result one hour
> earlier:
> SELECT to_timestamp('09.03.2014'||' '||lpad('2:00:00',8,'0'),'DD.MM.YYYY
> HH24:MI:SS').
>
> That's because to_timestamp was silently converting into my local time zone
> (UTC -7), even though I was putting the result into a 'timestamp without
> time zone' variable.  Like commenters on the thread "to_timestamp() and
> timestamp without time zone", I consider the silent conversion to be bad
> behavior, or at least I wish that the documentation warned the user more
> clearly, and I would greatly prefer a function that just dealt in UTC.
>
> In the recent thread "BUG #12739: to_timestamp function conver string to
> time incorrectly", tom lane suggests avoiding to_timestamp().  However, I
> don't see an easy way to get around it in my case.  Can anyone suggest a
> good alternative?  Please note that I want to avoid relying on global
> variables such as 'SET TIMEZONE = ...' if possible, since those just
> introduce more potential for confusion, IMHO.

Why not simplify:

test=> select ('09.03.2014'||' '||lpad('3:00:00',8,'0'))::timestamp;
       timestamp
---------------------
  2014-09-03 03:00:00
(1 row)

test=> select ('09.03.2014'||' '||lpad('2:00:00',8,'0'))::timestamp;
       timestamp
---------------------
  2014-09-03 02:00:00
(1 row)

>
> Thanks!
>
>
>
> --
> View this message in context: http://postgresql.nabble.com/to-timestamp-alternatives-tp5879723.html
> Sent from the PostgreSQL - general mailing list archive at Nabble.com.
>
>


--
Adrian Klaver
adrian.klaver@aklaver.com


Re: to_timestamp alternatives

From
Tom Lane
Date:
gkhan <drjohnpayne@gmail.com> writes:
> Hi. I have a practical need to convert some badly-formatted date/times into
> 'timestamp without time zone' data types.  Like other scientists, I try to
> avoid timezone problems by sticking to UTC and using the 'timestamp without
> time zone' data type whenever possible.

> In this case, I used the to_timestamp() function as follows:
> SELECT to_timestamp('09.03.2014'||' '||lpad('3:00:00',8,'0'),'DD.MM.YYYY
> HH24:MI:SS')

I think you're wasting your time with to_timestamp.  The timestamp type
itself is perfectly capable of parsing this, and most other reasonable
inputs too.

regression=# set datestyle = dmy;
SET
regression=# select '09.03.2014 03:00:00'::timestamp;
      timestamp
---------------------
 2014-03-09 03:00:00
(1 row)

In particular, since what to_timestamp() returns is timestamp WITH time
zone, converting its result to timestamp WITHOUT time zone will cause a
timezone rotation which is what is messing you up.  If you feel you really
must do things this way, set the timezone parameter to "UTC" so there's no
zone conversion.

> In the recent thread "BUG #12739: to_timestamp function conver string to
> time incorrectly", tom lane suggests avoiding to_timestamp().  However, I
> don't see an easy way to get around it in my case.  Can anyone suggest a
> good alternative?  Please note that I want to avoid relying on global
> variables such as 'SET TIMEZONE = ...' if possible, since those just
> introduce more potential for confusion, IMHO.

You haven't provided one bit of convincing explanation as to why you
reject doing things in the multiple ways that will work, and insist on
doing it in a way that won't.

If your statement that you want to work exclusively in UTC isn't really
true, and you have a reason to want the global setting of TIMEZONE to be
something else, you could consider making a wrapper function that sets
TIMEZONE to UTC transiently while invoking to_timestamp and then coercing
its result to timestamp without time zone.  Something like

create function to_timestamp_utc(text, text) returns timestamp
  as $$ begin return to_timestamp($1, $2)::timestamp; end; $$
  language plpgsql
  strict
  immutable
  set timezone = utc;

            regards, tom lane


Re: to_timestamp alternatives

From
gkhan
Date:
Thanks very much for both of your replies.  I had tried something similar and
gotten an error, so I am probably making a stupid mistake.  If I try this,
it works:

   SELECT ('09.03.2014'||' '||lpad('3:00:00',8,'0'),'DD.MM.YYYY
HH24:MI:SS')::timestamp

but if I use column names instead of the text, like this, it fails:
   SELECT (gmt_date||' '||lpad(gmt_time),'DD.MM.YYYY HH24:MI:SS')::timestamp
...

Both the gmt_date and gmt_time columns are "text" data type and formatted
exactly as in the original example, but I get this error:
   ERROR:  cannot cast type record to timestamp without time zone

I certainly would prefer "doing things in the multiple ways that will work,"
and I have probably just missed the obvious solution.  I work on wildlife
telemetry, and most GPS units and other satellite-linked devices report
times in UTC.  We often run into problems where someone plugs a laptop into
a piece of equipment and downloads data, and inadvertently sets the times to
a local time zone.  Therefore, we try to stick to UTC whenever collating
data from different sources.  However, when studying wildlife activity
patterns we are interested in local, biologically-meaningful times such as
sunrise and sunset, but not in daylight savings times, which are meaningless
to wildlife.  Therefore, most of us just add a fixed interval to UTC to
represent "local" times.




--
View this message in context: http://postgresql.nabble.com/to-timestamp-alternatives-tp5879723p5879738.html
Sent from the PostgreSQL - general mailing list archive at Nabble.com.


Re: to_timestamp alternatives

From
gkhan
Date:
...I meant to add, "and we therefore try to store all time and date values in
'timestamp without time zone' variables.



--
View this message in context: http://postgresql.nabble.com/to-timestamp-alternatives-tp5879723p5879739.html
Sent from the PostgreSQL - general mailing list archive at Nabble.com.


Re: to_timestamp alternatives

From
"David G. Johnston"
Date:
On Thu, Dec 31, 2015 at 2:34 PM, gkhan <drjohnpayne@gmail.com> wrote:
Thanks very much for both of your replies.  I had tried something similar and
gotten an error, so I am probably making a stupid mistake.  If I try this,
it works:

   SELECT ('09.03.2014'||' '||lpad('3:00:00',8,'0'),'DD.MM.YYYY
HH24:MI:SS')::timestamp

but if I use column names instead of the text, like this, it fails:
   SELECT (gmt_date||' '||lpad(gmt_time),'DD.MM.YYYY HH24:MI:SS')::timestamp
...

Both the gmt_date and gmt_time columns are "text" data type and formatted
exactly as in the original example, but I get this error:
   ERROR:  cannot cast type record to timestamp without time zone

​Um, both fail for the same reason.​  You added ", 'DD.MM.YYYY HH24:MI:ss'" to the parenthesized expression which turns it into an adhoc record type instead of simply performing grouping.  Basically you wrote: ROW(literal, literal)::timestamp.

You cannot pass arguments here, which is what your format expression is.  But the casting mechanism understands most comment formats are will try them until it fails, runs out of possibilities, or succeeds.

David J.

Re: to_timestamp alternatives

From
gkhan
Date:
Oh sorry, what a dumb mistake!  ::timestamp works, of course!

Thanks



--
View this message in context: http://postgresql.nabble.com/to-timestamp-alternatives-tp5879723p5879746.html
Sent from the PostgreSQL - general mailing list archive at Nabble.com.


Re: to_timestamp alternatives

From
Adrian Klaver
Date:
On 12/31/2015 01:34 PM, gkhan wrote:
> Thanks very much for both of your replies.  I had tried something similar and
> gotten an error, so I am probably making a stupid mistake.  If I try this,
> it works:
>
>     SELECT ('09.03.2014'||' '||lpad('3:00:00',8,'0'),'DD.MM.YYYY
> HH24:MI:SS')::timestamp
>
> but if I use column names instead of the text, like this, it fails:
>     SELECT (gmt_date||' '||lpad(gmt_time),'DD.MM.YYYY HH24:MI:SS')::timestamp
> ...
>
> Both the gmt_date and gmt_time columns are "text" data type and formatted
> exactly as in the original example, but I get this error:
>     ERROR:  cannot cast type record to timestamp without time zone
>
> I certainly would prefer "doing things in the multiple ways that will work,"
> and I have probably just missed the obvious solution.  I work on wildlife
> telemetry, and most GPS units and other satellite-linked devices report
> times in UTC.  We often run into problems where someone plugs a laptop into
> a piece of equipment and downloads data, and inadvertently sets the times to
> a local time zone.  Therefore, we try to stick to UTC whenever collating
> data from different sources.  However, when studying wildlife activity
> patterns we are interested in local, biologically-meaningful times such as
> sunrise and sunset, but not in daylight savings times, which are meaningless
> to wildlife.

But not necessarily to the interaction of wildlife with humans. As
someone who used to drive for a living I can tell you DST/ST moving
human activity backwards and forwards through biologically-meaningful
times has meaning to the critters.

> Therefore, most of us just add a fixed interval to UTC to
> represent "local" times.
>
>
>
>
> --
> View this message in context: http://postgresql.nabble.com/to-timestamp-alternatives-tp5879723p5879738.html
> Sent from the PostgreSQL - general mailing list archive at Nabble.com.
>
>


--
Adrian Klaver
adrian.klaver@aklaver.com


Re: to_timestamp alternatives

From
gkhan
Date:
Follow-up:

My initial question was about oddly-formatted date/times.  The suggested
solution of casting directly to timestamp with ::timestamp is not as
flexible as the to_timestamp function that I was trying to avoid.  For
example, this fails because of the day-before-month format:

SELECT ('18.09.2015 18:01:40')::timestamp
--ERROR: date/time field value out of range

whereas this works, but results in a timestamp *with* time zone that makes
assumptions about daylight savings times:
SELECT to_timestamp('18.09.2015 18:01:40','DD.MM.YYYY HH24:MI:SS')

I ended up with this simple solution, which does what I wanted to and avoids
time zones:
SELECT (to_date('18.09.2015','DD.MM.YYYY') ||' '||'18:01:40')::timestamp

Adrian: thanks for your observation about wildlife-human interactions --
that is a useful reminder since I'll be looking at traffic patterns.



--
View this message in context: http://postgresql.nabble.com/to-timestamp-alternatives-tp5879723p5879753.html
Sent from the PostgreSQL - general mailing list archive at Nabble.com.


Re: to_timestamp alternatives

From
Jim Nasby
Date:
On 12/31/15 5:05 PM, gkhan wrote:
> For
> example, this fails because of the day-before-month format:

Right, which is why Tom had in his example:

regression=# set datestyle = dmy;

BTW, my recommendation would be to store in a timestamptz field *with
the correct timezone*, and then convert on output as necessary. This is
easy to do by either

SET timezone

or

SELECT timestamptz_field AT TIME ZONE '...';

If you want the time without DST, you can just use a timezone like '+8'
or '-8'.

Since you're dealing with GPS data and presumably have lat/long, it
shouldn't be hard to do this dynamically either, either by just blindly
dividing longitude by 15 or using actual timezone shape polygons and @>
or <@.
--
Jim Nasby, Data Architect, Blue Treble Consulting, Austin TX
Experts in Analytics, Data Architecture and PostgreSQL
Data in Trouble? Get it in Treble! http://BlueTreble.com


Re: to_timestamp alternatives

From
Adrian Klaver
Date:
On 12/31/2015 03:05 PM, gkhan wrote:
> Follow-up:
>
> My initial question was about oddly-formatted date/times.  The suggested
> solution of casting directly to timestamp with ::timestamp is not as
> flexible as the to_timestamp function that I was trying to avoid.  For
> example, this fails because of the day-before-month format:
>
> SELECT ('18.09.2015 18:01:40')::timestamp
> --ERROR: date/time field value out of range

It it where me I would deal with this in the original data, either
pre-import or as part of the import process. Presumably for a given data
set the date/time format is the same and therefore more easily
converted. The goal would be to then have a 'standard' date/time output
format landing in the database. Seems easier then going back after the
fact and building a process for all eventualities.

>
> whereas this works, but results in a timestamp *with* time zone that makes
> assumptions about daylight savings times:
> SELECT to_timestamp('18.09.2015 18:01:40','DD.MM.YYYY HH24:MI:SS')
>
> I ended up with this simple solution, which does what I wanted to and avoids
> time zones:
> SELECT (to_date('18.09.2015','DD.MM.YYYY') ||' '||'18:01:40')::timestamp
>
> Adrian: thanks for your observation about wildlife-human interactions --
> that is a useful reminder since I'll be looking at traffic patterns.

Just part of a bigger observation that it is often assumed humans are
not animals.



--
Adrian Klaver
adrian.klaver@aklaver.com


Re: to_timestamp alternatives

From
Thomas Kellerer
Date:
gkhan schrieb am 31.12.2015 um 22:34:
> Thanks very much for both of your replies.  I had tried something similar and
> gotten an error, so I am probably making a stupid mistake.  If I try this,
> it works:
>
>     SELECT ('09.03.2014'||' '||lpad('3:00:00',8,'0'),'DD.MM.YYYY
> HH24:MI:SS')::timestamp
>
> but if I use column names instead of the text, like this, it fails:
>     SELECT (gmt_date||' '||lpad(gmt_time),'DD.MM.YYYY HH24:MI:SS')::timestamp
> ...
>
> Both the gmt_date and gmt_time columns are "text" data type and formatted
> exactly as in the original example, but I get this error:
>     ERROR:  cannot cast type record to timestamp without time zone

This expression:

     (gmt_date||' '||lpad(gmt_time),'DD.MM.YYYY HH24:MI:SS')


creates an anonymous object type with two columns, that's why you get the "cannot cast type record" error.

You only need to provide the text value to be casted (no format mask).
Use only a single expression without those unnecessary parentheses:

     SELECT gmt_date||' '||lpad(gmt_time,8,'0')::timestamp

You don't need the lpad() either:

     SELECT gmt_date||' '||gmt_time::timestamp








Re: to_timestamp alternatives

From
Alban Hertroys
Date:
> On 01 Jan 2016, at 0:46, Jim Nasby <Jim.Nasby@BlueTreble.com> wrote:
>
> BTW, my recommendation would be to store in a timestamptz field *with the correct timezone*, and then convert on
outputas necessary. This is easy to do by either 
>
> SET timezone
>
> or
>
> SELECT timestamptz_field AT TIME ZONE '…';

This. When converting the original timestamps to UTC, you lose data. In my experience, you will end up needing that
datasooner or later. 
Remember, the database stores timestamps in UTC internally anyway, you don't need to the conversion yourself.

> Since you're dealing with GPS data and presumably have lat/long, it shouldn't be hard to do this dynamically either,
eitherby just blindly dividing longitude by 15 or using actual timezone shape polygons and @> or <@. 

That would be a bad idea for global data, since not all time zones are full hours apart, or have the same (if any) DST
change-overdates. For example, India is currently at UTC+05:30, probably because they wrapped the entire country in the
sameTZ after their independence. 

Alban Hertroys
--
If you can't see the forest for the trees,
cut the trees and you'll find there is no forest.



Re: to_timestamp alternatives

From
Jim Nasby
Date:
On 1/1/16 7:15 AM, Alban Hertroys wrote:
>> Since you're dealing with GPS data and presumably have lat/long, it shouldn't be hard to do this dynamically either,
eitherby just blindly dividing longitude by 15 or using actual timezone shape polygons and @> or <@. 
> That would be a bad idea for global data, since not all time zones are full hours apart, or have the same (if any)
DSTchange-over dates. For example, India is currently at UTC+05:30, probably because they wrapped the entire country in
thesame TZ after their independence. 

The original desire was to ignore DST, hence my suggestion. Normally
you'd want to figure out the correct TZ for the lat/long.
--
Jim Nasby, Data Architect, Blue Treble Consulting, Austin TX
Experts in Analytics, Data Architecture and PostgreSQL
Data in Trouble? Get it in Treble! http://BlueTreble.com


Re: to_timestamp alternatives

From
Jim Nasby
Date:
On 1/1/16 4:11 AM, Thomas Kellerer wrote:
> You only need to provide the text value to be casted (no format mask).
> Use only a single expression without those unnecessary parentheses:
>
>      SELECT gmt_date||' '||lpad(gmt_time,8,'0')::timestamp
>
> You don't need the lpad() either:
>
>      SELECT gmt_date||' '||gmt_time::timestamp

I suspect you need to wrap that in (). Even if the parser does the right
thing there, it'd certainly make the intent a lot clearer.

SELECT (gmt_date || ' ' || gmt_time)::timestamp
--
Jim Nasby, Data Architect, Blue Treble Consulting, Austin TX
Experts in Analytics, Data Architecture and PostgreSQL
Data in Trouble? Get it in Treble! http://BlueTreble.com