File indexing completed on 2024-06-02 05:19:07

0001 /*
0002  *  holidays.cpp  -  holiday checker
0003  *  This file is part of kalarmcalendar library, which provides access to KAlarm
0004  *  calendar data.
0005  *  Program:  kalarm
0006  *  SPDX-FileCopyrightText: 2023 David Jarvie <djarvie@kde.org>
0007  *
0008  *  SPDX-License-Identifier: LGPL-2.0-or-later
0009  */
0010 
0011 #include "holidays.h"
0012 
0013 #include <KHolidays/HolidayRegion>
0014 
0015 namespace KAlarmCal
0016 {
0017 
0018 Holidays::Holidays(const KHolidays::HolidayRegion& holidayRegion)
0019 {
0020     initialise(holidayRegion.regionCode());
0021 }
0022 
0023 Holidays::Holidays(const QString& regionCode)
0024 {
0025     initialise(regionCode);
0026 }
0027 
0028 /******************************************************************************
0029 * Set a new holiday region.
0030 */
0031 void Holidays::setRegion(const KHolidays::HolidayRegion& holidayRegion)
0032 {
0033     if (holidayRegion.regionCode() == mRegion->regionCode())
0034         return;
0035     mTypes.clear();
0036     mNames.clear();
0037     initialise(holidayRegion.regionCode());
0038 }
0039 
0040 /******************************************************************************
0041 * Set a new holiday region.
0042 */
0043 void Holidays::setRegion(const QString& regionCode)
0044 {
0045     if (regionCode == mRegion->regionCode())
0046         return;
0047     mTypes.clear();
0048     mNames.clear();
0049     initialise(regionCode);
0050 }
0051 
0052 /******************************************************************************
0053 * Set a new holiday region.
0054 */
0055 void Holidays::initialise(const QString& regionCode)
0056 {
0057     mRegion.reset(new KHolidays::HolidayRegion(regionCode));
0058     mCacheStartDate = QDate::currentDate().addDays(-1);   // in case KAlarm time zone is different
0059     mNoCacheDate    = mCacheStartDate;
0060 
0061     if (mRegion->isValid())
0062     {
0063         // Initially cache holiday data up to a year from today
0064         const int COUNT = 366;
0065         extendCache(mCacheStartDate.addDays(COUNT - 1));
0066     }
0067 }
0068 
0069 /******************************************************************************
0070 * Return the holiday region code.
0071 */
0072 QString Holidays::regionCode() const
0073 {
0074     return mRegion ? mRegion->regionCode() : QString();
0075 }
0076 
0077 /******************************************************************************
0078 * Return whether the holiday data is valid.
0079 */
0080 bool Holidays::isValid() const
0081 {
0082     return mRegion && mRegion->isValid();
0083 }
0084 
0085 /******************************************************************************
0086 * Determine whether a date is a non-working holiday.
0087 */
0088 bool Holidays::isHoliday(const QDate& date) const
0089 {
0090     return holidayType(date) == NonWorking;
0091 }
0092 
0093 /******************************************************************************
0094 * Determine the holiday type for a date.
0095 */
0096 Holidays::Type Holidays::holidayType(const QDate& date) const
0097 {
0098     if (date < QDate::currentDate().addDays(-1))
0099         return None;
0100     const int offset = mCacheStartDate.daysTo(date);
0101     if (date < mNoCacheDate)
0102         return mTypes[offset*2] ? NonWorking : mTypes[offset*2 + 1] ? Working : None;
0103     // The date is past the end of the cache. Fill the cache.
0104     extendCache(QDate(date.year(), 12, 31));
0105     if (date < mNoCacheDate)
0106         return mTypes[offset*2] ? NonWorking : mTypes[offset*2 + 1] ? Working : None;
0107 
0108     // The date is past the maximum cache limit.
0109     const KHolidays::Holiday::List hols = mRegion->rawHolidaysWithAstroSeasons(date);
0110     for (const KHolidays::Holiday& h : hols)
0111         if (h.dayType() == KHolidays::Holiday::NonWorkday)
0112             return NonWorking;
0113     return hols.isEmpty() ? None : Working;
0114 }
0115 
0116 /******************************************************************************
0117 * Return the name of a holiday.
0118 */
0119 QStringList Holidays::holidayNames(const QDate& date) const
0120 {
0121     if (date < QDate::currentDate().addDays(-1))
0122         return {};
0123     if (date < mNoCacheDate)
0124         return mNames[mCacheStartDate.daysTo(date)];
0125     // The date is past the end of the cache. Fill the cache.
0126     extendCache(QDate(date.year(), 12, 31));
0127     if (date < mNoCacheDate)
0128         return mNames[mCacheStartDate.daysTo(date)];
0129 
0130     // The date is past the maximum cache limit.
0131     const KHolidays::Holiday::List hols = mRegion->rawHolidaysWithAstroSeasons(date);
0132     QStringList names;
0133     for (const KHolidays::Holiday& h : hols)
0134         names.append(h.name());
0135     return names;
0136 }
0137 
0138 /******************************************************************************
0139 * Set the maximum cache size.
0140 */
0141 void Holidays::setCacheYears(int years)
0142 {
0143     mCacheYears = years;
0144 }
0145 
0146 /******************************************************************************
0147 * Cache holiday data up to an end date.
0148 * This will not be done past mCacheYears from now.
0149 */
0150 void Holidays::extendCache(const QDate& end) const
0151 {
0152     const QDate limit = QDate(QDate::currentDate().year() + mCacheYears, 12, 31);
0153     const QDate endDate = (end > limit) ? limit : end;
0154     if (endDate < mNoCacheDate)
0155         return;    // already cached
0156 
0157     const int count = mCacheStartDate.daysTo(endDate) + 1;
0158     const KHolidays::Holiday::List hols = mRegion->rawHolidaysWithAstroSeasons(mNoCacheDate, endDate);
0159     mTypes.resize(count * 2);   // this sets all new bits to 0
0160     mNames.resize(count);
0161     // Note that more than one holiday can fall on a given day.
0162     for (const KHolidays::Holiday& h : hols)
0163     {
0164         const QString name = h.name();
0165         const int workday = (h.dayType() == KHolidays::Holiday::NonWorkday) ? 0 : 1;
0166         const int offset2 = mCacheStartDate.daysTo(h.observedEndDate());
0167         for (int offset = mCacheStartDate.daysTo(h.observedStartDate());  offset <= offset2;  ++offset)
0168         {
0169             mTypes[offset*2 + workday] = 1;
0170             mNames[offset].append(name);
0171         }
0172     }
0173     mNoCacheDate = endDate.addDays(1);
0174 }
0175 
0176 } // namespace KAlarmCal
0177 
0178 // vim: et sw=4: