~ubuntu-branches/ubuntu/vivid/libsynthesis/vivid-201501071725

« back to all changes in this revision

Viewing changes to src/sysync/rrules.cpp

  • Committer: Bazaar Package Importer
  • Author(s): David Bremner
  • Date: 2010-11-20 19:50:44 UTC
  • mfrom: (3.1.3 experimental)
  • Revision ID: james.westby@ubuntu.com-20101120195044-8s6wxqb2d0nmqdv3
Tags: 3.4.0.6+ds5-2
* Upload to unstable.
* Add missing commit from upstream 3.4.0.6+ds5

Show diffs side-by-side

added added

removed removed

Lines of Context:
482
482
  TDebugLogger *aLogP
483
483
)
484
484
{
485
 
  LOGDEBUGPRINTFX(aLogP,DBG_GEN,("InternalToRRULE2() need to analyze freq %c and freqmod %c", freq, freqmod));
 
485
  LOGDEBUGPRINTFX(aLogP,DBG_EXOTIC+DBG_GEN,(
 
486
        "InternalToRRULE2(): expanding freq=%c, freqmod=%c, interval=%hd, firstmask=%llX, lastmask=%llX",
 
487
    freq, freqmod, interval, (long long)firstmask, (long long)lastmask
 
488
  ));
486
489
 
487
490
  // Now do the conversion
488
491
  string s;
829
832
} // RRULE1toInternal
830
833
 
831
834
 
832
 
// Check if a combined BYDAY and BYMONTH, where BYMONTH is one entry and equal to <startmonth>
833
 
static bool DayMonthCombi( char freq, string byday,
834
 
                                      string bymonth, sInt16 startmonth )
835
 
{
836
 
  return freq=='Y'    &&                      // it is yearly,
837
 
         startmonth>0 &&                      // start month of <dtstart> available ...
838
 
         startmonth==atoi(bymonth.c_str()) && // ... and aequivalent to bymonth item
839
 
         !byday.empty();                      // and byday item available as well
840
 
} // DayMonthCombi
841
 
 
842
 
 
843
 
 
844
 
 
845
835
/// @brief calculate end date of RRULE when count is specified
846
836
/// @return true if repeating, false if not repeating at all
847
837
/// @note returns until=noLinearTime for endless repeat (count=0)
878
868
  // check if daily
879
869
  if (freq == 'D') {
880
870
        // Daily recurrence is same for occurrence and interval counts
881
 
    LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() daily calc - same in all cases"));
 
871
    LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("endDateFromCount: daily calc - same in all cases"));
882
872
    until = dtstart+(ivrep*linearDateToTimeFactor);
883
873
    return true;
884
874
  }
888
878
    switch (freq)
889
879
    {
890
880
      case 'W':
891
 
        LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() simple weekly calc"));
 
881
        LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("endDateFromCount: simple weekly calc"));
892
882
        // v1 end date calc, we need to take into account possible masks, so result must be end of interval, not just start date+interval
893
883
        // weekly: end date is last day of target week
894
884
        until=dtstart+((ivrep*DaysOfWeek-startwday+6)*linearDateToTimeFactor);
895
885
        return true;
896
886
      case 'M':
897
 
        LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() simple monthly calc"));
 
887
        LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("endDateFromCount: simple monthly calc"));
898
888
        startmonth--; // make 0 based
899
889
        startmonth += ivrep+1; // add number of months plus one (as we want next month, and then go one day back to last day of month)
900
890
        startyear += startmonth / 12; // update years
903
893
        until = (date2lineardate(startyear,startmonth,1)-1)*linearDateToTimeFactor+starttime;
904
894
        return true;
905
895
      case 'Y':
906
 
        LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() simple yearly calc"));
 
896
        LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("endDateFromCount: simple yearly calc"));
907
897
        // yearly: end date is end of end year
908
898
        until = (date2lineardate(startyear+ivrep,12,31))*linearDateToTimeFactor+starttime;
909
899
        return true;
919
909
    switch (freq)
920
910
    {
921
911
      case 'W':
922
 
        LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() full expansion weekly calc"));
 
912
        LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("endDateFromCount: full expansion weekly calc"));
