Convert military time integer to standard time Postgres without time zone - postgresql

I have a time column in postgres called military_time that is an integer and in some cases needs to be padded Ex: 1400, 1300, 25, 0900. I need to convert to 2:00 pm,1:00 pm,12:25 am,9:00 am. I have read I need cast integer to time and then use the Postgres function to_char into the format I need but I am a little lost. I have found a bunch of syntax for other languages but nothing in Postgres sql.

This is going to be more complicated then that. You will need a way to distinguish between hour only 1400/hour and minutes 1425 and minutes only 25. The hour/hrs&minutes is simple enough:
select to_char(1400::text::time, 'HH:MI:SS AM'); 02:00:00 PM,
select to_char(1425::text::time, 'HH:MI:SS AM'); 02:25:00 PM.
Minutes only could be done as:
select to_char(('00:'|| 25::text)::time, 'HH:MI:SS AM'); 12:25:00 AM
To pull it together:
create table mil_time (time_fld integer);
insert into mil_time values (1400), (1425), (25), (700);
SELECT
time_fld,
CASE
WHEN time_fld >= 1000 THEN
to_char(time_fld::text::time, 'HH:MI:SS AM')
WHEN time_fld >= 100 THEN
to_char(('0'|| time_fld::text)::time, 'HH:MI:SS AM')
WHEN time_fld <= 60 THEN
to_char(('00:'|| time_fld::text)::time, 'HH:MI:SS AM')
ELSE
'00:00:00'
END
FROM
mil_time;
time_fld | case
----------+-------------
1400 | 02:00:00 PM
1425 | 02:25:00 PM
25 | 12:25:00 AM
700 | 07:00:00 AM
UPDATE
Explanation of time_fld::text::time. It is Postgres shorthand for cast to text then to time, so:
select pg_typeof(1400::text); text
select pg_typeof(1400::text::time); time without time zone

Related

Postgresql extracting 'epoch' from timestamp cuts off last date in date range

My table has the column event_ts with column type numeric.
Here is my query:
select
min(to_timestamp(event_ts)), max(to_timestamp(event_ts))
from
table1
where
event_ts >= extract('epoch' from '2021-07-01'::timestamp) and
event_ts <= extract('epoch' from '2021-07-31'::timestamp)
However, the results are
min: 2021-06-30 20:00:00.000 -0400
max: 2021-07-30 20:00:00.000 -0400
I would think the where clause would include data from 2021-07-01 to 2021-07-31.
There is data for July 31st, 2021.
Why does this query start at 2021-06-30 and end 2021-07-30?
show timezone;
TimeZone
------------
US/Pacific
select extract('epoch' from '2021-07-01'::timestamp);
extract
-------------------
1625097600.000000
select to_timestamp(1625097600);;
to_timestamp
-------------------------
06/30/2021 17:00:00 PDT
select extract('epoch' from '2021-07-01'::timestamptz);
extract
-------------------
1625122800.000000
(1 row)
test(5432)=# select to_timestamp(1625122800);
to_timestamp
-------------------------
07/01/2021 00:00:00 PDT
So by using timestamp you are creating a local time offset by the timezone offset. Using timestamptz will return a timestamp at 0:00:00.
This is because from here:
https://www.postgresql.org/docs/current/functions-datetime.html#FUNCTIONS-DATETIME-EXTRACT
epoch
For timestamp with time zone values, the number of seconds since 1970-01-01 00:00:00 UTC (negative for timestamps before that); for date and timestamp values, the nominal number of seconds since 1970-01-01 00:00:00, without regard to timezone or daylight-savings rules; for interval values, the total number of seconds in the interval
Epoch is based on UTC timezone.
Not sure why you are using epoch anyway?
Why not?:
...
where
event_ts between '2021-07-01'::timestamptz and '2021-07-31'::timestamptz

Week Day Starting from a Certain Day (01 Jan 2021) in Postgres

I am trying to get week numbers in a Year starting from a certain day
I've checked the stack but quite confused.
SELECT EXTRACT(WEEK FROM TIMESTAMP '2021-01-01'),
extract('year' from TIMESTAMP '2021-01-01')
The output is 53|2021
I want it to be 01|2021
I understand the principle of the isoweek but I want the year to start in 01-01-2021
The aim is to use intervals from this day to determine week numbers
Week N0| End Date
1 | 01-01-2021
2 | 01-08-2021
5 | 01-29-2021
...
This is really strange way to determine the week number, but in the end it's a simple math operation: the number of days since January first divided by 7.
You can create a function for this:
create function custom_week(p_input date)
returns int
as
$$
select (p_input - date_trunc('year', p_input)::date) / 7 + 1;
$$
language sql
immutable;
So this:
select date, custom_week(date)
from (
values
(date '2021-01-01'),
(date '2021-01-08'),
(date '2021-01-29')
) as v(date)
yields
date | custom_week
-----------+------------
2021-01-01 | 1
2021-01-08 | 2
2021-01-29 | 5

