Warning, file /frameworks/kholidays/src/parsers/plan2/holidayparserdriverplan.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 Original version from plan: 0003 SPDX-FileCopyrightText: Thomas Driemeyer <thomas@bitrot.de> 0004 0005 Adapted for use in KOrganizer: 0006 SPDX-FileCopyrightText: Preston Brown <pbrown@kde.org> 0007 SPDX-FileCopyrightText: Reinhold Kainhofer <reinhold@kainhofer.com> 0008 0009 Portions contributed: 0010 SPDX-FileCopyrightText: Peter Littlefield <plittle@sofkin.ca> 0011 SPDX-FileCopyrightText: Armin Liebl <liebla@informatik.tu-muenchen.de> 0012 SPDX-FileCopyrightText: Efthimios Mavrogeorgiadis <emav@enl.auth.gr> 0013 SPDX-FileCopyrightText: Erwin Hugo Achermann <acherman@inf.ethz.ch> 0014 0015 Major rewrite using Bison C++ skeleton: 0016 SPDX-FileCopyrightText: 2010 John Layt <john@layt.net> 0017 0018 SPDX-License-Identifier: LGPL-2.0-or-later 0019 */ 0020 0021 #include "holiday_p.h" 0022 #include "holidayparserdriverplan_p.h" 0023 #include "holidayparserplan.hpp" 0024 #include "holidayscannerplan_p.h" 0025 #include <kholidays_debug.h> 0026 0027 #include <QFileInfo> 0028 0029 #include <sstream> 0030 0031 #define LAST 99999 0032 #define ANY (-99999) 0033 #define BEFORE (-1) 0034 #define AFTER 1 0035 0036 using namespace KHolidays; 0037 0038 HolidayParserDriverPlan::HolidayParserDriverPlan(const QString &planFilePath) 0039 : HolidayParserDriver(planFilePath) 0040 , m_traceParsing(false) 0041 , m_traceScanning(false) 0042 , m_parseMetadataOnly(false) 0043 , m_eventYear(0) 0044 , m_eventMonth(0) 0045 , m_eventDay(0) 0046 { 0047 QFile holidayFile(filePath()); 0048 if (holidayFile.open(QIODevice::ReadOnly)) { 0049 m_scanData = holidayFile.readAll(); 0050 holidayFile.close(); 0051 } 0052 m_scanner = new HolidayScannerPlan(); 0053 m_scanner->set_debug(m_traceScanning); 0054 m_parser = new HolidayParserPlan(*this); 0055 m_parser->set_debug_level(m_traceParsing); 0056 m_fileToParse = new std::string(filePath().toLocal8Bit().data()); 0057 parseMetadata(); 0058 } 0059 0060 HolidayParserDriverPlan::~HolidayParserDriverPlan() 0061 { 0062 delete m_parser; 0063 delete m_scanner; 0064 delete m_fileToParse; 0065 } 0066 0067 // TODO Figure why it doesn't compile 0068 void HolidayParserDriverPlan::error(const KHolidays::location &errorLocation, const QString &errorMessage) 0069 { 0070 Q_UNUSED(errorLocation); 0071 // std::cerr << errorLocation << " : " << errorMessage; //Doesn't work??? 0072 // qDebug() << errorLocation << " : " << errorMessage; //Doesn't work??? 0073 qCDebug(KHOLIDAYS_LOG) << errorMessage; 0074 } 0075 0076 void HolidayParserDriverPlan::error(const QString &errorMessage) 0077 { 0078 qCDebug(KHOLIDAYS_LOG) << errorMessage; 0079 } 0080 0081 void HolidayParserDriverPlan::parse() 0082 { 0083 // Parse the file using every calendar system in the file 0084 for (const QString &calendarType : std::as_const(m_fileCalendarTypes)) { 0085 // Cater for events defined in other Calendar Systems where request year could cover 2 or 3 event years 0086 // Perhaps also parse year before and year after to allow events to span years or shift to other year? 0087 setParseCalendar(calendarType); 0088 setParseStartEnd(); 0089 0090 // Generate all events for this calendar in the required year(s) 0091 for (m_parseYear = m_parseStartYear; m_parseYear <= m_parseEndYear; ++m_parseYear) { 0092 m_parseYearStart = m_parseCalendar.firstDayOfYear(m_parseYear); 0093 m_parseYearEaster = easter(m_parseYear); 0094 m_parseYearPascha = pascha(m_parseYear); 0095 0096 std::istringstream iss2(std::string(m_scanData.data())); 0097 m_scanner->yyrestart(&iss2); 0098 0099 m_parser->parse(); 0100 } 0101 } 0102 } 0103 0104 void HolidayParserDriverPlan::parseMetadata() 0105 { 0106 m_parseMetadataOnly = true; 0107 m_fileCountryCode.clear(); 0108 m_fileLanguageCode.clear(); 0109 m_fileName.clear(); 0110 m_fileDescription.clear(); 0111 m_fileCalendarTypes.clear(); 0112 m_fileCalendarTypes.append(QStringLiteral("gregorian")); 0113 0114 // Default to files internal metadata 0115 setParseCalendar(QStringLiteral("gregorian")); 0116 m_parseYear = QDate::currentDate().year(); 0117 std::istringstream iss2(std::string(m_scanData.data())); 0118 m_scanner->yyrestart(&iss2); 0119 m_parser->parse(); 0120 m_resultList.clear(); 0121 0122 // If not populated, then use filename metadata, this may change later 0123 // metadata is encoded in filename in form holiday_<region>_<type>_<language>_<name> 0124 // with region, type and language sub groups separated by -, and with name optional 0125 QFileInfo file(m_filePath); 0126 if (file.exists()) { 0127 QStringList metadata = file.fileName().split(QLatin1Char('_')); 0128 if (metadata[0] == QLatin1String("holiday") && metadata.count() > 2) { 0129 if (m_fileCountryCode.isEmpty()) { 0130 setFileCountryCode(metadata[1].toUpper()); 0131 } 0132 if (m_fileLanguageCode.isEmpty()) { 0133 QStringList language = metadata[2].split(QLatin1Char('-')); 0134 m_fileLanguageCode = language[0]; 0135 if (language.count() > 1) { 0136 setFileLanguageCode(language[0].append(QLatin1Char('_')).append(language[1].toUpper())); 0137 } else { 0138 setFileLanguageCode(language[0]); 0139 } 0140 } 0141 if (m_fileLanguageCode.isEmpty() && metadata.count() > 3) { 0142 m_fileName = metadata[3]; 0143 } 0144 } 0145 } 0146 0147 m_parseMetadataOnly = false; 0148 } 0149 0150 void HolidayParserDriverPlan::setParseCalendar(QCalendarSystem::CalendarSystem calendar) 0151 { 0152 m_parseCalendarType = systemToType(calendar); 0153 HolidayParserDriver::setParseCalendar(calendar); 0154 } 0155 0156 QString HolidayParserDriverPlan::filePath() 0157 { 0158 return m_filePath; 0159 } 0160 0161 std::string *HolidayParserDriverPlan::fileToParse() const 0162 { 0163 return m_fileToParse; 0164 } 0165 0166 /***************************************** 0167 Calendar and Date convenience routines 0168 ******************************************/ 0169 0170 // Adjust month numbering for Hebrew civil calendar leap month 0171 int HolidayParserDriverPlan::adjustedMonthNumber(int month) 0172 { 0173 if (m_eventCalendarType != QLatin1String("hebrew") || // Only adjust Hebrew months 0174 m_parseCalendarType != QLatin1String("hebrew") || !m_parseCalendar.isLeapYear(m_parseYear) || // Only adjust in leap year 0175 month < 6) { // Only adjust from Adar onwards 0176 return month; 0177 } 0178 0179 if (month == 13) { // Adar I 0180 return 6; 0181 } 0182 0183 if (month == 14) { // Adar II 0184 return 7; 0185 } 0186 0187 return month + 1; // Inserting Adar I moves other months up by 1 0188 } 0189 0190 bool HolidayParserDriverPlan::isLeapYear(int year) 0191 { 0192 return m_parseCalendar.isLeapYear(year); 0193 } 0194 0195 int HolidayParserDriverPlan::parseYear() 0196 { 0197 return m_parseYear; 0198 } 0199 0200 int HolidayParserDriverPlan::julianDay(int year, int month, int day) 0201 { 0202 return m_parseCalendar.date(year, month, day).toJulianDay(); 0203 } 0204 0205 void HolidayParserDriverPlan::julianDayToDate(int jd, int *year, int *month, int *day) 0206 { 0207 QDate tempDate = QDate::fromJulianDay(jd); 0208 0209 if (year) { 0210 *year = m_parseCalendar.year(tempDate); 0211 } 0212 if (month) { 0213 *month = m_parseCalendar.month(tempDate); 0214 } 0215 if (day) { 0216 *day = m_parseCalendar.day(tempDate); 0217 } 0218 } 0219 0220 QDate HolidayParserDriverPlan::easter(int year) 0221 { 0222 if (m_parseCalendar.calendarSystem() != QCalendarSystem::GregorianCalendar) { 0223 return QDate(); 0224 } 0225 if (year < 0) { 0226 return QDate(); 0227 } 0228 0229 // Algorithm taken from Tondering 0230 // https://www.tondering.dk/claus/cal/easter.php 0231 int g = year % 19; 0232 int c = year / 100; 0233 int h = (c - (c / 4) - (((8 * c) + 13) / 25) + (19 * g) + 15) % 30; 0234 int i = h - ((h / 28) * (1 - ((29 / (h + 1)) * ((21 - g) / 11)))); 0235 int j = (year + (year / 4) + i + 2 - c + (c / 4)) % 7; 0236 int l = i - j; 0237 int month = 3 + ((l + 40) / 44); 0238 int day = l + 28 - (31 * (month / 4)); 0239 0240 return QDate::fromJulianDay(julianDay(year, month, day)); 0241 } 0242 0243 QDate HolidayParserDriverPlan::pascha(int year) 0244 { 0245 if (year < 0) { 0246 return QDate(); 0247 } 0248 0249 if (m_parseCalendar.calendarSystem() == QCalendarSystem::GregorianCalendar || m_parseCalendar.calendarSystem() == QCalendarSystem::JulianCalendar) { 0250 // Algorithm taken from Tondering 0251 // https://www.tondering.dk/claus/cal/easter.php#orthodoxeast 0252 // Gives Orthodox Easter in the Julian Calendar, need to convert afterwards to Gregorian if needed 0253 int g = year % 19; 0254 int i = ((19 * g) + 15) % 30; 0255 int j = (year + (year / 4) + i) % 7; 0256 int l = i - j; 0257 int month = 3 + ((l + 40) / 44); 0258 int day = l + 28 - (31 * (month / 4)); 0259 0260 if (m_parseCalendar.calendarSystem() == QCalendarSystem::JulianCalendar) { 0261 return QDate::fromJulianDay(julianDay(year, month, day)); 0262 } 0263 0264 if (m_parseCalendar.calendarSystem() == QCalendarSystem::GregorianCalendar) { 0265 setParseCalendar(QStringLiteral("julian")); 0266 int paschaJd = julianDay(year, month, day); 0267 setParseCalendar(QStringLiteral("gregorian")); 0268 return QDate::fromJulianDay(paschaJd); 0269 } 0270 } 0271 0272 return QDate(); 0273 } 0274 0275 QCalendarSystem::CalendarSystem HolidayParserDriverPlan::typeToSystem(const QString &calendarType) 0276 { 0277 if (calendarType == QStringLiteral("gregorian")) { 0278 return QCalendarSystem::GregorianCalendar; 0279 } else if (calendarType == QStringLiteral("hebrew")) { 0280 return QCalendarSystem::HebrewCalendar; 0281 } else if (calendarType == QStringLiteral("hijri")) { 0282 return QCalendarSystem::IslamicCivilCalendar; 0283 } else if (calendarType == QStringLiteral("jalali")) { 0284 return QCalendarSystem::PersianCalendar; 0285 } else if (calendarType == QStringLiteral("julian")) { 0286 return QCalendarSystem::JulianCalendar; 0287 } else if (calendarType == QStringLiteral("coptic")) { 0288 return QCalendarSystem::CopticCalendar; 0289 } else if (calendarType == QStringLiteral("ethiopian")) { 0290 return QCalendarSystem::EthiopicCalendar; 0291 } else if (calendarType == QStringLiteral("indiannational")) { 0292 return QCalendarSystem::IndianNationalCalendar; 0293 } 0294 return QCalendarSystem::GregorianCalendar; 0295 } 0296 0297 QString HolidayParserDriverPlan::systemToType(QCalendarSystem::CalendarSystem calendar) 0298 { 0299 switch (calendar) { 0300 case QCalendarSystem::GregorianCalendar: 0301 return QStringLiteral("gregorian"); 0302 case QCalendarSystem::HebrewCalendar: 0303 return QStringLiteral("hebrew"); 0304 case QCalendarSystem::IslamicCivilCalendar: 0305 return QStringLiteral("hijri"); 0306 case QCalendarSystem::PersianCalendar: 0307 return QStringLiteral("jalali"); 0308 case QCalendarSystem::JulianCalendar: 0309 return QStringLiteral("julian"); 0310 case QCalendarSystem::CopticCalendar: 0311 return QStringLiteral("coptic"); 0312 case QCalendarSystem::EthiopicCalendar: 0313 return QStringLiteral("ethiopian"); 0314 case QCalendarSystem::IndianNationalCalendar: 0315 return QStringLiteral("indiannational"); 0316 default: 0317 return QStringLiteral("gregorian"); 0318 } 0319 } 0320 0321 /************************* 0322 * Calculate jd routines * 0323 *************************/ 0324 0325 // Return the jd of an existing event, assumes unique names and correct order in file 0326 int HolidayParserDriverPlan::julianDayFromEventName(const QString &eventName) 0327 { 0328 for (const KHolidays::Holiday &thisHoliday : std::as_const(m_resultList)) { 0329 if (thisHoliday.name() == eventName) { 0330 return thisHoliday.observedStartDate().toJulianDay(); 0331 } 0332 } 0333 return -1; 0334 } 0335 0336 // Return jd of Easter if Gregorian 0337 int HolidayParserDriverPlan::julianDayFromEaster() 0338 { 0339 if (m_eventCalendarType == QLatin1String("gregorian")) { 0340 return m_parseYearEaster.toJulianDay(); 0341 } else { 0342 error(QStringLiteral("Can only use Easter in Gregorian event rule")); 0343 return -1; 0344 } 0345 } 0346 0347 // Return jd of Orthodox Easter if Gregorian or Julian 0348 int HolidayParserDriverPlan::julianDayFromPascha() 0349 { 0350 if (m_eventCalendarType == QLatin1String("gregorian") || m_eventCalendarType == QLatin1String("julian")) { 0351 return m_parseYearPascha.toJulianDay(); 0352 } else { 0353 error(QStringLiteral("Can only use Easter in Gregorian or Julian event rule")); 0354 return -1; 0355 } 0356 } 0357 0358 // Return jd of weekday from a month/day in parse year 0359 int HolidayParserDriverPlan::julianDayFromMonthDay(int month, int day) 0360 { 0361 return julianDay(m_parseYear, month, day); 0362 } 0363 0364 // Return jd of weekday relative to a Julian Day number 0365 int HolidayParserDriverPlan::julianDayFromRelativeWeekday(int occurrence, int weekday, int jd) 0366 { 0367 if (occurrence == ANY) { /* Should never get this, convert to AFTER instead */ 0368 occurrence = AFTER; 0369 } 0370 0371 int thisWeekday = m_parseCalendar.dayOfWeek(QDate::fromJulianDay(jd)); 0372 0373 /* AFTER actually means on or after */ 0374 /* BEFORE actually means on or before */ 0375 if (occurrence > 0) { 0376 occurrence = occurrence - 1; 0377 } else if (occurrence < 0 && weekday == thisWeekday) { 0378 occurrence = occurrence + 1; 0379 } 0380 0381 if (weekday < thisWeekday) { 0382 occurrence = occurrence + 1; 0383 } 0384 0385 return jd + weekday - thisWeekday + (occurrence * 7); 0386 } 0387 0388 // Return jd of weekday occurrence in a given month and day in the parse year 0389 int HolidayParserDriverPlan::julianDayFromWeekdayInMonth(int occurrence, int weekday, int month) 0390 { 0391 if (occurrence == LAST) { // Is weekday on or before last day of month 0392 return julianDayFromRelativeWeekday(BEFORE, weekday, julianDay(m_parseYear, month, m_parseCalendar.daysInMonth(m_parseYear, month))); 0393 } else { // Is nth weekday on or after first day of month 0394 return julianDayFromRelativeWeekday(occurrence, weekday, julianDay(m_parseYear, month, 1)); 0395 } 0396 } 0397 0398 /**************************************************** 0399 * Set parsed event variables convenience functions * 0400 ****************************************************/ 0401 0402 void HolidayParserDriverPlan::setParseCalendar(const QString &calendarType) 0403 { 0404 m_parseCalendarType = calendarType; 0405 setParseCalendar(typeToSystem(calendarType)); 0406 } 0407 0408 void HolidayParserDriverPlan::setFileCountryCode(const QString &countryCode) 0409 { 0410 m_fileCountryCode = countryCode; 0411 } 0412 0413 void HolidayParserDriverPlan::setFileLanguageCode(const QString &languageCode) 0414 { 0415 m_fileLanguageCode = languageCode; 0416 } 0417 0418 void HolidayParserDriverPlan::setFileName(const QString &name) 0419 { 0420 m_fileName = name; 0421 } 0422 0423 void HolidayParserDriverPlan::setFileDescription(const QString &description) 0424 { 0425 m_fileDescription = description; 0426 } 0427 0428 void HolidayParserDriverPlan::setEventName(const QString &eventName) 0429 { 0430 // Assume if setting an event name then is start of new event line, so clear categories 0431 m_eventCategories.clear(); 0432 m_eventName = eventName; 0433 } 0434 0435 void HolidayParserDriverPlan::setEventCategory(const QString &category) 0436 { 0437 m_eventCategories.append(category); 0438 } 0439 0440 void HolidayParserDriverPlan::setEventCalendarType(const QString &calendarType) 0441 { 0442 m_eventCalendarType = calendarType; 0443 if (m_parseMetadataOnly && !m_fileCalendarTypes.contains(calendarType)) { 0444 m_fileCalendarTypes.append(calendarType); 0445 } 0446 } 0447 0448 void HolidayParserDriverPlan::setEventDate(int eventYear, int eventMonth, int eventDay) 0449 { 0450 m_eventYear = eventYear; 0451 m_eventMonth = eventMonth; 0452 m_eventDay = eventDay; 0453 } 0454 0455 void HolidayParserDriverPlan::setEventDate(int jd) 0456 { 0457 julianDayToDate(jd, &m_eventYear, &m_eventMonth, &m_eventDay); 0458 } 0459 0460 /******************************************** 0461 * Set event date from event rules routines * 0462 ********************************************/ 0463 0464 /* 0465 * Set event by weekday (Monday..Sunday). The rule expression is 0466 * "every <occurrence> <weekday> of <month> plus <offset> days length <duration> days". 0467 * Occurrence and month can be ANY or LAST, offset and duration are optional. 0468 */ 0469 0470 void HolidayParserDriverPlan::setFromWeekdayInMonth(int occurrence, int weekday, int month, int offset, int duration) 0471 { 0472 // Don't set if only parsing metadata or calendar for event rule is not the current parse calendar 0473 if (m_parseMetadataOnly || m_eventCalendarType != m_parseCalendarType) { 0474 return; 0475 } 0476 0477 int startMonth, endMonth; 0478 if (month == LAST) { 0479 startMonth = m_parseCalendar.monthsInYear(m_parseYear); 0480 endMonth = startMonth; 0481 } else if (month == ANY) { 0482 startMonth = 1; 0483 endMonth = m_parseCalendar.monthsInYear(m_parseYear); 0484 } else { 0485 startMonth = month; 0486 endMonth = month; 0487 } 0488 0489 // Generate all events in the required event month(s) 0490 for (int thisMonth = startMonth; thisMonth <= endMonth; ++thisMonth) { 0491 if (m_parseCalendar.isValid(m_parseYear, thisMonth, 1)) { 0492 int startOccurrence, endOccurrence; 0493 if (occurrence == ANY) { // Generate 1st through 5th weekdays, assumes no month with > 35 days 0494 startOccurrence = 1; 0495 endOccurrence = 5; 0496 } else { // Generate just nth or LAST weekday 0497 startOccurrence = occurrence; 0498 endOccurrence = occurrence; 0499 } 0500 0501 int jdMonthStart = julianDay(m_parseYear, thisMonth, 1); 0502 int jdMonthEnd = julianDay(m_parseYear, thisMonth, m_parseCalendar.daysInMonth(m_parseYear, thisMonth)); 0503 0504 // Generate each required occurrence of weekday in month, check occurrence actually falls in month 0505 for (int thisOccurrence = startOccurrence; thisOccurrence <= endOccurrence; ++thisOccurrence) { 0506 int thisJd = julianDayFromWeekdayInMonth(thisOccurrence, weekday, thisMonth); 0507 if (thisJd >= jdMonthStart && thisJd <= jdMonthEnd) { 0508 setEvent(thisJd + offset, 0, duration); 0509 } 0510 } 0511 } 0512 } 0513 } 0514 0515 /* 0516 * Set event by weekday (Monday..Sunday) relative to a date. The expression is 0517 * "<weekday> <occurrence> <date> plus <offset> days length <duration> days". 0518 * Occurrence, month and day can be ANY or LAST, year can be ANY, offset and duration are optional. 0519 */ 0520 0521 void HolidayParserDriverPlan::setFromRelativeWeekday(int occurrence, int weekday, int offset, int duration) 0522 { 0523 // Don't set if only parsing metadata or calendar for event rule is not the current parse calendar 0524 if (m_parseMetadataOnly || m_eventCalendarType != m_parseCalendarType) { 0525 return; 0526 } 0527 0528 int thisYear; 0529 if (m_eventYear == ANY) { // Generate the parse year 0530 thisYear = m_parseYear; 0531 } else { // Generate a specific event year 0532 thisYear = m_eventYear; 0533 } 0534 0535 int startMonth, endMonth; 0536 if (m_eventMonth == LAST) { // Generate just the last month 0537 startMonth = m_parseCalendar.monthsInYear(thisYear); 0538 endMonth = startMonth; 0539 } else if (m_eventMonth == ANY) { // Generate every month 0540 startMonth = 1; 0541 endMonth = m_parseCalendar.monthsInYear(thisYear); 0542 } else { // Generate just the specific event month 0543 startMonth = m_eventMonth; 0544 endMonth = m_eventMonth; 0545 } 0546 0547 // Generate all events in the required month(s) 0548 int thisMonth; 0549 for (thisMonth = startMonth; thisMonth <= endMonth; ++thisMonth) { 0550 int startDay, endDay; 0551 if (m_eventDay == LAST) { // Generate just the last day in the month 0552 startDay = m_parseCalendar.daysInMonth(thisYear, thisMonth); 0553 endDay = startDay; 0554 } else if (m_eventDay == ANY) { // Generate every day in the month 0555 startDay = 1; 0556 endDay = m_parseCalendar.daysInMonth(thisYear, thisMonth); 0557 } else { // Generate just the specific event day 0558 startDay = m_eventDay; 0559 endDay = m_eventDay; 0560 } 0561 0562 // Generate all events on the required day(s) 0563 for (int thisDay = startDay; thisDay <= endDay; ++thisDay) { 0564 if (m_parseCalendar.isValid(thisYear, thisMonth, thisDay)) { 0565 int relativeJd = julianDayFromRelativeWeekday(occurrence, weekday, julianDay(thisYear, thisMonth, thisDay)); 0566 setEvent(relativeJd + offset, 0, duration); 0567 } 0568 } 0569 } 0570 } 0571 0572 // TODO Figure out how this works :-) 0573 int HolidayParserDriverPlan::conditionalOffset(int year, int month, int day, int condition) 0574 { 0575 /** The encoding of the condition is: 0576 8 lower bits: conditions to shift (bit-register, bit 1=mon, ..., bit 7=sun) 0577 8 higher bits: weekday to shift to (bit-register, bit 1=mon, ..., bit 7=sun) 0578 */ 0579 0580 int offset = 0; 0581 0582 int weekday = m_parseCalendar.dayOfWeek(year, month, day); 0583 0584 if (condition & (1 << weekday)) { 0585 /* condition matches -> higher 8 bits contain the possible days to shift to */ 0586 int to = (condition >> 8); 0587 while (!(to & (1 << ((weekday + offset) % 7))) && (offset < 8)) { 0588 ++offset; 0589 } 0590 } 0591 0592 if (offset >= 8) { 0593 offset = 0; 0594 } 0595 0596 return offset; 0597 } 0598 0599 /* 0600 * Set event by date. The expression is 0601 * "<date> plus <offset> days shift <condition> length <duration> days". 0602 * Occurrence, month and day can be ANY or LAST, year can be ANY, offset and duration are optional. 0603 */ 0604 0605 void HolidayParserDriverPlan::setFromDate(int offset, int condition, int duration) 0606 { 0607 // Don't set if only parsing metadata or calendar for event rule is not the current parse calendar 0608 if (m_parseMetadataOnly || m_eventCalendarType != m_parseCalendarType) { 0609 return; 0610 } 0611 0612 int thisYear; 0613 if (m_eventYear == ANY) { // Generate the parse year 0614 thisYear = m_parseYear; 0615 } else { // Generate a specific event year 0616 thisYear = m_eventYear; 0617 } 0618 0619 int startMonth, endMonth; 0620 if (m_eventMonth == LAST) { // Generate just the last month 0621 startMonth = m_parseCalendar.monthsInYear(thisYear); 0622 endMonth = startMonth; 0623 } else if (m_eventMonth == ANY) { // Generate every month 0624 startMonth = 1; 0625 endMonth = m_parseCalendar.monthsInYear(thisYear); 0626 } else { // Generate just the specific event month 0627 startMonth = m_eventMonth; 0628 endMonth = m_eventMonth; 0629 } 0630 0631 // Generate all events in the required month(s) 0632 for (int thisMonth = startMonth; thisMonth <= endMonth; ++thisMonth) { 0633 int startDay, endDay; 0634 if (m_eventDay == LAST) { // Generate just the last day in the month 0635 startDay = m_parseCalendar.daysInMonth(thisYear, thisMonth); 0636 endDay = startDay; 0637 } else if (m_eventDay == ANY) { // Generate every day in the month 0638 startDay = 1; 0639 endDay = m_parseCalendar.daysInMonth(thisYear, thisMonth); 0640 } else { // Generate just the specific event day 0641 startDay = m_eventDay; 0642 endDay = m_eventDay; 0643 } 0644 0645 // Generate all events on the required day(s) 0646 for (int thisDay = startDay; thisDay <= endDay; ++thisDay) { 0647 if (m_parseCalendar.isValid(thisYear, thisMonth, thisDay)) { 0648 setEvent(julianDay(thisYear, thisMonth, thisDay) + offset, // 0649 conditionalOffset(thisYear, thisMonth, thisDay, condition), // 0650 duration); 0651 } 0652 } 0653 } 0654 } 0655 0656 /* 0657 * Set event relative to Easter. The expression is 0658 * "EASTER plus <offset> days length <duration> days". 0659 * Offset and duration are optional. 0660 */ 0661 0662 void HolidayParserDriverPlan::setFromEaster(int offset, int duration) 0663 { 0664 // Don't set if only parsing metadata or calendar for event rule is not the current parse calendar 0665 if (m_parseMetadataOnly || m_eventCalendarType != m_parseCalendarType) { 0666 return; 0667 } 0668 0669 if (m_eventCalendarType == QLatin1String("gregorian")) { 0670 setEvent(m_parseYearEaster.toJulianDay() + offset, 0, duration); 0671 } else { 0672 error(QStringLiteral("Can only use Easter in Gregorian event rule")); 0673 } 0674 } 0675 0676 /* 0677 * Set event relative to Pascha. The expression is 0678 * "PASCHA plus <offset> days length <duration> days". 0679 * Offset and duration are optional. 0680 */ 0681 0682 void HolidayParserDriverPlan::setFromPascha(int offset, int duration) 0683 { 0684 // Don't set if only parsing metadata or calendar for event rule is not the current parse calendar 0685 if (m_parseMetadataOnly || m_eventCalendarType != m_parseCalendarType) { 0686 return; 0687 } 0688 0689 if (m_eventCalendarType == QLatin1String("gregorian") || m_eventCalendarType == QLatin1String("julian")) { 0690 setEvent(m_parseYearPascha.toJulianDay(), offset, duration); 0691 } else { 0692 error(QStringLiteral("Can only use Pascha in Julian and Gregorian event rule")); 0693 } 0694 } 0695 0696 // Set the event if it falls inside the requested date range 0697 void HolidayParserDriverPlan::setEvent(int jd, int observeOffset, int duration) 0698 { 0699 // Don't set if only parsing metadata or calendar for event rule is not the current parse calendar 0700 if (m_parseMetadataOnly || m_eventCalendarType != m_parseCalendarType) { 0701 return; 0702 } 0703 0704 // Date the holiday will be observed on 0705 int observeJd = jd + observeOffset; 0706 0707 addHoliday(QDate::fromJulianDay(observeJd), duration); 0708 } 0709 0710 void HolidayParserDriverPlan::addHoliday(const QDate &observedDate, int duration) 0711 { 0712 // Only set if event falls in requested date range, i.e. either starts or ends during range 0713 if (m_parseCalendar.isValid(observedDate) // 0714 && observedDate <= m_requestEnd // 0715 && observedDate.addDays(duration - 1) >= m_requestStart) { 0716 KHolidays::Holiday holiday; 0717 holiday.d->mObservedDate = observedDate; 0718 holiday.d->mDuration = duration; 0719 holiday.d->mName = m_eventName; 0720 holiday.d->mDescription = m_eventName; 0721 holiday.d->mCategoryList = m_eventCategories; 0722 if (m_eventCategories.contains(QStringLiteral("public"))) { 0723 holiday.d->mDayType = KHolidays::Holiday::NonWorkday; 0724 } else { 0725 holiday.d->mDayType = KHolidays::Holiday::Workday; 0726 } 0727 m_resultList.append(holiday); 0728 } 0729 }