923
913
        // - make sure we have a mask
924
914
        if (firstmask==0 && lastmask==0)
925
915
                firstmask = 1<<startwday; // set start day in mask
944
934
      case 'M':
945
935
        if (freqmod=='W') {
946
936
          // monthly by weekday
947
 
          LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() full expansion of monthly by weekday"));
 
937
          LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("endDateFromCount: full expansion of monthly by weekday"));
948
938
          // - make sure we have a mask
949
939
          if (firstmask==0 && lastmask==0)
950
940
                                                firstmask = (uInt64)1<<(startwday+7*((startday-1)/7)); // set start day in mask
983
973
        }
984
974
        else {
985
975
          // everything else, including no modifier, is treated as monthly by monthday
986
 
          LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() full expansion of monthly by monthday"));
 
976
          LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("endDateFromCount: full expansion of monthly by monthday"));
987
977
          // - make sure we have a mask
988
978
          if (firstmask==0 && lastmask==0)
989
979
                                                firstmask = (uInt64)1<<(startday-1); // set start day in mask
1017
1007
      case 'Y':
1018
1008
        if (freqmod=='M') {
1019
1009
                // Yearly by month
1020
 
          LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() full expansion of yearly by month"));
 
1010
          LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("endDateFromCount: full expansion of yearly by month"));
1021
1011
          // - make sure we have a mask
1022
1012
          if (firstmask==0 && lastmask==0)
1023
1013
                                                firstmask = (uInt64)1<<(startmonth-1); // set start month in mask
1058
1048
        }
1059
1049
        else {
1060
1050
          // everything else, including no modifier, is treated as yearly on the same date (multiple occurrences per year not supported)
1061
 
          LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() full expansion of yearly by yearday - NOT SUPPORTED with more than one day"));
 
1051
          LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("endDateFromCount: full expansion of yearly by yearday - NOT SUPPORTED with more than one day"));
1062
1052
                until = (date2lineardate(startyear+ivrep,startmonth,startday))*linearDateToTimeFactor+starttime;
1063
1053
        }
1064
1054
        return true;
1680
1670
  fieldinteger_t &lastmask,
1681
1671
  lineartime_t &until,
1682
1672
  timecontext_t &untilcontext,
1683
 
  TDebugLogger *aLogP
 
1673
  TDebugLogger *aLogP,
 
1674
  lineartime_t *aNewStartP
1684
1675
)
1685
1676
{
1686
1677
  #ifdef SYDEBUG
1687
1678
  string abc;
1688
 
  LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() need to analyze %s", aText));
 
1679
  LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal(): start analyzing '%s'", aText));
1689
1680
  #endif
1690
1681
 
1691
1682
  string temp;
1698
1689
  string::size_type startIndex = 0;
1699
1690
  uInt16 cnt = 0;
1700
1691
  size_t pos;
1701
 
  bool calculateEndDate = false;
 
1692
  bool calculateEndDate = false; // indicator that end date calc is required as last step
 
1693
  bool calculateFirstOccurrence = false; // indicator that aNewStartP should be adjusted to show the first occurrence
1702
1694
  string key,value,byday,bymonthday,bymonth;
1703
1695
 
1704
1696
  // get elements of start point
1710
1702
  p=aText;
1711
1703
  int start = 0;
1712
1704
  // get freq
1713
 
  if (!getNextDirective(temp, p, start))
1714
 
  {
 
1705
  if (!getNextDirective(temp, p, start)) {
1715
1706
    // failed
1716
1707
    goto incompat;
1717
1708
  }
1718
1709
  // analyze freq
1719
 
  if (temp == "FREQ=YEARLY")
1720
 
  {
 
1710
  if (temp == "FREQ=YEARLY") {
1721
1711
    freq='Y';
1722
1712
  }
1723
 
  else if (temp == "FREQ=MONTHLY")
1724
 
  {
 
1713
  else if (temp == "FREQ=MONTHLY") {
1725
1714
    freq='M';
1726
1715
  }
1727
 
  else if (temp == "FREQ=WEEKLY")
1728
 
  {
 
1716
  else if (temp == "FREQ=WEEKLY") {
1729
1717
    freq='W';
1730
1718
  }
1731
 
  else if (temp == "FREQ=DAILY")
1732
 
  {
 
1719
  else if (temp == "FREQ=DAILY") {
1733
1720
    freq='D';
1734
1721
  }
1735
 
  else
1736
 
  {
 
1722
  else {
1737
1723
    goto incompat;
1738
1724
  }
1739
1725
 
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));
1741
1727
 
1742
1728
  // init vars
1743
1729
  cnt = 0;
1744
 
  // indicator that end date calc is required as last step
1745
 
  calculateEndDate = false;
 
1730
  
1746
1731
  // set interval to 1
1747
1732
  interval = 1;
1748
1733
  freqmod = ' ';
1749
1734
  firstmask = 0;
1750
1735
  lastmask = 0;
1751
1736
  // get next directives
1752
 
  while (getNextDirective(temp, p, start))
1753
 
  {
1754
 
 
1755
 
    LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() found next directive %s", temp.c_str()));
1756
 
 
 
1737
  while (getNextDirective(temp, p, start)) {
 
1738
    LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("- found next directive %s", temp.c_str()));
1757
1739
    // split
1758
1740
    pos = temp.find("=");
1759
 
    if (pos == string::npos || pos == 0 || pos == (temp.length() - 1))
1760
 
    {
 
1741
    if (pos == string::npos || pos == 0 || pos == (temp.length() - 1)) {
1761
1742
      goto incompat;
1762
1743
    }
1763
1744
    key = temp.substr(0, pos);
1764
1745
    value = temp.substr(pos + 1, temp.length() - 1);
1765
 
 
1766
 
    LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() extracted key/value %s/%s", key.c_str(), value.c_str()));
1767
 
 
 
1746
    LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("- extracted key/value %s/%s", key.c_str(), value.c_str()));
1768
1747
    // check for until
1769
 
    if (key == "UNTIL")
1770
 
    {
 
1748
    if (key == "UNTIL") {
1771
1749
      if (ISO8601StrToTimestamp(value.c_str(), until, untilcontext)==0)
1772
 
      {
1773
 
        goto incompat;
1774
 
      }
 
1750
        goto incompat; // invalid end date
1775
1751
      calculateEndDate = false;
1776
1752
    }
1777
1753
    // check for count
1778
 
    else if (key == "COUNT")
1779
 
    {
 
1754
    else if (key == "COUNT") {
1780
1755
      // convert count
1781
 
      for (int i = 0, ii = value.length(); i < ii; ++i)
1782
 
      {
1783
 
        if (isdigit(value[i]))
1784
 
        {
 
1756
      for (int i = 0, ii = value.length(); i < ii; ++i) {
 
1757
        if (isdigit(value[i])) {
1785
1758
          cnt = cnt * 10 + ((value[i]) - '0');
1786
1759
        }
1787
1760
      }
1788
1761
      // check if no count or one only
1789
1762
      if (cnt <= 1)
1790
 
      {
1791
 
        goto norep;
1792
 
      }
1793
 
      // recalc enddate
 
1763
        goto norep; // no repetition
 
1764
      // we need to recalculate the enddate from count
1794
1765
      calculateEndDate = true;
1795
1766
    }
1796
1767
    // check for interval
1797
 
    else if (key == "INTERVAL")
1798
 
    {
 
1768
    else if (key == "INTERVAL") {
1799
1769
      // convert interval
1800
1770
      interval = 0;
1801
 
      for (int i = 0, ii = value.length(); i < ii; ++i)
1802
 
      {
1803
 
        if (isdigit(value[i]))
1804
 
        {
 
1771
      for (int i = 0, ii = value.length(); i < ii; ++i) {
 
1772
        if (isdigit(value[i])) {
1805
1773
          interval = interval * 10 + ((value[i]) - '0');
1806
1774
        }
1807
1775
      }
1808
1776
      if (interval == 0)
1809
 
      {
1810
 
        goto incompat;
1811
 
      }
 
1777
        goto incompat; // invalid interval
1812
1778
    }
1813
 
    // just copy all supported byxxx rules into vars
 
1779
    // just copy all supported BYxxx rules into vars
1814
1780
    else if (key == "BYDAY")
1815
 
    {
1816
1781
      byday = value;
1817
 
    }
1818
1782
    else if (key == "BYMONTHDAY")
1819
 
    {
1820
1783
      bymonthday = value;
1821
 
    }
1822
1784
    else if (key == "BYMONTH")
1823
 
    {
1824
1785
      bymonth = value;
1825
 
    }
1826
1786
    // ignore week start
1827
 
    else if (key == "WKST")
1828
 
    {
 
1787
    else if (key == "WKST") {
 
1788
        // nop
1829
1789
    }
1830
1790
    else
1831
 
    {
1832
 
      goto incompat;
1833
 
    }
 
1791
      goto incompat; // unknown directive
1834
1792
  } // while
1835
1793
 
1836
1794
  #ifdef SYDEBUG
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()));
1847
1805
  #endif
1848
1806
 
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
1852
 
    bymonth = "";
1853
 
  } // if
1854
 
 
 
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;
 
1826
      }
 
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
 
1832
      }
 
1833
    }
 
1834
        } // if FREQ=YEARLY + BYMONTH + BYDAY
1855
1835
  // check freq
1856
1836
  endIndex = 0;
1857
1837
  startIndex = 0;
1858
 
  switch (freq)
1859
 
  {
 
1838
  switch (freq) {
1860
1839
    case 'D' :
1861
1840
      // make weekly if byday and nothing else is set
1862
 
      if (!(byday == "") && bymonth == "" && bymonthday == "")
1863
 
      {
 
1841
      if (!(byday == "") && bymonth == "" && bymonthday == "") {
1864
1842
        freq='W';
1865
1843
        freqmod='W';
1866
1844
        // search separator ','
1867
 
        while ((endIndex = byday.find(",", startIndex)) != string::npos)
1868
 
        {
 
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))
1871
 
          {
1872
1848
            goto incompat;
1873
 
          }
1874
1849
          // set new start
1875
1850
          startIndex = endIndex + 1;
1876
1851
          if (startIndex >= byday.length())
1877
 
          {
1878
1852
            break;
1879
 
          }
1880
1853
        }
1881
1854
        // check if anything is behind endindex
1882
 
        if (endIndex == string::npos && startIndex < byday.length())
1883
 
        {
 
1855
        if (endIndex == string::npos && startIndex < byday.length()) {
1884
1856
          endIndex = byday.length();
1885
1857
          if (!setWeekday(byday, firstmask, lastmask, startIndex, endIndex, false))
1886
 
          {
1887
1858
            goto incompat;
1888
 
          }
1889
1859
        }
1890
1860
      }
1891
 
      else
1892
 
      {
 
1861
      else {
1893
1862
        // we don't support byday and anything else at the same time
1894
1863
        if (!byday.empty())
1895
 
        {
1896
1864
          goto incompat;
1897
 
        }
1898
1865
        // ok, no mod
1899
1866
        freqmod = ' ';
1900
1867
      }
1902
1869
    case 'W' :
1903
1870
      // we don't support month or monthday within weekly
1904
1871
      if (!bymonth.empty() || !bymonthday.empty())
1905
 
      {
1906
1872
        goto incompat;
1907
 
      }
1908
1873
      // set weekly and days
1909
1874
      freqmod='W';
1910
 
      if (!byday.empty())
1911
 
      {
 
1875
      if (!byday.empty()) {
1912
1876
        // search separator ','
1913
 
        while ((endIndex = byday.find(",", startIndex)) != string::npos)
1914
 
        {
 
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))
1917
 
          {
1918
1880
            goto incompat;
1919
 
          }
1920
1881
          // set new start
1921
1882
          startIndex = endIndex + 1;
1922
1883
          if (startIndex >= byday.length())
1923
 
          {
1924
1884
            break;
1925
 
          }
1926
1885
        }
1927
1886
        // check if anything is behind endindex
1928
 
        if (endIndex == string::npos && startIndex < byday.length())
1929
 
        {
 
1887
        if (endIndex == string::npos && startIndex < byday.length()) {
1930
1888
          endIndex = byday.length();
1931
1889
          if (!setWeekday(byday, firstmask, lastmask, startIndex, endIndex, false))
1932
 
          {
1933
1890
            goto incompat;
1934
 
          }
1935
1891
        }
1936
1892
      }
1937
1893
      break;
1938
1894
    case 'M' :
1939
1895
      // we don't support by month in monthly
1940
1896
      if (!bymonth.empty())
1941
 
      {
1942
1897
        goto incompat;
1943
 
      }
1944
1898
      // we don't support byday and bymonthday at the same time
1945
1899
      if (!byday.empty() && !bymonthday.empty())
1946
 
      {
1947
1900
        goto incompat;
1948
 
      }
1949
1901
      // check if bymonthday
1950
 
      if (!bymonthday.empty())
1951
 
      {
 
1902
      if (!bymonthday.empty()) {
1952
1903
        freqmod = 'D';
1953
1904
        // search separator ','
1954
 
        while ((endIndex = bymonthday.find(",", startIndex)) != string::npos)
1955
 
        {
 
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))
1958
 
          {
1959
1908
            goto incompat;
1960
 
          }
1961
1909
          // set new start
1962
1910
          startIndex = endIndex + 1;
1963
1911
          if (startIndex >= bymonthday.length())
1964
 
          {
1965
1912
            break;
1966
 
          }
1967
1913
        }
1968
1914
        // check if anything is behind endindex
1969
 
        if (endIndex == string::npos && startIndex < bymonthday.length())
1970
 
        {
 
1915
        if (endIndex == string::npos && startIndex < bymonthday.length()) {
1971
1916
          endIndex = bymonthday.length();
1972
1917
          if (!setMonthDay(bymonthday, firstmask, lastmask, startIndex, endIndex))
1973
 
          {
1974
1918
            goto incompat;
1975
 
          }
1976
1919
        }
1977
1920
      }
1978
1921
      // or if by weekday
1979
 
      else if (!byday.empty())
1980
 
      {
 
1922
      else if (!byday.empty()) {
1981
1923
        freqmod = 'W';
1982
1924
        // search separator ','
1983
 
        while ((endIndex = byday.find(",", startIndex)) != string::npos)
1984
 
        {
 
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))
1987
 
          {
1988
1928
            goto incompat;
1989
 
          }
1990
1929
          // set new start
1991
1930
          startIndex = endIndex + 1;
1992
1931
          if (startIndex >= byday.length())
1993
 
          {
1994
1932
            break;
1995
 
          }
1996
1933
        }
1997
1934
        // check if anything is behind endindex
1998
 
        if (endIndex == string::npos && startIndex < byday.length())
1999
 
        {
 
1935
        if (endIndex == string::npos && startIndex < byday.length()) {
2000
1936
          endIndex = byday.length();
2001
1937
          if (!setWeekday(byday, firstmask, lastmask, startIndex, endIndex, true))
2002
 
          {
2003
1938
            goto incompat;
2004
 
          }
2005
1939
        }
2006
1940
      }
2007
 
      else
2008
 
      {
 
1941
      else {
2009
1942
        // fine, no mod
2010
1943
        freqmod = ' ';
2011
1944
      }
2012
1945
      break;
2013
1946
    case 'Y' :
2014
 
      if (byday == "" ||
2015
 
         (byday.length() == 2 && byday[0] == RRULE_weekdays[startwday][0] &&
2016
 
                                 byday[1] == RRULE_weekdays[startwday][1]))
2017
 
      {
 
1947
      if (
 
1948
        byday == "" ||
 
1949
        (byday.length() == 2 && byday[0] == RRULE_weekdays[startwday][0] &&
 
1950
        byday[1] == RRULE_weekdays[startwday][1])
 
1951
      ) {
2018
1952
        temp.erase();
2019
1953
        sprintf(s, "%hd", startday);
2020
1954
        temp.append(s);
2021
 
 
2022
 
        LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() bymonthday checking %s against %s", temp.c_str(), bymonthday.c_str()));
2023
 
 
2024
 
        if (bymonthday == "" || bymonthday == temp)
2025
 
        {
 
1955
        LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("- bymonthday checking %s against %s", temp.c_str(), bymonthday.c_str()));
 
1956
        if (bymonthday == "" || bymonthday == temp) {
2026
1957
          temp.erase();
2027
1958
          sprintf(s, "%hd", startmonth);
2028
1959
          temp.append(s);
2029
 
 
2030
 
          LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() bymonth checking %s against %s", temp.c_str(), bymonth.c_str()));
2031
 
 
2032
 
          if (bymonth == "" || bymonth == temp)
2033
 
          {
 
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'
2035
1963
            freqmod = 'M';
2036
1964
            lastmask = 0;
2037
1965
            firstmask = 0;
2038
1966
          }
2039
1967
          else
2040
 
          {
2041
1968
            goto incompat;
2042
 
          }
2043
1969
        }
2044
1970
        else
2045
 
        {
2046
1971
          goto incompat;
2047
 
        }
2048
1972
      }
2049
1973
      else
2050
 
      {
2051
1974
        goto incompat;
2052
 
      }
2053
1975
      break;
2054
1976
    default :
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));
2056
1978
      break;
2057
1979
  } // switch
2058
1980
 
2059
1981
  // calc end date, assumption/make sure: cnt > 1
2060
 
  if (calculateEndDate)
2061
 
  {
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))
2064
1985
      goto norep;
2065
1986
    untilcontext = startcontext; // until is in same context as start
2066
1987
  }
 
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)) {
 
1994
        sInt16 ny,nm,nd;
 
1995
                  lineartime2date(dtstart,&ny,&nm,&nd);
 
1996
                  LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("- adjusted first occurrence date to %04hd-%02hd-%02hd",ny, nm, nd));
 
1997
    }
 
1998
    *aNewStartP = dtstart;
 
1999
  }
2067
2000
  // parsed ok, now store it
2068
2001
  goto store;
2069
2002
norep:
2077
2010
store:
2078
2011
 
2079
2012
  #ifdef SYDEBUG
2080
 
  LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() leaving with freq %c, freqmod %c, interval %hd, firstmask %ld, lastmask %ld", freq, freqmod, interval, (long)firstmask, (long)lastmask));
 
2013
  LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("- leaving with freq %c, freqmod %c, interval %hd, firstmask %ld, lastmask %ld", freq, freqmod, interval, (long)firstmask, (long)lastmask));
2081
2014
  TimestampToISO8601Str(abc,until,untilcontext,false,false);
2082
 
  LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal() extracted until %s", abc.c_str()));
 
2015
  LOGDEBUGPRINTFX(aLogP,DBG_PARSE+DBG_EXOTIC,("RRULE2toInternal(): extracted until '%s'", abc.c_str()));
2083
2016
  #endif
2084
2017
 
2085
2018
  return true; // ok
2125
2058
)
2126
2059
{
2127
2060
  // check
2128
 
  if (*aText == 0)
2129
 
  {
 
2061
  if (*aText == 0) {
2130
2062
    return false;
2131
2063
  }
2132
 
 
 
2064
  
2133
2065
  char c;
2134
2066
  // check start
2135
 
  if (aStart > 0)
2136
 
  {
 
2067
  if (aStart > 0) {
2137
2068
    // skip to start
2138
2069
    for (int i = 0; (i < aStart) && ((c = *aText) != 0); ++i) aText++;
2139
2070
  }
2140
 
  else
2141
 
  {
 
2071
  else {
2142
2072
    // skip all spaces
2143
 
    while ((c = *aText) == ' ')
2144
 
    {
 
2073
    while ((c = *aText) == ' ') {
2145
2074
       aText++;
2146
2075
    }
2147
2076
  }
2148
 
 
2149
2077
  // erase string, set counter
2150
2078
  aString.erase();
2151
2079
  uInt16 counter = 0;
2152
 
 
2153
2080
  // append text
2154
2081
  c = *aText++; aStart++;
2155
 
  while (c != 0 && c != ';')
2156
 
  {
 
2082
  while (c != 0 && c != ';') {
2157
2083
    aString.append(1, c);
2158
2084
    ++counter;
2159
2085
    c = *aText++; aStart++;
2160
2086
  }
2161
 
 
2162
2087
  // empty?
2163
2088
  if (counter == 0)
2164
 
  {
2165
2089
    return false;
2166
 
  }
2167
 
 
2168
2090
  // remove trailing whitespace
2169
2091
  sInt16 i = counter - 1;
2170
 
  while (i >= 0 && aString[i] == ' ')
2171
 
  {
 
2092
  while (i >= 0 && aString[i] == ' ') {
2172
2093
    aString.erase(i, 1);
2173
2094
    --i;
2174
2095
  }
2175
 
 
2176
2096
  // check length
2177
2097
  if (i == -1)
2178
 
  {
2179
2098
    return false;
2180
 
  }
2181
 
 
2182
2099
  // fine
2183
2100
  return true;
2184
2101
} // getNextDirective
2185
2102
 
 
2103
 
 
2104
 
2186
2105
// maps the byday rule into the masks
2187
2106
bool setWeekday(
2188
2107
  const string &byday,
2194
2113
)
2195
2114
{
2196
2115
  // remove leading spaces
2197
 
  while (byday[startIndex] == ' ' && startIndex < endIndex)
2198
 
  {
 
2116
  while (byday[startIndex] == ' ' && startIndex < endIndex) {
2199
2117
    ++startIndex;
2200
2118
  }
2201
2119
  // check if digit or sign
2202
 
  if (isdigit(byday[startIndex]) || byday[startIndex] == '+' || byday[startIndex] == '-')
2203
 
  {
 
2120
  if (isdigit(byday[startIndex]) || byday[startIndex] == '+' || byday[startIndex] == '-') {
2204
2121
    // check if numbers before week days are allowed
2205
 
    if (!allowSpecial)
2206
 
    {
 
2122
    if (!allowSpecial) {
2207
2123
      return false;
2208
2124
    }
2209
2125
    // special treatment
2210
2126
    return setSpecialWeekday(byday, firstmask, lastmask, startIndex, endIndex);
2211
2127
  }
2212
 
 
2213
2128
  // get index of weekday array
2214
2129
  sInt16 weekdayIndex = getWeekdayIndex(byday, startIndex);
2215
 
  if (weekdayIndex == -1)
2216
 
  {
 
2130
  if (weekdayIndex == -1) {
2217
2131
    return false;
2218
2132
  }
2219
2133
  // put into mask
2220
 
 
2221
2134
  if (!allowSpecial) {
2222
2135
    firstmask |= ((uInt64)1<<weekdayIndex);
2223
2136
    return true;
2224
2137
  }
2225
 
 
2226
2138
  for (int i= 0; i<WeeksOfMonth; i++) { // do it for all weeks of the month
2227
2139
    firstmask |= ((uInt64)1<<weekdayIndex);
2228
2140
    weekdayIndex+= DaysOfWeek;
2229
2141
  } // for
2230
 
 
2231
2142
  return true;
2232
2143
} // setWeekday
2233
2144
 
 
2145
 
 
2146
 
2234
2147
// maps a special weekday (+/- int WEEKDAY) into the masks
2235
2148
bool setSpecialWeekday(
2236
2149
  const string &byday,
2246
2159
  sInt16 val = 0;
2247
2160
 
2248
2161
  // check sign
2249
 
  if (byday[startIndex] == '-')
2250
 
  {
 
2162
  if (byday[startIndex] == '-') {
2251
2163
    isNegative = true;
2252
2164
    ++startIndex;
2253
2165
  }
2254
 
  else if (byday[startIndex] == '+')
2255
 
  {
 
2166
  else if (byday[startIndex] == '+') {
2256
2167
    isNegative = false;
2257
2168
    ++startIndex;
2258
2169
  }
2259
 
  else
2260
 
  {
 
2170
  else {
2261
2171
    isNegative = false;
2262
2172
  }
2263
2173
  // convert to number
2264
 
  while (isdigit(byday[startIndex]))
2265
 
  {
 
2174
  while (isdigit(byday[startIndex])) {
2266
2175
    val = val * 10 + ((byday[startIndex++]) - '0');
2267
2176
  }
2268
2177
  // remove leading spaces
2269
 
  while (byday[startIndex] == ' ' && startIndex < endIndex)
2270
 
  {
 
2178
  while (byday[startIndex] == ' ' && startIndex < endIndex) {
2271
2179
    ++startIndex;
2272
2180
  }
2273
 
 
2274
2181
  // make sure there's enough space
2275
 
  if (startIndex >= endIndex - 1)
2276
 
  {
 
2182
  if (startIndex >= endIndex - 1) {
2277
2183
    return false;
2278
2184
  }
2279
2185
  // get index for weekday
2280
2186
  sInt16 index = getWeekdayIndex(byday, startIndex);
2281
 
  if (index == -1)
2282
 
  {
 
2187
  if (index == -1) {
2283
2188
    return false;
2284
2189
  }
2285
2190
  // put into mask
2286
 
  if (isNegative)
2287
 
  {
 
2191
  if (isNegative) {
2288
2192
    lastmask |= (((uInt64)1<<index)<<((val - 1) * DaysOfWeek));
2289
2193
  }
2290
 
  else
2291
 
  {
 
2194
  else {
2292
2195
    firstmask |= (((uInt64)1<<index)<<((val - 1) * DaysOfWeek));
2293
2196
  }
2294
 
 
2295
2197
  return true;
2296
2198
} // setSpecialWeekday
2297
2199
 
 
2200
 
2298
2201
// returns the index within the rrule weekday array for the next
2299
2202
// two chars of the supplied string starting at startIndex
2300
2203
sInt16 getWeekdayIndex(
2306
2209
  for (sInt16 i = 0; i < DaysOfWeek; ++i)
2307
2210
  {
2308
2211
    if (RRULE_weekdays[i][0] == byday[startIndex] && RRULE_weekdays[i][1] == byday[startIndex + 1])
2309
 
    {
2310
2212
      return i;
2311
 
    }
2312
2213
  }
2313
2214
  // not found
2314
2215
  return -1;
2315
2216
} // getWeekdayIndex
2316
2217
 
 
2218
 
2317
2219
// set a day in month
2318
2220
bool setMonthDay(
2319
2221
  const string &bymonthday,
2329
2231
  sInt16 val=0;
2330
2232
 
2331
2233
  // check sign
2332
 
  if (bymonthday[startIndex] == '-')
2333
 
  {
 
2234
  if (bymonthday[startIndex] == '-') {
2334
2235
    isNegative = true;
2335
2236
    ++startIndex;
2336
2237
  }
2337
 
  else if (bymonthday[startIndex] == '+')
2338
 
  {
 
2238
  else if (bymonthday[startIndex] == '+') {
2339
2239
    isNegative = false;
2340
2240
    ++startIndex;
2341
2241
  }
2342
 
  else
2343
 
  {
 
2242
  else {
2344
2243
    isNegative = false;
2345
2244
  }
2346
2245
  // convert to number
2347
 
  for (string::size_type i = startIndex; i < endIndex; ++i)
2348
 
  {
2349
 
    if (isdigit(bymonthday[i]))
2350
 
    {
 
2246
  for (string::size_type i = startIndex; i < endIndex; ++i) {
 
2247
    if (isdigit(bymonthday[i])) {
2351
2248
      val = val * 10 + ((bymonthday[i]) - '0');
2352
2249
    }
2353
 
    else
2354
 
    {
 
2250
    else {
2355
2251
      return false;
2356
2252
    }
2357
2253
  }
2358
2254
  // put into mask
2359
 
  if (isNegative)
2360
 
  {
 
2255
  if (isNegative) {
2361
2256
    lastmask |= ((uInt64)1<<(val - 1));
2362
2257
  }
2363
 
  else
2364
 
  {
 
2258
  else {
2365
2259
    firstmask |= ((uInt64)1<<(val - 1));
2366
2260
  }
2367
 
 
2368
2261
  return true;
2369
2262
} // setMonthDay
2370
2263