File indexing completed on 2024-11-24 04:42:24

0001 /*
0002  *  karecurrence.h  -  recurrence with special yearly February 29th handling
0003  *  This file is part of kalarmcalendar library, which provides access to KAlarm
0004  *  calendar data.
0005  *  Program:  kalarm
0006  *  SPDX-FileCopyrightText: 2005-2022 David Jarvie <djarvie@kde.org>
0007  *
0008  *  SPDX-License-Identifier: LGPL-2.0-or-later
0009  */
0010 
0011 #pragma once
0012 
0013 #include "kalarmcal_export.h"
0014 #include "kadatetime.h"
0015 
0016 #include <KCalendarCore/RecurrenceRule>
0017 #include <KCalendarCore/Duration>
0018 #include <KCalendarCore/IncidenceBase>
0019 namespace KCalendarCore
0020 {
0021 class Recurrence;
0022 }
0023 
0024 #include <QBitArray>
0025 
0026 namespace KAlarmCal
0027 {
0028 
0029 /**
0030  * @short Represents recurrences for KAlarm.
0031  *
0032  * This class represents the restricted range of recurrence types which are
0033  * handled by KAlarm, and translates between these and the libkcalendarcore
0034  * Recurrence class. In particular, it handles yearly recurrences on 29th
0035  * February in non-leap years specially:
0036  *
0037  * KARecurrence allows annual 29th February recurrences to fall on 28th
0038  * February or 1st March, or not at all, in non-leap years. It allows such
0039  * 29th February recurrences to be combined with the 29th of other months in
0040  * a simple way, represented simply as the 29th of multiple months including
0041  * February. For storage in the libkcalendarcore calendar, the 29th day of the month
0042  * recurrence for other months is combined with a last-day-of-February or a
0043  * 60th-day-of-the-year recurrence rule, thereby conforming to RFC2445.
0044  *
0045  * @author David Jarvie <djarvie@kde.org>
0046  */
0047 class KALARMCAL_EXPORT KARecurrence
0048 {
0049 public:
0050     /** The recurrence's period type.
0051      *  This is a subset of the possible KCalendarCore recurrence types.
0052      */
0053     enum Type
0054     {
0055         NO_RECUR,      //!< does not recur
0056         MINUTELY,      //!< at an hours/minutes interval
0057         DAILY,         //!< daily
0058         WEEKLY,        //!< weekly, on specified weekdays
0059         MONTHLY_POS,   //!< monthly, on specified weekdays in a specified week of the month
0060         MONTHLY_DAY,   //!< monthly, on a specified day of the month
0061         ANNUAL_DATE,   //!< yearly, on a specified date in each of the specified months
0062         ANNUAL_POS     //!< yearly, on specified weekdays in the specified weeks of the specified months
0063     };
0064 
0065     /** When annual February 29th recurrences should occur in non-leap years. */
0066     enum Feb29Type
0067     {
0068         Feb29_Feb28,   //!< occurs on 28 February in non-leap years
0069         Feb29_Mar1,    //!< occurs on 1 March in non-leap years
0070         Feb29_None     //!< does not occur in non-leap years
0071     };
0072 
0073     KARecurrence();
0074     KARecurrence(const KCalendarCore::Recurrence& r);   //cppcheck-suppress noExplicitConstructor; Allow implicit conversion
0075     KARecurrence(const KARecurrence& r);
0076     ~KARecurrence();
0077 
0078     /**
0079      * Assignment operator.
0080      * @param r the recurrence which will be assigned to this.
0081      */
0082     KARecurrence& operator=(const KARecurrence& r) = delete;
0083 
0084     /**
0085      * Comparison operator for equality.
0086      * @param r instance to compare with
0087      * @return true if recurrences are the same, false otherwise
0088      */
0089     bool operator==(const KARecurrence& r) const;
0090 
0091     /**
0092      * Comparison operator for inequality.
0093      * @param r instance to compare with
0094      * @return true if recurrences are the different, false if the same
0095      */
0096     bool operator!=(const KARecurrence& r) const
0097     {
0098         return !operator==(r);
0099     }
0100 
0101     /** Initialise the recurrence from an iCalendar RRULE string.
0102      *  @return true if successful, false if an error occurred.
0103      */
0104     bool set(const QString& icalRRULE);
0105 
0106     /** Set up a KARecurrence from recurrence parameters, using the start date to
0107      *  determine the recurrence day/month as appropriate.
0108      *  Annual 29th February recurrences in non-leap years will be handled
0109      *  according to the default set by setDefaultFeb29Type().
0110      *  Only a restricted subset of recurrence types is allowed: minutely, daily,
0111      *  weekly, monthly, yearly or none.
0112      *  @return true if successful.
0113      */
0114     bool set(Type t, int freq, int count, const KADateTime& start, const KADateTime& end);
0115 
0116     /** Set up a KARecurrence from recurrence parameters, using the start date to
0117      *  determine the recurrence day/month as appropriate, and specifying how
0118      *  annual 29th February recurrences in non-leap years should be handled.
0119      *  @return true if successful.
0120      */
0121     bool set(Type t, int freq, int count, const KADateTime& start, const KADateTime& end, Feb29Type f29);
0122 
0123     /** Set up a KARecurrence from recurrence parameters.
0124      *  Annual 29th February recurrences in non-leap years will be handled
0125      *  according to the default set by setDefaultFeb29Type().
0126      *  Only a restricted subset of recurrence types is allowed: minutely, daily,
0127      *  weekly, monthly, yearly or none.
0128      *  @return true if successful.
0129      */
0130     bool init(KCalendarCore::RecurrenceRule::PeriodType t, int freq, int count, const KADateTime& start, const KADateTime& end);
0131 
0132     /** Set up a KARecurrence from recurrence parameters, specifying how
0133      *  annual 29th February recurrences in non-leap years should be handled.
0134      *  Only a restricted subset of recurrence types is allowed: minutely, daily,
0135      *  weekly, monthly, yearly or none.
0136      *  @return true if successful.
0137      */
0138     bool init(KCalendarCore::RecurrenceRule::PeriodType t, int freq, int count, const KADateTime& start, const KADateTime& end, Feb29Type f29);
0139 
0140     /** Removes all recurrence and exception rules and dates. */
0141     void clear();
0142 
0143     /** Initialise a KCalendarCore::Recurrence to be the same as this instance.
0144      *  Additional recurrence rules are created as necessary if it recurs on Feb 29th.
0145      */
0146     void writeRecurrence(KCalendarCore::Recurrence&) const;
0147 
0148     /** Convert the recurrence to KARecurrence types.
0149      *  Must be called after presetting with a KCalendarCore::Recurrence.
0150      *  - Convert hourly recurrences to minutely.
0151      *  - Remove all but the first day in yearly date recurrences.
0152      *  - Check for yearly recurrences falling on February 29th and adjust them as
0153      *    necessary. A 29th of the month rule can be combined with either a 60th day
0154      *    of the year rule or a last day of February rule.
0155      */
0156     void fix();
0157 
0158     /** Return the start date/time of the recurrence (Time for all-day recurrences will be 0:00).
0159      * @return the current start/time of the recurrence.
0160      */
0161     KADateTime startDateTime() const;
0162 
0163     /** Return the start date/time of the recurrence */
0164     QDate startDate() const;
0165 
0166     /** Set the recurrence start date/time, and optionally set it to all-day.
0167      *  @param dt        start date/time.
0168      *  @param dateOnly  if true, sets the recurrence to all-day.
0169      */
0170     void setStartDateTime(const KADateTime& dt, bool dateOnly);
0171 
0172     /** Return the date/time of the last recurrence. */
0173     KADateTime endDateTime() const;
0174 
0175     /** Return the date of the last recurrence. */
0176     QDate endDate() const;
0177 
0178     /** Sets the date of the last recurrence. The end time is set to the recurrence start time.
0179      * @param endDate the ending date after which to stop recurring. If the
0180      *   recurrence is not all-day, the end time will be 23:59.
0181      */
0182     void setEndDate(const QDate& endDate);
0183 
0184     /** Sets the date and time of the last recurrence.
0185      * @param endDateTime the ending date/time after which to stop recurring.
0186      */
0187     void setEndDateTime(const KADateTime& endDateTime);
0188 
0189     /** Set whether the recurrence has no time, just a date.
0190      * All-day means -- according to rfc2445 -- that the event has no time
0191      * associated.
0192      * N.B. This property is derived by default from whether setStartDateTime() is
0193      * called with a date-only or date/time parameter.
0194      * @return whether the recurrence has a time (false) or it is just a date (true).
0195      */
0196     bool allDay() const;
0197 
0198     /** Set if recurrence is read-only or can be changed. */
0199     void setRecurReadOnly(bool readOnly);
0200 
0201     /** Returns true if the recurrence is read-only, or false if it can be changed. */
0202     bool recurReadOnly() const;
0203 
0204     /** Returns whether the event recurs at all. */
0205     bool recurs() const;
0206 
0207     /** Returns week day mask (bit 0 = Monday). */
0208     QBitArray days() const; // Emulate the old behavior
0209 
0210     /** Returns list of day positions in months. */
0211     QList<KCalendarCore::RecurrenceRule::WDayPos> monthPositions() const;
0212 
0213     /** Returns list of day numbers of a  month. */
0214     // Emulate old behavior
0215     QList<int> monthDays() const;
0216 
0217     /** Returns the day numbers within a yearly recurrence.
0218      * @return the days of the year for the event. E.g. if the list contains
0219      *         60, this means the recurrence happens on day 60 of the year, i.e.
0220      *         on Feb 29 in leap years and March 1 in non-leap years.
0221      */
0222     QList<int> yearDays() const;
0223 
0224     /** Returns the dates within a yearly recurrence.
0225      * @return the days of the month for the event. E.g. if the list contains
0226      *         13, this means the recurrence happens on the 13th of the month.
0227      *         The months for the recurrence can be obtained through
0228      *         yearlyMonths(). If this list is empty, the month of the start
0229      *         date is used.
0230      */
0231     QList<int> yearDates() const;
0232 
0233     /** Returns the months within a yearly recurrence.
0234      * @return the months for the event. E.g. if the list contains
0235      *         11, this means the recurrence happens in November.
0236      *         The days for the recurrence can be obtained either through
0237      *         yearDates() if they are given as dates within the month or
0238      *         through yearlyPositions() if they are given as positions within the
0239      *         month. If none is specified, the date of the start date is used.
0240      */
0241     QList<int> yearMonths() const;
0242 
0243     /** Returns the positions within a yearly recurrence.
0244      * @return the positions for the event, either within a month (if months
0245      *         are set through addYearlyMonth()) or within the year.
0246      *         E.g. if the list contains {Pos=3, Day=5}, this means the third
0247      *         friday. If a month is set this position is understoodas third
0248      *         Friday in the given months, otherwise as third Friday of the
0249      *         year.
0250      */
0251     QList<KCalendarCore::RecurrenceRule::WDayPos> yearPositions() const;
0252 
0253     /** Adds days to the weekly day recurrence list.
0254      * @param days a 7 bit array indicating which days on which to recur (bit 0 = Monday).
0255      */
0256     void addWeeklyDays(const QBitArray& days);
0257 
0258     /** Adds day number of year within a yearly recurrence.
0259      *  By default infinite recurrence is used. To set an end date use the
0260      *  method setEndDate and to set the number of occurrences use setDuration.
0261      * @param day the day of the year for the event. E.g. if day is 60, this
0262      *            means Feb 29 in leap years and March 1 in non-leap years.
0263      */
0264     void addYearlyDay(int day);
0265 
0266     /** Adds date within a yearly recurrence. The month(s) for the recurrence
0267      *  can be specified with addYearlyMonth(), otherwise the month of the
0268      *  start date is used.
0269      *
0270      *   By default infinite recurrence is used. To set an end date use the
0271      *   method setEndDate and to set the number of occurrences use setDuration.
0272      * @param date the day of the month for the event
0273      */
0274     void addYearlyDate(int date);
0275 
0276     /** Adds month in yearly recurrence. You can specify specific day numbers
0277      *  within the months (by calling addYearlyDate()) or specific day positions
0278      *  within the month (by calling addYearlyPos).
0279      * @param month the month in which the event will recur.
0280      */
0281     void addYearlyMonth(short month);
0282 
0283     /** Adds position within month/year within a yearly recurrence. If months
0284      *  are specified (via addYearlyMonth()), the parameters are understood as
0285      *  position within these months, otherwise within the year.
0286      *
0287      *  By default infinite recurrence is used.
0288      *   To set an end date use the method setEndDate and to set the number
0289      *   of occurrences use setDuration.
0290      * @param pos the position in the month/year for the recurrence, with valid
0291      * values being 1 to 53 and -1 to -53 (53 weeks max in a year).
0292      * @param days the days for the position to recur on (bit 0 = Monday).
0293      * Example: pos = 2, and bits 0 and 2 are set in days
0294      *   If months are specified (via addYearlyMonth), e.g. March, the rule is
0295      *   to repeat every year on the 2nd Monday and Wednesday of March.
0296      *   If no months are specified, the rule is to repeat every year on the
0297      *   2nd Monday and Wednesday of the year.
0298      */
0299     void addYearlyPos(short pos, const QBitArray& days);
0300 
0301     /** Adds a position (e.g. first monday) to the monthly recurrence rule.
0302      * @param pos the position in the month for the recurrence, with valid
0303      * values being 1-5 (5 weeks max in a month).
0304      * @param days the days for the position to recur on (bit 0 = Monday).
0305      * Example: pos = 2, and bits 0 and 2 are set in days:
0306      * the rule is to repeat every 2nd Monday and Wednesday in the month.
0307      */
0308     void addMonthlyPos(short pos, const QBitArray& days);
0309     void addMonthlyPos(short pos, ushort day);
0310 
0311     /** Adds a date (e.g. the 15th of each month) to the monthly day
0312      *  recurrence list.
0313      * @param day the date in the month to recur.
0314      */
0315     void addMonthlyDate(short day);
0316 
0317     /** Get the next time the recurrence occurs, strictly after a specified time. */
0318     KADateTime   getNextDateTime(const KADateTime& preDateTime) const;
0319 
0320     /** Get the previous time the recurrence occurred, strictly before a specified time. */
0321     KADateTime   getPreviousDateTime(const KADateTime& afterDateTime) const;
0322 
0323     /** Return whether the event will recur on the specified date.
0324      *  The start date only returns true if it matches the recurrence rules. */
0325     bool        recursOn(const QDate&, const KADateTime::Spec&) const;
0326 
0327     /**
0328      * Returns true if the date/time specified is one at which the event will
0329      * recur. Times are rounded down to the nearest minute to determine the result.
0330      *
0331      * @param dt is the date/time to check.
0332      */
0333     bool recursAt(const KADateTime& dt) const;
0334 
0335     /** Returns a list of the times on the specified date at which the
0336      * recurrence will occur. The returned times should be interpreted in the
0337      * context of @p timeSpec.
0338      * @param date the date for which to find the recurrence times
0339      * @param timeSpec time specification for @p date
0340      */
0341     KCalendarCore::TimeList recurTimesOn(const QDate& date, const KADateTime::Spec& timeSpec) const;
0342 
0343     /** Returns a list of all the times at which the recurrence will occur
0344      * between two specified times.
0345      *
0346      * There is a (large) maximum limit to the number of times returned. If due to
0347      * this limit the list is incomplete, this is indicated by the last entry being
0348      * set to an invalid KADateTime value. If you need further values, call the
0349      * method again with a start time set to just after the last valid time returned.
0350      *
0351      * @param start inclusive start of interval
0352      * @param end inclusive end of interval
0353      * @return list of date/time values
0354      */
0355     KCalendarCore::DateTimeList timesInInterval(const KADateTime& start, const KADateTime& end) const;
0356 
0357     /** Returns frequency of recurrence, in terms of the recurrence time period type. */
0358     int frequency() const;
0359 
0360     /** Sets the frequency of recurrence, in terms of the recurrence time period type. */
0361     void setFrequency(int freq);
0362 
0363     /**
0364      * Returns -1 if the event recurs infinitely, 0 if the end date is set,
0365      * otherwise the total number of recurrences, including the initial occurrence.
0366      */
0367     int duration() const;
0368 
0369     /** Sets the total number of times the event is to occur, including both the
0370      * first and last.
0371      */
0372     void setDuration(int duration);
0373 
0374     /** Returns the number of recurrences up to and including the date/time specified.
0375      *  @warning This function can be very time consuming - use it sparingly!
0376      */
0377     int durationTo(const KADateTime& dt) const;
0378 
0379     /** Returns the number of recurrences up to and including the date specified.
0380      *  @warning This function can be very time consuming - use it sparingly!
0381      */
0382     int durationTo(const QDate& date) const;
0383 
0384     /** Return the longest interval between recurrences.
0385      *  @return  0 if it never recurs.
0386      */
0387     KCalendarCore::Duration longestInterval() const;
0388 
0389     /** Return the interval between recurrences, if the interval between
0390      *  successive occurrences does not vary.
0391      *  @return  0 if recurrence does not occur at fixed intervals.
0392      */
0393     KCalendarCore::Duration regularInterval() const;
0394     KCalendarCore::DateTimeList exDateTimes() const;
0395     KCalendarCore::DateList exDates() const;
0396     void setExDateTimes(const KCalendarCore::DateTimeList& exdates);
0397     void setExDates(const KCalendarCore::DateList& exdates);
0398     void addExDateTime(const KADateTime& exdate);
0399     void addExDate(const QDate& exdate);
0400 
0401     /**
0402      * Shift the times of the recurrence so that they appear at the same clock
0403      * time as before but in a new time zone. The shift is done from a viewing
0404      * time zone rather than from the actual recurrence time zone.
0405      *
0406      * For example, shifting a recurrence whose start time is 09:00 America/New York,
0407      * using an old viewing time zone (@p oldSpec) of Europe/London, to a new time
0408      * zone (@p newSpec) of Europe/Paris, will result in the time being shifted
0409      * from 14:00 (which is the London time of the recurrence start) to 14:00 Paris
0410      * time.
0411      *
0412      * @param oldSpec the time specification which provides the clock times
0413      * @param newSpec the new time specification
0414      */
0415     void shiftTimes(const QTimeZone& oldSpec, const QTimeZone& newSpec);
0416 
0417     KCalendarCore::RecurrenceRule* defaultRRuleConst() const;
0418     /** Return the recurrence's period type. */
0419     Type type() const;
0420 
0421     /** Return the type of a recurrence rule. */
0422     static Type type(const KCalendarCore::RecurrenceRule*);
0423 
0424     /** Check if the recurrence rule is a daily rule with or without BYDAYS specified. */
0425     static bool dailyType(const KCalendarCore::RecurrenceRule*);
0426 
0427     /** Return when 29th February annual recurrences should occur in non-leap years. */
0428     Feb29Type feb29Type() const;
0429 
0430     /** Return the default way that 29th February annual recurrences should occur
0431      *  in non-leap years.
0432      *  @see setDefaultFeb29Type().
0433      */
0434     static Feb29Type defaultFeb29Type();
0435 
0436     /** Set the default way that 29th February annual recurrences should occur
0437      *  in non-leap years.
0438      *  @see defaultFeb29Type().
0439      */
0440     static void setDefaultFeb29Type(Feb29Type t);
0441 
0442 private:
0443     //@cond PRIVATE
0444     class Private;
0445     Private* const d;
0446     //@endcond
0447 };
0448 
0449 } // namespace KAlarmCal
0450 
0451 // vim: et sw=4: