Thread: Work hours?

Work hours?

From
stan
Date:
I am just starting to explore the power of PostgreSQL's time and date
functionality. I must say they seem very powerful.

I need to write a function that, given a month, and a year as input returns
the "work hours" in that month. In other words something like

8 * the count of all days in the range Monday to Friday) within that
calendar month.

Any thoughts as to the best way to approach this?


-- 
"They that would give up essential liberty for temporary safety deserve
neither liberty nor safety."
                        -- Benjamin Franklin



Re: Work hours?

From
Ron
Date:
On 8/27/19 5:27 PM, stan wrote:
> I am just starting to explore the power of PostgreSQL's time and date
> functionality. I must say they seem very powerful.
>
> I need to write a function that, given a month, and a year as input returns
> the "work hours" in that month. In other words something like
>
> 8 * the count of all days in the range Monday to Friday) within that
> calendar month.
>
> Any thoughts as to the best way to approach this?

I haven't tried this, but should point you in the right direction:

SELECT SUM(EXTRACT(DOW FROM the_date)) * 8.0 AS work_week_hours
FROM some_table
WHERE EXTRACT(DOW FROM the_date) BETWEEN 1 and 5
   AND the_date BETWEEN '2019-07-01' AND '2019-07-31 23:59:59';


It'll probably (nay, almost definitely) seq scan.

-- 
Angular momentum makes the world go 'round.



Re: Work hours?

From
Adrian Klaver
Date:
On 8/27/19 3:27 PM, stan wrote:
> I am just starting to explore the power of PostgreSQL's time and date
> functionality. I must say they seem very powerful.
> 
> I need to write a function that, given a month, and a year as input returns
> the "work hours" in that month. In other words something like
> 
> 8 * the count of all days in the range Monday to Friday) within that
> calendar month.
> 
> Any thoughts as to the best way to approach this?

Use generate_series:

https://www.postgresql.org/docs/11/functions-srf.html

to generate all the days in the month.

Loop over the days and use EXTRACT:

https://www.postgresql.org/docs/11/functions-datetime.html#FUNCTIONS-DATETIME-EXTRACT

to find the dates with a dow(The day of the week as Sunday (0) to 
Saturday (6)) or isodow(The day of the week as Monday (1) to Sunday (7))
that falls in Mon-Fri and add to counter.

> 
> 


-- 
Adrian Klaver
adrian.klaver@aklaver.com



Re: Work hours?

From
Paul A Jungwirth
Date:
On Tue, Aug 27, 2019 at 3:27 PM stan <stanb@panix.com> wrote:
> I need to write a function that, given a month, and a year as input returns
> the "work hours" in that month. In other words something like
>
> 8 * the count of all days in the range Monday to Friday) within that
> calendar month.

This gives you all the weekdays in August 2019:

select t::date
from generate_series('2019-08-01'::date, '2019-09-01'::date, interval
'1 day') s(t)
where extract(dow from t) not in (0, 6);

From there you could count & multiply by 8 (e.g. `select count(*) * 8`
instead). You'll probably want to remove holidays first though. If
those lived in another table you could do a NOT EXISTS to remove them
before you count.

Yours,
Paul



Re: Work hours?

From
Rob Sargent
Date:
On 8/27/19 4:59 PM, Adrian Klaver wrote:
> On 8/27/19 3:27 PM, stan wrote:
>> I am just starting to explore the power of PostgreSQL's time and date
>> functionality. I must say they seem very powerful.
>>
>> I need to write a function that, given a month, and a year as input 
>> returns
>> the "work hours" in that month. In other words something like
>>
>> 8 * the count of all days in the range Monday to Friday) within that
>> calendar month.
>>
>> Any thoughts as to the best way to approach this?
>
> Use generate_series:
>
> https://www.postgresql.org/docs/11/functions-srf.html
>
> to generate all the days in the month.
>
> Loop over the days and use EXTRACT:
>
> https://www.postgresql.org/docs/11/functions-datetime.html#FUNCTIONS-DATETIME-EXTRACT 
>
>
> to find the dates with a dow(The day of the week as Sunday (0) to 
> Saturday (6)) or isodow(The day of the week as Monday (1) to Sunday (7))
> that falls in Mon-Fri and add to counter.
>
>>
Don't you also need a feed from something like google US holidays 
(assuming OP is stateside)
>>
>
>



