From b477e59d2daa8e272f5ad679c94cd886b3ce4d4d Mon Sep 17 00:00:00 2001
From: vincent <vincent@groupe-sa.fr>
Date: Mon, 4 Jan 2016 14:08:38 +0100
Subject: [PATCH] Add Holiday management (France only).

---
 src/CMakeLists.txt    |  1 +
 src/holiday.cpp       | 56 +++++++++++++++++++++++++++++++++++
 src/holiday.hpp       | 13 ++++++++
 test/CMakeLists.txt   |  1 +
 test/holiday_test.cpp | 69 +++++++++++++++++++++++++++++++++++++++++++
 test/holiday_test.hpp | 15 ++++++++++
 6 files changed, 155 insertions(+)
 create mode 100644 src/holiday.cpp
 create mode 100644 src/holiday.hpp
 create mode 100644 test/holiday_test.cpp
 create mode 100644 test/holiday_test.hpp

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 5fac8ff..ddd657b 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -2,6 +2,7 @@ set(${PROJECT}_SRC_FILES
   qcron.cpp
   qcronfield.cpp
   qcronnode.cpp
+  holiday.cpp
   )
 
 set(${PROJECT}_HEADERS
diff --git a/src/holiday.cpp b/src/holiday.cpp
new file mode 100644
index 0000000..3276839
--- /dev/null
+++ b/src/holiday.cpp
@@ -0,0 +1,56 @@
+#include "holiday.hpp"
+
+#include <QList>
+
+/******************************************************************************/
+
+bool
+Holiday::
+isHoliday(const QDate & today)
+{
+    int y = today.year();
+    QList<QDate> holidays  = QList<QDate>()
+                             << QDate(y, 1, 1)   // Jour de l'an
+                             << QDate(y, 5, 1)   // Fete du travail
+                             << QDate(y, 5, 8)   // Fete de la victoire
+                             << QDate(y, 7, 14)  // Fete nationale
+                             << QDate(y, 8, 15)  // Assomption
+                             << QDate(y, 11, 1)  // Toussaint
+                             << QDate(y, 11, 11) // Armistice de 1918
+                             << QDate(y, 12, 25) // Noel
+                             ;
+    QDate easter = Holiday::easter(y);
+
+    return holidays.contains(today) ||
+           today == easter.addDays(1) ||  // Lundi de Paques
+           today == easter.addDays(39) || // Jeaudi de l'Ascension
+           today == easter.addDays(50)    // Lundi de Pentecote
+           ;
+}
+
+/******************************************************************************/
+
+QDate
+Holiday::
+easter(int y)
+{
+    /* Many thanks to http://www.henk-reints.nl/easter/index.htm */
+    int a = y % 19 + 1;
+    int b = y / 100 + 1;
+    int c = (3 * b) / 4 - 12;
+    int d = (8 * b + 5) / 25 - 5;
+    int e = (y * 5) / 4 - 10 - c;
+    int f = ((11 * a + 20 + d - c) % 30 + 30) % 30;
+    if (24 == f || (25 == f && a > 11))
+    {
+        ++f;
+    }
+    int g = 44 - f;
+    if (g < 21)
+    {
+        g += 30;
+    }
+    return QDate(y, 3, 1).addDays(g + 7 - (e + g) % 7 - 1);
+}
+
+/******************************************************************************/
diff --git a/src/holiday.hpp b/src/holiday.hpp
new file mode 100644
index 0000000..03f1c87
--- /dev/null
+++ b/src/holiday.hpp
@@ -0,0 +1,13 @@
+#ifndef _HOLIDAY_HPP
+#define _HOLIDAY_HPP
+
+#include <QDate>
+
+class Holiday
+{
+public:
+    static bool isHoliday(const QDate & date);
+    static QDate easter(int year);
+};
+
+#endif
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index ba1fc03..be4cd31 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -7,6 +7,7 @@ set(EXECUTABLE_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/test)
 set(TESTS
   qcron_test
   syntax_test
+  holiday_test
   )
 
 foreach(test ${TESTS})
diff --git a/test/holiday_test.cpp b/test/holiday_test.cpp
new file mode 100644
index 0000000..5c067cb
--- /dev/null
+++ b/test/holiday_test.cpp
@@ -0,0 +1,69 @@
+#include "holiday_test.hpp"
+
+#include <holiday.hpp>
+
+#include <QTest>
+
+void
+HolidayTest::
+easter()
+{
+    QCOMPARE(Holiday::easter(2000), QDate(2000, 4, 23));
+    QCOMPARE(Holiday::easter(2001), QDate(2001, 4, 15));
+    QCOMPARE(Holiday::easter(2002), QDate(2002, 3, 31));
+    QCOMPARE(Holiday::easter(2003), QDate(2003, 4, 20));
+    QCOMPARE(Holiday::easter(2004), QDate(2004, 4, 11));
+    QCOMPARE(Holiday::easter(2005), QDate(2005, 3, 27));
+    QCOMPARE(Holiday::easter(2006), QDate(2006, 4, 16));
+    QCOMPARE(Holiday::easter(2007), QDate(2007, 4, 8));
+    QCOMPARE(Holiday::easter(2008), QDate(2008, 3, 23));
+    QCOMPARE(Holiday::easter(2009), QDate(2009, 4, 12));
+    QCOMPARE(Holiday::easter(2010), QDate(2010, 4, 4));
+    QCOMPARE(Holiday::easter(2011), QDate(2011, 4, 24));
+    QCOMPARE(Holiday::easter(2012), QDate(2012, 4, 8));
+    QCOMPARE(Holiday::easter(2013), QDate(2013, 3, 31));
+    QCOMPARE(Holiday::easter(2014), QDate(2014, 4, 20));
+    QCOMPARE(Holiday::easter(2015), QDate(2015, 4, 5));
+    QCOMPARE(Holiday::easter(2016), QDate(2016, 3, 27));
+    QCOMPARE(Holiday::easter(2017), QDate(2017, 4, 16));
+    QCOMPARE(Holiday::easter(2018), QDate(2018, 4, 1));
+    QCOMPARE(Holiday::easter(2019), QDate(2019, 4, 21));
+    QCOMPARE(Holiday::easter(2020), QDate(2020, 4, 12));
+    QCOMPARE(Holiday::easter(2021), QDate(2021, 4, 4));
+    QCOMPARE(Holiday::easter(2022), QDate(2022, 4, 17));
+    QCOMPARE(Holiday::easter(2023), QDate(2023, 4, 9));
+    QCOMPARE(Holiday::easter(2024), QDate(2024, 3, 31));
+    QCOMPARE(Holiday::easter(2025), QDate(2025, 4, 20));
+    QCOMPARE(Holiday::easter(2026), QDate(2026, 4, 5));
+    QCOMPARE(Holiday::easter(2027), QDate(2027, 3, 28));
+    QCOMPARE(Holiday::easter(2028), QDate(2028, 4, 16));
+    QCOMPARE(Holiday::easter(2029), QDate(2029, 4, 1));
+    QCOMPARE(Holiday::easter(2030), QDate(2030, 4, 21));
+
+}
+
+/******************************************************************************/
+
+void
+HolidayTest::
+holiday()
+{
+    QVERIFY(Holiday::isHoliday( QDate(2016, 1, 1)));
+    QVERIFY(Holiday::isHoliday( QDate(2016, 5, 1)));
+    QVERIFY(Holiday::isHoliday( QDate(2016, 5, 8)));
+    QVERIFY(Holiday::isHoliday( QDate(2016, 7, 14)));
+    QVERIFY(Holiday::isHoliday( QDate(2016, 8, 15)));
+    QVERIFY(Holiday::isHoliday( QDate(2016, 11, 1)));
+    QVERIFY(Holiday::isHoliday( QDate(2016, 11, 11)));
+    QVERIFY(Holiday::isHoliday( QDate(2016, 12, 25)));
+    QVERIFY(Holiday::isHoliday( QDate(2016, 3, 28))); // Lundi de Paques
+    QVERIFY(Holiday::isHoliday( QDate(2016, 5, 5))); // Jeudi de l'Ascension
+    QVERIFY(Holiday::isHoliday( QDate(2016, 5, 16))); // Lundi de Pentecote
+
+    // Chuck Norris' birthday (should be a holiday though).
+    QVERIFY(!Holiday::isHoliday(QDate(2016, 3, 10)));
+}
+
+/******************************************************************************/
+
+QTEST_MAIN(HolidayTest)
diff --git a/test/holiday_test.hpp b/test/holiday_test.hpp
new file mode 100644
index 0000000..83d9377
--- /dev/null
+++ b/test/holiday_test.hpp
@@ -0,0 +1,15 @@
+#ifndef _HOLIDAY_TEST_HPP
+#define _HOLIDAY_TEST_HPP
+
+#include <QObject>
+
+class HolidayTest : public QObject
+{
+    Q_OBJECT
+
+private slots:
+    void easter();
+    void holiday();
+};
+
+#endif