Suggerirei di generare tutti gli intervalli di orari lavorativi tra created_at
e current_timestamp e sommandoli. Sebbene sia tutt'altro che una soluzione ottimale in termini di prestazioni, penso che sia l'approccio più diretto. Uso timestamp con intervallo di fuso orario tstzrange
con intersezione operatore (*)
.
SELECT id,
created_at,
SUM(upper(business_range) - lower(business_range) ) business_hours
FROM (
SELECT id,
created_at,
tstzrange(date_in_range + '09:00'::time with time zone, date_in_range + '17:00'::time with time zone)
* tstzrange(created_at, current_timestamp) as business_range
FROM (
SELECT id,
created_at,
created_at::date + generate_series(0, current_timestamp::date - created_at::date) as date_in_range
FROM times
) dates_in_range
WHERE date_part('dow', date_in_range) in (1,2,3,4,5)
) business_hour_ranges
GROUP BY
id,
created_at
Vedi la configurazione e l'esempio in db<>fiddle
Avvertimento
Sebbene l'implementazione sia stata realizzata per essere facile da capire e da eseguire il debug, può comportare prestazioni scadenti, specialmente se utilizzata con date create_at molto vecchie. Se questo può essere il caso nel tuo sistema, potresti fare meglio a scrivere una funzione che controlla il giorno della settimana della data_at e quella_corrente, trova il numero di giorni tra di loro e determina l'orario di lavoro.