Re: Work hours?

From
raf
Date:
Rob Sargent wrote:

> On 8/27/19 4:59 PM, Adrian Klaver wrote:
> > On 8/27/19 3:27 PM, stan wrote:
> > > I am just starting to explore the power of PostgreSQL's time and date
> > > functionality. I must say they seem very powerful.
> > > 
> > > I need to write a function that, given a month, and a year as input
> > > returns
> > > the "work hours" in that month. In other words something like
> > > 
> > > 8 * the count of all days in the range Monday to Friday) within that
> > > calendar month.
> > > 
> > > Any thoughts as to the best way to approach this?
> > 
> > Use generate_series:
> > 
> > https://www.postgresql.org/docs/11/functions-srf.html
> > 
> > to generate all the days in the month.
> > 
> > Loop over the days and use EXTRACT:
> > 
> > https://www.postgresql.org/docs/11/functions-datetime.html#FUNCTIONS-DATETIME-EXTRACT
> > 
> > 
> > to find the dates with a dow(The day of the week as Sunday (0) to
> > Saturday (6)) or isodow(The day of the week as Monday (1) to Sunday (7))
> > that falls in Mon-Fri and add to counter.
> > 
> > > 
> Don't you also need a feed from something like google US holidays (assuming
> OP is stateside)

The definition of "work hours" differs depending on who
it's being calculated for. If your definition above is
sufficient for your needs then ignore the following but
some people work an 8 hour day, others a 7.6 hour day,
etc. It varies from one agreement to another. Some
people work 7 days a week with several weeks "on" and
several weeks "off". Some are full-time. Others are
part-time. Some might have regular hours. Others might
have an arbitrary roster that changes from week to
week. Some public holidays are state-wide. Others are
regional so you need to know where they work and the
definitions of the regions. Some public holidays aren't
even for the whole day. And no doubt every country is
different.

All of that is by far the biggest component of such a
calculation. The postgres-specific bit is easy and yes,
what Adrian suggests will be fine if you can use the
dates returned by generate_series to look up the
working conditions of the person involved. I've done it
in plpgsql with a loop over the dates rather using
generate_series in plain sql. Both are fine but plain
sql is probablby faster. Do whatever is most readable.

cheers,
raf




Re: Work hours?

From
Christopher Browne
Date:


On Tue, Aug 27, 2019, 6:27 PM stan <stanb@panix.com> wrote:
I am just starting to explore the power of PostgreSQL's time and date
functionality. I must say they seem very powerful.

I need to write a function that, given a month, and a year as input returns
the "work hours" in that month. In other words something like

8 * the count of all days in the range Monday to Friday) within that
calendar month.

Any thoughts as to the best way to approach this?

In data warehouse applications, they have the habit of creating tables that have various interesting attributes about dates.


I'd be inclined to solve this by defining various useful sets of dates; you might then attach relevant attributes to a dimension table like the d_date table in the article.

- a table with all weekdays (say, Monday to Friday)

- a table listing statutory holidays that likely need to be excluded

These are going to be small tables even if you put 10 years worth of dates in it.

Select entries from the first table, excluding holidays from the second, and you get the set of working days that can then be counted to get the desired result.

The "data warehouse" approach would probably be to put an extra is_stat_holiday onto the dimension table; then you could do...

Select count(*) * 8 from d_date where date_actual between '2019-09-01' and '2019-09-30' and day_of_week in (1,2,3,4,5) and not is_statutory_holiday;

If there are multiple jurisdictions with differing sets of holidays, that's going to complicate life.  Down that road, I'd define a bunch of tables for differing jurisdictions' holidays, clearly that diverges a bit from the data warehousing approach.


Re: Work hours?

From
Ron
Date:
On 8/27/19 10:22 PM, Christopher Browne wrote:


On Tue, Aug 27, 2019, 6:27 PM stan <stanb@panix.com> wrote:
I am just starting to explore the power of PostgreSQL's time and date
functionality. I must say they seem very powerful.

I need to write a function that, given a month, and a year as input returns
the "work hours" in that month. In other words something like

8 * the count of all days in the range Monday to Friday) within that
calendar month.

Any thoughts as to the best way to approach this?

In data warehouse applications, they have the habit of creating tables that have various interesting attributes about dates.


I'd be inclined to solve this by defining various useful sets of dates; you might then attach relevant attributes to a dimension table like the d_date table in the article.

- a table with all weekdays (say, Monday to Friday)

- a table listing statutory holidays that likely need to be excluded

These are going to be small tables even if you put 10 years worth of dates in it.

We did something similar to that, except all the columns were in one single table.  It wasn't a data warehouse, though: the RDBMS we used could be coerced into using a date index when large ranges were needed in detail tables by joining it to T_CALENDAR, and doing the range filter on T_CALENDAR.

--
Angular momentum makes the world go 'round.

Re: Work hours?

From
Steve Atkins
Date:

> On Aug 27, 2019, at 11:27 PM, stan <stanb@panix.com> wrote:
>
> I am just starting to explore the power of PostgreSQL's time and date
> functionality. I must say they seem very powerful.
>
> I need to write a function that, given a month, and a year as input returns
> the "work hours" in that month. In other words something like
>
> 8 * the count of all days in the range Monday to Friday) within that
> calendar month.
>
> Any thoughts as to the best way to approach this?

You might find this useful: https://gist.github.com/wttw/b6f5d0d67c31d499c05f22a4f2b6f628

It's not the most efficient approach, but it's relatively simple to customize.

Cheers,
  Steve




Re: Work hours?

From
Luca Ferrari
Date:
On Wed, Aug 28, 2019 at 12:27 AM stan <stanb@panix.com> wrote:
> Any thoughts as to the best way to approach this?

I've written a couple of functions to compute working hours depending
on a possible per-day hour template.
Another possible implementation besides the other proposed solutions.
<https://github.com/fluca1978/fluca1978-pg-utils/blob/master/examples/functions/working_hours.sql>

Luca



Re: Work hours?

From
Uwe Seher
Date:
select sum(case when extract(dow from t.d) in (1,2,3,4,5) then 1 else 0 end) * 8 as hours
  from generate_series(current_date::date, (current_date + '10 days'::interval), '1 day'::interval) as t(d)

This calculates the working days/hours between 2 dates. You can make your firt/lastr day of the month/year to a date and feed it into the series.

Bye Uwe



Am Mi., 28. Aug. 2019 um 00:27 Uhr schrieb stan <stanb@panix.com>:
I am just starting to explore the power of PostgreSQL's time and date
functionality. I must say they seem very powerful.

I need to write a function that, given a month, and a year as input returns
the "work hours" in that month. In other words something like

8 * the count of all days in the range Monday to Friday) within that
calendar month.

Any thoughts as to the best way to approach this?


--
"They that would give up essential liberty for temporary safety deserve
neither liberty nor safety."
                                                -- Benjamin Franklin


Re: Work hours?

From
Christopher Browne
Date:
On Wed, 28 Aug 2019 at 01:02, Ron <ronljohnsonjr@gmail.com> wrote:
We did something similar to that, except all the columns were in one single table.  It wasn't a data warehouse, though: the RDBMS we used could be coerced into using a date index when large ranges were needed in detail tables by joining it to T_CALENDAR, and doing the range filter on T_CALENDAR.

Ah, interesting!  I like it, mostly...

The one bad thing would be that this sorta mis-matches timestamp with timezone which is a more or less continuous data type (rather than discrete, like date).  I could see an argument, in that environment, to put a DATE type onto detail tables if they are inevitably being joined to T_CALENDAR.

I recall we had a case where some reports were ridiculously inefficient because a query involved effectively a "where date_part(something, column)" clause that made that into a Seq Scan.

Alternatively (and I'm thinking out loud here), I wonder if putting a range type with a pair of timestamps would help with matching, as the range type would put the full range of each day into the table; you could have full date/time stamps match the calendar table via the range type...

select [stuff] from tz_table t, t_calendar tc
where
   [various stuff]
    and
      tc.t_workday and
      tc.t_date between '2017-01-01' and '2017-02-01'
   and (to get the range bit)
     t.original_tstz <@ tc.t_range;
--
When confronted by a difficult problem, solve it by reducing it to the
question, "How would the Lone Ranger handle this?"

Re: Work hours?

From
Gavin Flower
Date:
On 28/08/2019 15:22, Christopher Browne wrote:
>
>
> On Tue, Aug 27, 2019, 6:27 PM stan <stanb@panix.com 
> <mailto:stanb@panix.com>> wrote:
>
>     I am just starting to explore the power of PostgreSQL's time and date
>     functionality. I must say they seem very powerful.
>
>     I need to write a function that, given a month, and a year as
>     input returns
>     the "work hours" in that month. In other words something like
>
>     8 * the count of all days in the range Monday to Friday) within that
>     calendar month.
>
>     Any thoughts as to the best way to approach this?
>
>
> In data warehouse applications, they have the habit of creating tables 
> that have various interesting attributes about dates.
>
> https://medium.com/@duffn/creating-a-date-dimension-table-in-postgresql-af3f8e2941ac
>
> I'd be inclined to solve this by defining various useful sets of 
> dates; you might then attach relevant attributes to a dimension table 
> like the d_date table in the article.
>
> - a table with all weekdays (say, Monday to Friday)
>
> - a table listing statutory holidays that likely need to be excluded
>
> These are going to be small tables even if you put 10 years worth of 
> dates in it.
>
[...]

You don't need a whole table for weekdays.  You can easily calculate the 
number of weekdays simply from knowing the first day of the month and 
how many days in a month.


Cheers,
Gavin




Re: Work hours?

From
Steven Lembark
Date:
> > Any thoughts as to the best way to approach this?  
> 
> Use generate_series:
> 
> https://www.postgresql.org/docs/11/functions-srf.html
> 
> to generate all the days in the month.
> 
> Loop over the days and use EXTRACT:
> 
> https://www.postgresql.org/docs/11/functions-datetime.html#FUNCTIONS-DATETIME-EXTRACT
> 
> to find the dates with a dow(The day of the week as Sunday (0) to 
> Saturday (6)) or isodow(The day of the week as Monday (1) to Sunday
> (7)) that falls in Mon-Fri and add to counter.

Assumes all weekdays are work days and that all weeks are uniform
for work. Any holiday, emergency, outage leaves you with a skewed
result.

First workaround is table of non-work days: generate a temp table of 
the series not intersecting the non-workdays (materialized views
are nice for this). 

Then you get into issues of different people having different non-
work days, leaving your subtraction table keyed by person+date.

Frequently the non-work days are by employee class, which allows a
table of days off by employee grade + employees w/ grade => days
off by empoloyee.

Then individual employees will have their own time off due to paid
vacation, medical or family leave, and sick days. Depending on your
number of employees a non_working_days w/ date + employee works or 
you get into the pay grade + generic days and employee + pay grade
for the generic days off merged with a separate table of individual
days off. Subtract that from a temp table generated by the sequences
and you'll have a complete schedule.



-- 
Steven Lembark                                     3920 10th Ave South
Workhorse Computing                               Birmingham, AL 35222
lembark@wrkhors.com                                    +1 888 359 3508