1

i want to know if particular date (in this example today's date) is holiday somwhere in the world. if yes - i would like to create list with tupple, in each tuple - (holiday name, where in the world). if it is not holiday anywhere - empty list. i tried to import holidays but i need to run on each country like in this example: anyone has something more efficient?

from datetime import date
import holidays

listOfHolidays = []
for ptr in holidays.ISR(years=date.today().year).items():
    if date.today() == ptr[0]:
        listOfHolidays.append(tuple((ptr[1], "ISRAEL")))

for ptr in holidays.US(years=date.today().year).items():
    if date.today() == ptr[0]:
        listOfHolidays.append((tuple(ptr[1], "US")))

for ptr in holidays.UK(years=date.today().year).items():
    if date.today() == ptr[0]:
        listOfHolidays.append((tuple(ptr[1], "UK")))

for ptr in holidays.CHN(years=date.today().year).items():
    if date.today() == ptr[0]:
        listOfHolidays.append((tuple(ptr[1], "CHN")))

print(listOfHolidays)

1 Answers1

1

Due to how the holidays package is structured, you will indeed have to iterate through all the supported regions. The function holidays.list_supported_countries can help you with this. The good news is that you can do direct lookup in a country object without having to manually search through the dict items:

list_of_holidays = []
target = date.today()
for country in holidays.list_supported_countries():
    country = getattr(holidays, country)()
    holiday = country.get(target)
    if holiday is not None:
        list_of_holidays.append((holiday, country.country))

You can write this as a comprehension in versions of python that support the walrus operator:

list_of_holidays = [(c[target], c.country) for country in holidays.list_supported_countries() 
                                           if target in (c := getattr(holidays, country)())]

The problem is that there will be a lot of duplicates here, because almost every supported country has multiple ways to reference it. This is also an issue because you do not want to generate the same list over and over on the fly.

On inspecting the library, I find that the most reliable way to check if the country class is "real" is to check if it introduces any new holidays via the _populate method. You can add the condition for that with:

list_of_holidays = []
target = date.today()
for country in holidays.list_supported_countries():
    country_obj = getattr(holidays, country)
    country = country_obj()
    holiday = country.get(target)
    if holiday is not None and '_populate' in country_obj.__dict__:
        list_of_holidays.append((holiday, country.country))

Or as a comprehension:

list_of_holidays = [(c[target], c.country) for country in holidays.list_supported_countries()
                        if target in (c := getattr(holidays, country)()) and '_populate' in c.__class__.__dict__]

Finally, the country code is always given as the name when you use the country attribute. If you want something more legible, I find it's better to use the name of the class that implements the _populate method. For the second loop, replace (holiday, country.country) with

(holiday, country_obj.__name__)

For the second list comprehension, replace (c[target], c.country) with

(c[target], c.__class__.__name__)
Mad Physicist
  • 107,652
  • 25
  • 181
  • 264