PostgreSQL grouping by day with timezone and DST

I'm working on an existing database which date+time values are stored in BIGINT column (milliseconds since EPOCH). For each entry I can get the corresponding time zone from another table. But to make things easier to understand I will explain my problem by simulating the problem.
The timestamp 1609534800000 is equal to 2021-01-01 21:00:00 at GMT (00-00)
Now if I run the following queries (with DBeaver)
set time zone 'America/Asuncion'; -- (UTC-3)
select
to_timestamp(1609534800000 / 1000) as "1"
, to_timestamp(1609534800000 / 1000) at TIME zone 'America/New_York' as "2"
, date_trunc('day', (to_timestamp(1609534800000 / 1000) at TIME zone 'America/New_York')) as "3"
, to_timestamp(1609534800000 / 1000) at TIME zone 'Pacific/Wake' as "4"
, date_trunc('day', (to_timestamp(1609534800000 / 1000) at TIME zone 'Pacific/Wake')) as "5"
, date_part('epoch', (to_timestamp(1609534800000 / 1000) at TIME zone 'Pacific/Wake')) as "6"
, date_part('epoch', (date_trunc('day', (to_timestamp(1609534800000 / 1000) at TIME zone 'Pacific/Wake') ) ) ) as "7"
I'm getting
1 |2 |3 |4 |5 |6 |7 |
-------------------|-------------------|-------------------|-------------------|-------------------|----------|----------|
2021-01-01 16:00:00|2021-01-01 16:00:00|2021-01-01 00:00:00|2021-01-02 09:00:00|2021-01-02 00:00:00|1609578000|1609545600|
I don't understand the result at all. According to the documentation, the function to_timestamp is supposed to return a timestamp with time zone ? In this case the time zone applied should be the one in my session America/Asuncion (UTC-3). If at GMT the time is 2021-01-01 21:00:00, I should get 2021-01-01 18:00:00. Because (21:00 - 3h = 18:00). Why 16h ?
From my understanding this result is OK as 2021-01-01 21:00:00 at UTC -5h for timezone America/New_York is equal to 2021-01-01 16:00.
Here I'm asking the same thing as the #2 but I want to discard the time of the day. So 2021-01-01 16:00:00 is 2021-01-01 00:00:00. The result is OK.
This result is still OK as 2021-01-01 21:00:00 at UTC + 12h for timezone Pacific/Wake is equal to 2021-01-02 09:00:00.
5 I'm asking the same thing as the #4 but I want to discard the time of the day. So 2021-01-02 09:00:00 is 2021-01-02 00:00:00. The result is OK.
I want to extract the unix EPOCH time in seconds of this timestamp. If I well understand, the timestamp pass to the date_part function is now a timestamp without time zone. Now if I use an online converter to convert the resulting value 1609578000 to GMT time then I'm getting 2021-01-02 9:00:00. Which is OK for me.
This is the same operation as the #6 but I want the unix epoch from the beginning of the day of that local time. The resulting value 1609545600 correspond to the GMT time 2021-01-02 00:00:00. Which is NOT correct as I should get 2021-01-02 12:00:00 as 'Pacific/Wake' is 12h past GMT.
(UPDATED)
Also why Montreal locale time is not correct here ? I'm supposed to have 2021-01-01 00:00:00-05
select ((to_timestamp(1609477200000 /1000) at time zone 'America/Asuncion') at time zone 'America/Asuncion') as asuncion
, ((to_timestamp(1609477200000 /1000) at time zone 'America/Montreal') at time zone 'America/Montreal') as montreal
asuncion | montreal
------------------------+------------------------
2021-01-01 02:00:00-03 | 2021-01-01 02:00:00-03
How could I get
asuncion | montreal
------------------------+------------------------
2021-01-01 02:00:00-03 | 2021-01-01 00:00:00-05
Is there a way to see the time WITHOUT the configured session timeszone ?
PS : My Windows OS timezone is set at America/New_York and I'm using PostgreSQL 10.
Best regards,
I can't replicate 1). I get:
set time zone 'America/Asuncion';
select to_timestamp(1609534800000 / 1000);
to_timestamp
------------------------
2021-01-01 18:00:00-03
As to 6), you did not account for the SET timezone:
select date_part('epoch', date_trunc('day', to_timestamp(1609534800000 / 1000) at TIME zone 'Pacific/Wake') at TIME zone 'Pacific/Wake');
date_part
------------
1609502400
select to_timestamp(1609502400);
to_timestamp
------------------------
2021-01-01 09:00:00-03
--What happened
select to_timestamp(1609534800000 / 1000) at TIME zone 'Pacific/Wake';
timezone
---------------------
2021-01-02 09:00:00
(1 row)
--- Note you lopped off 9 hours and the returned timestamp has no time zone offset
--- so it is now local time 'America/Asuncion'
select date_trunc('day', to_timestamp(1609534800000 / 1000) at TIME zone 'Pacific/Wake');
date_trunc
---------------------
2021-01-02 00:00:00
--- This gives it back a timezone offset.
--- Running the date_part on this then gets you the proper value.
select date_trunc('day', to_timestamp(1609534800000 / 1000) at TIME zone 'Pacific/Wake') at TIME zone 'Pacific/Wake';
timezone
------------------------
2021-01-01 09:00:00-03

Postgres tsrange, filter by date and time

I have an events table that has a field called duration thats of type tsrange and that captures the beginning and end time of an event thats of type timestamp. What I want is to be able to filter all events across a certain date range and then filter those events by time. So for instance, a user should be able to filter for all events happening between (inclusive) 12-15-2019 to 12-17-2019 and that are playing at 9PM. To do this, the user submits a date range which filters all events in that date range:
WHERE lower(duration)::date <# '[start, finish]'::daterange
In the above start and finish are user submitted parameters.
Then I want to filter those events by events that are playing during a specific time e.g. 9PM, essentially only show events that have 9PM between their start and end time.
So if I have the following table:
id duration
--- ------------------------------------
A 2019-12-21 19:00...2019-12-22 01:00
B 2019-12-17 16:00...2019-12-17 18:00
C 2019-12-23 19:00...2019-12-23 21:00
D 2019-12-23 19:00...2019-12-24 01:00
E 2019-12-27 14:00...2019-12-27 16:00
And the user submits a date range of 2019-12-21 to 2019-12-27 then event B will be filtered out. Then the user submits a time of 9:00PM (21:00), in which case A, C, and D will be returned.
EDIT
I was able to get it to work using the following:
WHERE duration #> (lower(duration)::date || ' 21:00:00')::timestamp
Where the 21:00 above is the user data, but it seems a bit hackish
A tsrange contains a timestamp at 9 p.m. if and only if 9 p.m. on the starting day or 9 p.m. on the following day are part of the range.
You can use that to write your condition.
An example:
lower(r)::date + TIME '21:00' <# r OR
(lower(r)::date + 1) + TIME '21:00' <# r
is a test if r contains some timestamp at 9 p.m.
The user input from 2019-12-21 to 2019-12-27 at 21:00 means that he is interested in
select generate_series(timestamp '2019-12-21 21:00', '2019-12-27 21:00', '1 day') as t
t
---------------------
2019-12-21 21:00:00
2019-12-22 21:00:00
2019-12-23 21:00:00
2019-12-24 21:00:00
2019-12-25 21:00:00
2019-12-26 21:00:00
2019-12-27 21:00:00
(7 rows)
Hence you should check whether the duration column contains one of the timestamp:
select distinct e.*
from events e
cross join generate_series(timestamp '2019-12-21 21:00', '2019-12-27 21:00', '1 day') as t
where duration #> t
id | duration
----+-----------------------------------------------
A | ["2019-12-21 19:00:00","2019-12-22 01:10:00")
C | ["2019-12-23 19:00:00","2019-12-23 21:10:00")
D | ["2019-12-23 19:00:00","2019-12-24 01:10:00")
(3 rows)

How many seconds passed by grouped by hour between two dates

Let's suppose I have a start date 2016-06-19 09:30:00 and an end date 2016-06-19 10:20:00
I would like to get the time that elapsed every hour before starting the next hour or before getting to the final time in seconds grouped by hour and date, the result I'm trying to achieve (without having any success) would be something like this:
hour | date | time_elapsed_in_seconds
9 | 2016-06-19 | 1800 (there are 1800 seconds between 09:30:00 and 10:00:00)
10 | 2016-06-19 | 1200 (there are 1200 seconds between 10:00:00 and 10:20:00)
Try this :
with table1 as (
select '2016-06-19 09:30:00'::timestamp without time zone start_date,'2016-06-19 10:20:00'::timestamp without time zone end_date
)
select extract(hour from the_hour) "hour",the_hour::date "date",extract (epoch from (new_end-new_start)) "time_elapsed" from (
select the_hour,CASE WHEN date_trunc('hour',start_date)=the_hour then start_date else the_hour end new_start,
CASE WHEN date_trunc('hour',end_date)=the_hour then end_date else the_hour+'1 hour'::interval end new_end
from (
select generate_series(date_trunc('hour',start_date),end_date,'1 hour'::interval) the_hour,start_date,end_date from table1
) a
) b