1713
if (!getNextDirective(temp, p, start))
1705
if (!getNextDirective(temp, p, start)) {
1718
1709
// analyze freq
1719
if (temp == "FREQ=YEARLY")
1710
if (temp == "FREQ=YEARLY") {
1723
else if (temp == "FREQ=MONTHLY")
1713
else if (temp == "FREQ=MONTHLY") {
1727
else if (temp == "FREQ=WEEKLY")
1716
else if (temp == "FREQ=WEEKLY") {
1731
else if (temp == "FREQ=DAILY")
1719
else if (temp == "FREQ=DAILY") {
1740
LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() found frequency %s which maps to freq %c", temp.c_str(), freq));
1726
LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("- found frequency %s which maps to freq %c", temp.c_str(), freq));
1744
// indicator that end date calc is required as last step
1745
calculateEndDate = false;
1746
1731
// set interval to 1
1751
1736
// get next directives
1752
while (getNextDirective(temp, p, start))
1755
LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() found next directive %s", temp.c_str()));
1737
while (getNextDirective(temp, p, start)) {
1738
LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("- found next directive %s", temp.c_str()));
1758
1740
pos = temp.find("=");
1759
if (pos == string::npos || pos == 0 || pos == (temp.length() - 1))
1741
if (pos == string::npos || pos == 0 || pos == (temp.length() - 1)) {
1763
1744
key = temp.substr(0, pos);
1764
1745
value = temp.substr(pos + 1, temp.length() - 1);
1766
LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() extracted key/value %s/%s", key.c_str(), value.c_str()));
1746
LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("- extracted key/value %s/%s", key.c_str(), value.c_str()));
1768
1747
// check for until
1748
if (key == "UNTIL") {
1771
1749
if (ISO8601StrToTimestamp(value.c_str(), until, untilcontext)==0)
1750
goto incompat; // invalid end date
1775
1751
calculateEndDate = false;
1777
1753
// check for count
1778
else if (key == "COUNT")
1754
else if (key == "COUNT") {
1780
1755
// convert count
1781
for (int i = 0, ii = value.length(); i < ii; ++i)
1783
if (isdigit(value[i]))
1756
for (int i = 0, ii = value.length(); i < ii; ++i) {
1757
if (isdigit(value[i])) {
1785
1758
cnt = cnt * 10 + ((value[i]) - '0');
1788
1761
// check if no count or one only
1763
goto norep; // no repetition
1764
// we need to recalculate the enddate from count
1794
1765
calculateEndDate = true;
1796
1767
// check for interval
1797
else if (key == "INTERVAL")
1768
else if (key == "INTERVAL") {
1799
1769
// convert interval
1801
for (int i = 0, ii = value.length(); i < ii; ++i)
1803
if (isdigit(value[i]))
1771
for (int i = 0, ii = value.length(); i < ii; ++i) {
1772
if (isdigit(value[i])) {
1805
1773
interval = interval * 10 + ((value[i]) - '0');
1808
1776
if (interval == 0)
1777
goto incompat; // invalid interval
1813
// just copy all supported byxxx rules into vars
1779
// just copy all supported BYxxx rules into vars
1814
1780
else if (key == "BYDAY")
1818
1782
else if (key == "BYMONTHDAY")
1820
1783
bymonthday = value;
1822
1784
else if (key == "BYMONTH")
1824
1785
bymonth = value;
1826
1786
// ignore week start
1827
else if (key == "WKST")
1787
else if (key == "WKST") {
1791
goto incompat; // unknown directive
1837
LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() dtstart weekday is %i", startwday));
1838
LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() extracted interval %u", interval));
1839
LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() extracted interval (2nd time) %u", interval));
1840
LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() extracted count %u", cnt));
1841
LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() end date calc required? %s", calculateEndDate ? "true" : "false"));
1842
LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() extracted byday %s", byday.c_str()));
1843
LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() extracted bymonthday %s", bymonthday.c_str()));
1844
LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() extracted bymonth %s", bymonth.c_str()));
1795
LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("- dtstart weekday is %i", startwday));
1796
LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("- extracted interval %u", interval));
1797
LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("- extracted interval (2nd time) %u", interval));
1798
LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("- extracted count %u", cnt));
1799
LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("- end date calc required? %s", calculateEndDate ? "true" : "false"));
1800
LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("- extracted byday %s", byday.c_str()));
1801
LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("- extracted bymonthday %s", bymonthday.c_str()));
1802
LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("- extracted bymonth %s", bymonth.c_str()));
1845
1803
TimestampToISO8601Str(abc,until,startcontext,false,false);
1846
LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() extracted until %s", abc.c_str()));
1804
LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("- extracted until %s", abc.c_str()));
1849
if (DayMonthCombi( freq, byday,bymonth,startmonth )) {
1850
freq = 'M'; // a different model will be chosen for this case,
1851
interval= 12*interval; // which is in fact aequivalent
1807
// Generally, we only support one BYxxx, not combinations. However, FREQ=YEARLY + BYMONTH + BYDAY is common
1808
// to express start and end times of DST in VTIMEZONEs, so we convert these into equivalent FREQ=MONTHLY
1809
if (freq=='Y' && !byday.empty() && !bymonth.empty() && bymonth.find(",")==string::npos) {
1810
// yearly by (single!) month and by day
1811
// - get month as indicated by BYMONTH
1812
sInt16 newStartMonth = 0;
1813
if (StrToShort(bymonth.c_str(), newStartMonth)>0 && newStartMonth) {
1814
// - check if BYMONTH is just redundant and references same month as start date
1815
bool convertToMonthly = startmonth>0 && startmonth==newStartMonth;
1816
if (!convertToMonthly && aNewStartP) {
1817
// not redundant, but we can return a new start date
1818
convertToMonthly = true;
1819
// calculate the new start date
1820
startmonth = newStartMonth;
1821
dtstart = date2lineartime(startyear, startmonth, startday) + lineartime2timeonly(dtstart);
1822
LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("- moved recurrence origin to %04hd-%02hd-%02hd",startyear, startmonth, startday));
1823
// pass it back to caller
1824
calculateFirstOccurrence = true; // need to fine tune at the end
1825
*aNewStartP = dtstart;
1827
if (convertToMonthly) {
1828
LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("- converted FREQ=YEARLY BYMONTH into MONTHLY"));
1829
freq = 'M'; // convert from YEARLY to MONTHLY
1830
interval = 12*interval; // but with 12 months interval -> equivalent
1831
bymonth.erase(); // forget BYMONTH clause
1834
} // if FREQ=YEARLY + BYMONTH + BYDAY
1857
1837
startIndex = 0;
1861
1840
// make weekly if byday and nothing else is set
1862
if (!(byday == "") && bymonth == "" && bymonthday == "")
1841
if (!(byday == "") && bymonth == "" && bymonthday == "") {
1866
1844
// search separator ','
1867
while ((endIndex = byday.find(",", startIndex)) != string::npos)
1845
while ((endIndex = byday.find(",", startIndex)) != string::npos) {
1869
1846
// set masks for the specific day
1870
1847
if (!setWeekday(byday, firstmask, lastmask, startIndex, endIndex, false))
1874
1849
// set new start
1875
1850
startIndex = endIndex + 1;
1876
1851
if (startIndex >= byday.length())
1881
1854
// check if anything is behind endindex
1882
if (endIndex == string::npos && startIndex < byday.length())
1855
if (endIndex == string::npos && startIndex < byday.length()) {
1884
1856
endIndex = byday.length();
1885
1857
if (!setWeekday(byday, firstmask, lastmask, startIndex, endIndex, false))
1893
1862
// we don't support byday and anything else at the same time
1894
1863
if (!byday.empty())
1903
1870
// we don't support month or monthday within weekly
1904
1871
if (!bymonth.empty() || !bymonthday.empty())
1908
1873
// set weekly and days
1875
if (!byday.empty()) {
1912
1876
// search separator ','
1913
while ((endIndex = byday.find(",", startIndex)) != string::npos)
1877
while ((endIndex = byday.find(",", startIndex)) != string::npos) {
1915
1878
// set masks for the specific day
1916
1879
if (!setWeekday(byday, firstmask, lastmask, startIndex, endIndex, false))
1920
1881
// set new start
1921
1882
startIndex = endIndex + 1;
1922
1883
if (startIndex >= byday.length())
1927
1886
// check if anything is behind endindex
1928
if (endIndex == string::npos && startIndex < byday.length())
1887
if (endIndex == string::npos && startIndex < byday.length()) {
1930
1888
endIndex = byday.length();
1931
1889
if (!setWeekday(byday, firstmask, lastmask, startIndex, endIndex, false))
1939
1895
// we don't support by month in monthly
1940
1896
if (!bymonth.empty())
1944
1898
// we don't support byday and bymonthday at the same time
1945
1899
if (!byday.empty() && !bymonthday.empty())
1949
1901
// check if bymonthday
1950
if (!bymonthday.empty())
1902
if (!bymonthday.empty()) {
1953
1904
// search separator ','
1954
while ((endIndex = bymonthday.find(",", startIndex)) != string::npos)
1905
while ((endIndex = bymonthday.find(",", startIndex)) != string::npos) {
1956
1906
// set masks for the specific day
1957
1907
if (!setMonthDay(bymonthday, firstmask, lastmask, startIndex, endIndex))
1961
1909
// set new start
1962
1910
startIndex = endIndex + 1;
1963
1911
if (startIndex >= bymonthday.length())
1968
1914
// check if anything is behind endindex
1969
if (endIndex == string::npos && startIndex < bymonthday.length())
1915
if (endIndex == string::npos && startIndex < bymonthday.length()) {
1971
1916
endIndex = bymonthday.length();
1972
1917
if (!setMonthDay(bymonthday, firstmask, lastmask, startIndex, endIndex))
1978
1921
// or if by weekday
1979
else if (!byday.empty())
1922
else if (!byday.empty()) {
1982
1924
// search separator ','
1983
while ((endIndex = byday.find(",", startIndex)) != string::npos)
1925
while ((endIndex = byday.find(",", startIndex)) != string::npos) {
1985
1926
// set masks for the specific day
1986
1927
if (!setWeekday(byday, firstmask, lastmask, startIndex, endIndex, true))
1990
1929
// set new start
1991
1930
startIndex = endIndex + 1;
1992
1931
if (startIndex >= byday.length())
1997
1934
// check if anything is behind endindex
1998
if (endIndex == string::npos && startIndex < byday.length())
1935
if (endIndex == string::npos && startIndex < byday.length()) {
2000
1936
endIndex = byday.length();
2001
1937
if (!setWeekday(byday, firstmask, lastmask, startIndex, endIndex, true))
2009
1942
// fine, no mod
2015
(byday.length() == 2 && byday[0] == RRULE_weekdays[startwday][0] &&
2016
byday[1] == RRULE_weekdays[startwday][1]))
1949
(byday.length() == 2 && byday[0] == RRULE_weekdays[startwday][0] &&
1950
byday[1] == RRULE_weekdays[startwday][1])
2019
1953
sprintf(s, "%hd", startday);
2020
1954
temp.append(s);
2022
LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() bymonthday checking %s against %s", temp.c_str(), bymonthday.c_str()));
2024
if (bymonthday == "" || bymonthday == temp)
1955
LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("- bymonthday checking %s against %s", temp.c_str(), bymonthday.c_str()));
1956
if (bymonthday == "" || bymonthday == temp) {
2027
1958
sprintf(s, "%hd", startmonth);
2028
1959
temp.append(s);
2030
LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() bymonth checking %s against %s", temp.c_str(), bymonth.c_str()));
2032
if (bymonth == "" || bymonth == temp)
1960
LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("- bymonth checking %s against %s", temp.c_str(), bymonth.c_str()));
1961
if (bymonth == "" || bymonth == temp) {
2034
1962
// this should usually be ' ' but the vcard conversion has a bug and requires 'M'
2055
LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() strange self-set freq %c, rule was %s", freq, aText));
1977
LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("- strange self-set freq %c, rule was %s", freq, aText));
2059
1981
// calc end date, assumption/make sure: cnt > 1
2060
if (calculateEndDate)
2062
LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() calculating end date now"));
1982
if (calculateEndDate) {
1983
LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("- calculating end date now"));
2063
1984
if (!endDateFromCount(until,dtstart,freq,freqmod,interval,firstmask,lastmask,cnt,true,aLogP))
2065
1986
untilcontext = startcontext; // until is in same context as start
1988
// calculate exact date of first occurrence
1989
if (calculateFirstOccurrence && aNewStartP) {
1990
TRRuleExpandStatus es;
1991
initRRuleExpansion(es, dtstart, freq, freqmod, interval, firstmask, lastmask, dtstart, noLinearTime);
1992
dtstart = getNextOccurrence(es);
1993
if (LOGDEBUGTEST(aLogP,DBG_PARSE+DBG_EXOTIC)) {
1995
lineartime2date(dtstart,&ny,&nm,&nd);
1996
LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("- adjusted first occurrence date to %04hd-%02hd-%02hd",ny, nm, nd));
1998
*aNewStartP = dtstart;
2067
2000
// parsed ok, now store it