holidays / holidays

A collection of Ruby methods to deal with statutory and other holidays. You deserve a holiday!

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Doesn't work for Australia.

nello opened this issue · comments

In Australia we have 2 Jan 2017 as a public holiday based on the holiday being the Monday after a Sunday public holiday. Unfortunately, Holidays.on(Date.civil(2017,1,2), :au) does not return that day as a holiday.

Looking a bit deeper, Holidays.between(Date.today, Date.today + 6.years, :au) returns hardly any Australian holidays, despite what is defined. Holidays.between(Date.today, Date.today + 6.years, :au_vic) is quite a bit better but still misses many holidays.

All these holidays seem to be present and catered-for in the rules, but most of them are not actually reflected in the code when executed.

Ooof, thank you for reporting this. Let me test right now and see what is going on.

I'm gonna do this in phases, if that's @alright. 😄

For your first point (Holidays.on(Date.civil(2017,1,2), :au) does not return that day as a holiday): this is actually expected. This gem has the concept of 'observed' (more details here). Technically, the actual holiday is on 2017, 1, 1. For example:

irb(main):007:0> Holidays.on(Date.civil(2017, 1, 1), :au)
=> [{:date=>#<Date: 2017-01-01 ((2457755j,0s,0n),+0s,2299161j)>, :name=>"New Year's Day", :regions=>[:au]}]

To get it on the 'observed' date you must call it like this:

irb(main):006:0> Holidays.on(Date.civil(2017, 1, 2), :au, :observed)
=> [{:date=>#<Date: 2017-01-02 ((2457756j,0s,0n),+0s,2299161j)>, :name=>"New Year's Day", :regions=>[:au]}]

I understand that this can be a bit confusing but I'm not sure how to show 'observed' vs 'actual' in another way. I am open to suggestions!

EDIT: Sorry, I thoroughly messed up my snippets before. Fixed!

For your second point, this a bit harder. I'm not quite sure how to respond without a specific example of a command and a specific holiday that you expect but isn't present. Could you provide an example of what you expect to see but isn't present when running the .between?

One issue I see is that if you run with :au only you will NOT get :au_vic, for example, You have to specify :au_vic to get that. If you call :au_vic you will get :au holidays but not the other way. Does this match with your way of thinking? Perhaps it's not clear in any of our documentation. I would be happy to update it based on your feedback.

Hi,

Firstly, thanks for taking the time to respond; your kindness is much appreciated.

The :observed flag definitely isn't what I expected. When requesting a list of holidays, I was really expecting to be returned a list of the non-work days, which (I guess) includes all observed holidays. My use case was an attempt to find the next working day, i.e. avoid bank holidays, so this distinction is a bit surprising.

My second question was a bit misguided, but I think still discloses a real issue. Let me explain. The biggest difference between the :au and :au_vic lists turned out to be extra days in Easter and a couple of local Victorian holidays. I presume there's some difference between Australia and Victoria in particular as to which days in Easter are holidays and which are just a normal weekend. So, I was probably mistaken, sorry. I should have done more investigation before I started typing.

However, part of what confused me was that there are also a number of holidays defined for Victoria in the .yaml that just don't turn up in the list returned by:

Holidays.between(Date.today, Date.today + 1.years, :au_vic), or
Holidays.between(Date.today, Date.today + 1.years, :au_vic, :observed)

Looking at the second of these, the dates returned are:

[Mon, 26 Dec 2016, Tue, 27 Dec 2016, Tue, 27 Dec 2016, Mon, 02 Jan 2017, Thu, 26 Jan 2017, Mon, 13 Mar 2017, Fri, 14 Apr 2017, Sat, 15 Apr 2017, Sun, 16 Apr 2017, Mon, 17 Apr 2017, Tue, 25 Apr 2017, Tue, 25 Apr 2017, Mon, 12 Jun 2017]

Note no holidays in the second half of the year!

Checking the .yaml, I can see:

  10:
  - name: Friday before the AFL Grand Final
    regions: [au_vic]
    function: afl_grand_final(year)
...
  11:
  - name: Melbourne Cup Day
    regions: [au_vic_melbourne]
    week: 1
    wday: 2
...
methods:
  afl_grand_final:
    arguments: year
    source: |
      case year
      when 2015
        Date.civil(2015, 10, 2)
      when 2016
        Date.civil(2017, 9, 30)
      end

Taking these in reverse order, there looks like there's an error in the afl_grand_final method. The years in the method don't match. It's a pretty obvious glitch. (The one for 2017 is actually 29 Sep, that's the 2016 date.) The other missing holiday is more interesting. It looks like my query to find bank holidays in Victoria has to actually query :au_vic_melbourne, :observed holidays?

I'm really not sure I could guess this without spending some real time with this library. Is there an easier way to formulate my query? I would guess that bank holidays are likely to be a pretty common use case.

Thanks again for your help and providing a very useful library.

A little more thought on this leads me to wonder, I think the way I'm expecting the library to work (somewhere in the back of my mind) is for :au holidays return all Australian holidays, i.e. the superset that contains all the holidays for all states. Any regionality could then be provided on a per-holiday basis.

I realise (now) that it's not the way this library works, but it is what I would expect to see if I visited a page for Australian holidays, for example. I'd see a list that showed all shared holidays and specific state-based differences.

Anyway, thanks again.

Hi @nello, we have this in a Rails initializer, because we have a similar use case to you in just caring if the day is a workday or not, rather than which specific holiday is the cause.

require 'holidays/core_extensions/date'
class Date
  include Holidays::CoreExtensions::Date

  def holidays_with_observed_check(*options)
    begin
      holidays = holidays_without_observed_check(options)
      holidays = holidays_without_observed_check(options + [:observed]) if holidays.empty? && !options.include?(:observed)
      holidays
    rescue Holidays::UnknownRegionError
      # we raise here based on some other conditions you probably won't care about
    end
  end

  alias_method_chain :holidays, :observed_check
end

This lets you safely call Date.today.holiday?(:au_vic) and get a true if it's an actual, or observed, holiday.


In response to #252 (comment) that sounds right. :au only contains holidays that are observed exactly the same way nation wide. Turns out that's a short list.


And in response to #252 (comment) you're right about the bug in the AFL Grand Final. That's my mistake - will fix. @ptrimble this one is actually pretty interesting because it seems to ignore the year part of the custom method and still work correctly - Date.civil(2016,9, 30).holidays(:au_vic) returns the correct holiday. Is that a bug in the library? (Putting aside the fact that my definition is wrong.)


More generally, you said that "My use case was an attempt to find the next working day, i.e. avoid bank holidays." - it would be great to get some more info on your use case if you can share it?

For context my use case is that we need to know when public holidays are so we can calculate how much people should get paid -- we use this gem to provide state-based holidays and then let users enter additional show holiday dates where appropriate.

If you're trying to cover every show day/bank holiday in every city in Australia then this gem is going to need a lot more content to be useful for you, and probably some rearchitecting too.