New Zealand Public Holidays Script

Using the wiki example as a base (https://github.com/openhab/openhab/wiki/Samples-Rules#how-to-calculate-public-holidays), I’ve created one for NZ (well auckland because of Auckland anniversary, but thats easily changed to another region, the rest will be the same).

If you find any bugs, please let me know. It picked up Good Friday :slight_smile:

var int year = now.getYear

var int a = year % 19
var int b = year / 100
var int c = year % 100
var int d = b / 4;
var int e = b % 4;
var int f = (b + 8) / 25;
var int g = (b - f + 1) / 3;
var int h = (19 * a + b - d - g + 15) % 30;
var int i = c / 4;
var int k = c % 4;
var int L = (32 + 2 * e + 2 * i - h - k) % 7;
var int m = (a + 11 * h + 22 * L) / 451;

var int month = (h + L - 7 * m + 114) / 31;
var int day = ((h + L - 7 * m + 114) % 31) + 1;

var boolean holiday = false
var String holidayName = null
var org.joda.time.DateTime easterSunday = parse(year+"-"+month+"-"+day)
var dayOfWeek = now.getDayOfWeek


var org.joda.time.DateTime newYears = parse(year+"-01-01")
if(newYears.getDayOfWeek == 0 || newYears.getDayOfWeek == 6)
	newYears= parse(year+"-01-03")	
var org.joda.time.DateTime newYears2 = parse(year+"-01-02")
if(newYears2.getDayOfWeek == 0 || newYears2.getDayOfWeek == 6) // saturday, we add 2 days, sunday we also add 2 days, since 1st is pushed to monday
	newYears2= parse(year+"-01-04")

var org.joda.time.DateTime waitangiDay= parse(year+"-02-06")
if(waitangiDay.getDayOfWeek == 0)
	waitangiDay= parse(year+"-02-07")
else if(waitangiDay.getDayOfWeek == 6)
	waitangiDay= parse(year+"-02-09")
	
var org.joda.time.DateTime anzacDay= parse(year+"-04-25")
if(anzacDay.getDayOfWeek == 0)
	anzacDay= parse(year+"-04-27")
else if(anzacDay.getDayOfWeek == 6)
	anzacDay= parse(year+"-04-27")
	
// queens birthday, first monday in june
var org.joda.time.DateTime queensBirthday = parse(year+"-06-01")
if(queensBirthday.getDayOfWeek == 0)
	queensBirthday= parse(year+"-06-02")
else if(queensBirthday.getDayOfWeek != 1){
	queensBirthday= parse(year+"-06-0"+ (8 - queensBirthday.getDayOfWeek))	
}
// labour day, 4th Monday in October...
var org.joda.time.DateTime octoberOne = parse(year+"-10-01")
var org.joda.time.DateTime labourDay = octoberOne.withDayOfWeek(1)
if(labourDay.isBefore(octoberOne))
	labourDay = labourDay.plusWeeks(4)
else 
	labourDay = labourDay.plusWeeks(3)
	
var org.joda.time.DateTime xmasDay = parse(year+"-12-25")
if(xmasDay.getDayOfWeek == 0 || xmasDay.getDayOfWeek == 6)
	xmasDay= parse(year+"-12-27")	
var org.joda.time.DateTime boxingDay = parse(year+"-12-26")
if(boxingDay.getDayOfWeek == 0 || boxingDay.getDayOfWeek == 6) // saturday, we add 2 days, sunday we also add 2 days, since xmasd day is pushed to monday
	boxingDay= parse(year+"-12-28")
	
	
var int dayOfYear = now.getDayOfYear
if (dayOfYear==newYears.getDayOfYear) {
    holidayName = "new_years_day" // New Year
    holiday = true
}
else if (dayOfYear==newYears2.getDayOfYear) {
    holidayName = "day_after_new_years"
    holiday = true 
}
else if (dayOfYear==easterSunday.getDayOfYear-2) {
    holidayName = "good_friday"
    holiday = true
}
else if (dayOfYear==easterSunday.getDayOfYear) {
    holidayName = "easter_sunday"
    holiday = true
}
else if (dayOfYear==easterSunday.getDayOfYear+1) {
    holidayName = "easter_monday"
    holiday = true
}
else if (dayOfWeek == 1 && Math::abs(dayOfYear - parse(year+"-01-29").getDayOfYear) <= 4) {
    holidayName = "auckland_anniversay_day"
    holiday = true 
}
else if (dayOfYear==waitangiDay.getDayOfYear) {
    holidayName = "waitangi_day" 
    holiday = true
}
else if (dayOfYear==queensBirthday.getDayOfYear) {
    holidayName = "queens_birthday" 
    holiday = true
}
else if (dayOfYear==labourDay.getDayOfYear) {
    holidayName = "labour_day" 
    holiday = true
}
else if (dayOfYear==xmasDay.getDayOfYear) {
    holidayName = "christmas" // New Year
    holiday = true
}
else if (dayOfYear==boxingDay.getDayOfYear) {
    holidayName = "boxing_day"
    holiday = true 
}

if (holidayName!=null) {
    postUpdate(SpecialDay,holidayName)
}
if (holiday) {
    postUpdate(Holiday,ON)
}
else {
    postUpdate(Holiday,OFF)
}
1 Like

Ha - genius!

I do this slightly differently, I setup a Google account for my house and then subscribe to the NZ public holidays calendar in that account;

I also subscribe to a Canterbury Anniversary calendar to get Show Day down here.

Then I have a little python script running at just after midnight which queries the calendar and checks for any events on that day, if it finds one it updates openHAB to say it is a public holiday.

Even put it on github a while ago… https://github.com/sumnerboy12/oh_calendar

1 Like

yeah I was thinking of a calendar, but seeing as the days are easy to compute (well easter’s the tricky one, but the sample script took care of that), I figured its easier to use a script. I just never record any things like personal holidays in calendars, only appointments so the benefits of a calendar vs a script doesnt really apply to me. But I can easily see how it can to some.

I agree, your script looks a much cleaner solution. Might switch over at some point.

Finally got around to implementing your script-based method of getting NZ public holidays. I ended up porting it to python so thought I would share it here in case anyone else was interested. Thanks for sharing!

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from datetime import date
from datetime import timedelta

import requests

# openHAB details
host            = 'localhost'
port            = 8080
username        = 'user'
password        = 'password'
holidayitem     = 'VT_Holiday'
holidaynameitem = 'VT_Holiday_Name'

def update_item(item, value):
    if item is not None:
        url = 'http://%s:%s@%s:%d/rest/items/%s/state' % (username, password, host, port, item)
        headers = { 'Content-Type': 'text/plain' }
        requests.put(url, headers=headers, data=value)

def push_to_monday(date):
    if date.weekday() == 5:
        return date + timedelta(days=2)
    if date.weekday() == 6:
        return date + timedelta(days=1)
    return date

def push_consecutive_to_monday(date):
    date2 = date + timedelta(days=1)
    if date.weekday() == 5 or date.weekday() == 6:
        date += timedelta(days=2)
    if date2.weekday() == 5 or date2.weekday() == 6:
        date2 += timedelta(days=2)
    return date, date2

def calculate_easter_sunday(year):
    a = year % 19
    b = year / 100
    c = year % 100
    d = b / 4
    e = b % 4
    f = (b + 8) / 25
    g = (b - f + 1) / 3
    h = (19 * a + b - d - g + 15) % 30
    i = c / 4
    k = c % 4
    L = (32 + 2 * e + 2 * i - h - k) % 7
    m = (a + 11 * h + 22 * L) / 451

    month = (h + L - 7 * m + 114) / 31
    day = ((h + L - 7 * m + 114) % 31) + 1
    return date(year, month, day)

def calculate_holidays(year):
    holidays = {}

    # monday = 0, sunday = 6
    newyears1, newyears2 = push_consecutive_to_monday(date(year, 1, 1))
    holidays[newyears1] = 'New Years Day'
    holidays[newyears2] = 'Day After New Years Day'

    waitangiday = push_to_monday(date(year, 2, 6))	
    holidays[waitangiday] = 'Waitangi Day'

    eastersunday = calculate_easter_sunday(year)
    goodfriday = eastersunday - timedelta(days=2)
    eastermonday = eastersunday + timedelta(days=1)
    holidays[goodfriday] = 'Good Friday'
    holidays[eastermonday] = 'Easter Monday'

    anzacday = push_to_monday(date(year, 4, 25))
    holidays[anzacday] = 'Anzac Day'
	
    # queens birthday, first monday in june
    queensbirthday = date(year, 6, 1)
    if queensbirthday.weekday() == 6:
        queensbirthday += timedelta(days=1)
    elif queensbirthday.weekday() != 0:
        queensbirthday += timedelta(days=7 - queensbirthday.weekday())
    holidays[queensbirthday] = 'Queens Birthday'

    # labour day, 4th monday in october
    labourday = date(year, 10, 1)
    while labourday.weekday() != 0:
        labourday += timedelta(days=1)
    labourday += timedelta(weeks=3)
    holidays[labourday] = 'Labour Day'

    # show day, second friday after the first tuesday in november
    showday = date(year, 11, 1)
    while showday.weekday() != 1:
        showday += timedelta(days=1)
    showday += timedelta(days=10)
    holidays[showday] = 'Show Day'

    xmasday, boxingday = push_consecutive_to_monday(date(year, 12, 25))
    holidays[xmasday] = 'Xmas Day'
    holidays[boxingday] = 'Boxing Day'

    return holidays

def test(years):
    for year in years:
        print "Holidays for %s..." % (year)
        holidays = calculate_holidays(year)
        for holiday in sorted(holidays):
            print "%s is %s" % (holiday, holidays[holiday])

# testing
#test(range(2010, 2019))


# get the holidays for this year
today = date.today()
holidays = calculate_holidays(today.year)

# check if today is a holiday
if today in holidays:
    update_item(holidayitem, 'ON')
    update_item(holidaynameitem, holidays[today])
else:
    update_item(holidayitem, 'OFF')
    update_item(holidaynameitem, '')
1 Like

NOTE: this includes the anniversary holiday for Canterbury only!

Hi @ben_jones12,

If you are using Python, why not use the holidays package in Python?

I am about the setup one for where I live, Vancouver Canada, just wondering if you tried the holidays package?

Thanks,
Joe

Had never heard of the holidays package - will definitely take a look - thanks!

1 Like

I have just tried it. Highly recommend it. Shrink the code down to 5 or 6 lines.

https://pypi.python.org/pypi/holidays

Thanks,
Joe

1 Like

Care to share your script @goodfore?

from datetime import date
import holidays
if date.today() in holidays.CA(prov='BC'):
     #code to update holiday switch to true
else:
     #code to update holiday switch to false
3 Likes