1.1 --- a/src/net/fortuna/ical4j/model/Recur.java Tue Feb 10 19:25:00 2015 +0100 1.2 +++ b/src/net/fortuna/ical4j/model/Recur.java Tue Feb 10 19:38:00 2015 +0100 1.3 @@ -44,6 +44,7 @@ 1.4 import java.util.StringTokenizer; 1.5 1.6 import net.fortuna.ical4j.model.parameter.Value; 1.7 +import net.fortuna.ical4j.util.CompatibilityHints; 1.8 import net.fortuna.ical4j.util.Configurator; 1.9 import net.fortuna.ical4j.util.Dates; 1.10 1.11 @@ -171,6 +172,8 @@ 1.12 private NumberList setPosList; 1.13 1.14 private String weekStartDay; 1.15 + 1.16 + private int calendarWeekStartDay; 1.17 1.18 private Map experimentalValues = new HashMap(); 1.19 1.20 @@ -181,6 +184,8 @@ 1.21 * Default constructor. 1.22 */ 1.23 public Recur() { 1.24 + // default week start is Monday per RFC5545 1.25 + calendarWeekStartDay = Calendar.MONDAY; 1.26 } 1.27 1.28 /** 1.29 @@ -189,6 +194,8 @@ 1.30 * @throws ParseException thrown when the specified string contains an invalid representation of an UNTIL date value 1.31 */ 1.32 public Recur(final String aValue) throws ParseException { 1.33 + // default week start is Monday per RFC5545 1.34 + calendarWeekStartDay = Calendar.MONDAY; 1.35 final StringTokenizer t = new StringTokenizer(aValue, ";="); 1.36 while (t.hasMoreTokens()) { 1.37 final String token = t.nextToken(); 1.38 @@ -241,10 +248,17 @@ 1.39 } 1.40 else if (WKST.equals(token)) { 1.41 weekStartDay = nextToken(t, token); 1.42 + calendarWeekStartDay = WeekDay.getCalendarDay(new WeekDay(weekStartDay)); 1.43 } 1.44 - // assume experimental value.. 1.45 else { 1.46 - experimentalValues.put(token, nextToken(t, token)); 1.47 + if (CompatibilityHints.isHintEnabled(CompatibilityHints.KEY_RELAXED_PARSING)) { 1.48 + // assume experimental value.. 1.49 + experimentalValues.put(token, nextToken(t, token)); 1.50 + } 1.51 + else { 1.52 + throw new IllegalArgumentException("Invalid recurrence rule part: " + 1.53 + token + "=" + nextToken(t, token)); 1.54 + } 1.55 } 1.56 } 1.57 validateFrequency(); 1.58 @@ -264,6 +278,8 @@ 1.59 * @param until maximum recurrence date 1.60 */ 1.61 public Recur(final String frequency, final Date until) { 1.62 + // default week start is Monday per RFC5545 1.63 + calendarWeekStartDay = Calendar.MONDAY; 1.64 this.frequency = frequency; 1.65 this.until = until; 1.66 validateFrequency(); 1.67 @@ -274,6 +290,8 @@ 1.68 * @param count maximum recurrence count 1.69 */ 1.70 public Recur(final String frequency, final int count) { 1.71 + // default week start is Monday per RFC5545 1.72 + calendarWeekStartDay = Calendar.MONDAY; 1.73 this.frequency = frequency; 1.74 this.count = count; 1.75 validateFrequency(); 1.76 @@ -416,6 +434,9 @@ 1.77 */ 1.78 public final void setWeekStartDay(final String weekStartDay) { 1.79 this.weekStartDay = weekStartDay; 1.80 + if (weekStartDay != null) { 1.81 + calendarWeekStartDay = WeekDay.getCalendarDay(new WeekDay(weekStartDay)); 1.82 + } 1.83 } 1.84 1.85 /** 1.86 @@ -578,8 +599,7 @@ 1.87 dates.setTimeZone(((DateTime) seed).getTimeZone()); 1.88 } 1.89 } 1.90 - final Calendar cal = Dates.getCalendarInstance(seed); 1.91 - cal.setTime(seed); 1.92 + final Calendar cal = getCalendarInstance(seed, true); 1.93 1.94 // optimize the start time for selecting candidates 1.95 // (only applicable where a COUNT is not specified) 1.96 @@ -670,8 +690,7 @@ 1.97 */ 1.98 public final Date getNextDate(final Date seed, final Date startDate) { 1.99 1.100 - final Calendar cal = Dates.getCalendarInstance(seed); 1.101 - cal.setTime(seed); 1.102 + final Calendar cal = getCalendarInstance(seed, true); 1.103 1.104 // optimize the start time for selecting candidates 1.105 // (only applicable where a COUNT is not specified) 1.106 @@ -857,8 +876,8 @@ 1.107 final DateList monthlyDates = getDateListInstance(dates); 1.108 for (final Iterator i = dates.iterator(); i.hasNext();) { 1.109 final Date date = (Date) i.next(); 1.110 - final Calendar cal = Dates.getCalendarInstance(date); 1.111 - cal.setTime(date); 1.112 + final Calendar cal = getCalendarInstance(date, true); 1.113 + 1.114 for (final Iterator j = getMonthList().iterator(); j.hasNext();) { 1.115 final Integer month = (Integer) j.next(); 1.116 // Java months are zero-based.. 1.117 @@ -883,8 +902,7 @@ 1.118 final DateList weekNoDates = getDateListInstance(dates); 1.119 for (final Iterator i = dates.iterator(); i.hasNext();) { 1.120 final Date date = (Date) i.next(); 1.121 - final Calendar cal = Dates.getCalendarInstance(date); 1.122 - cal.setTime(date); 1.123 + final Calendar cal = getCalendarInstance(date, true); 1.124 for (final Iterator j = getWeekNoList().iterator(); j.hasNext();) { 1.125 final Integer weekNo = (Integer) j.next(); 1.126 cal.set(Calendar.WEEK_OF_YEAR, Dates.getAbsWeekNo(cal.getTime(), weekNo.intValue())); 1.127 @@ -907,8 +925,7 @@ 1.128 final DateList yearDayDates = getDateListInstance(dates); 1.129 for (final Iterator i = dates.iterator(); i.hasNext();) { 1.130 final Date date = (Date) i.next(); 1.131 - final Calendar cal = Dates.getCalendarInstance(date); 1.132 - cal.setTime(date); 1.133 + final Calendar cal = getCalendarInstance(date, true); 1.134 for (final Iterator j = getYearDayList().iterator(); j.hasNext();) { 1.135 final Integer yearDay = (Integer) j.next(); 1.136 cal.set(Calendar.DAY_OF_YEAR, Dates.getAbsYearDay(cal.getTime(), yearDay.intValue())); 1.137 @@ -931,9 +948,7 @@ 1.138 final DateList monthDayDates = getDateListInstance(dates); 1.139 for (final Iterator i = dates.iterator(); i.hasNext();) { 1.140 final Date date = (Date) i.next(); 1.141 - final Calendar cal = Dates.getCalendarInstance(date); 1.142 - cal.setLenient(false); 1.143 - cal.setTime(date); 1.144 + final Calendar cal = getCalendarInstance(date, false); 1.145 for (final Iterator j = getMonthDayList().iterator(); j.hasNext();) { 1.146 final Integer monthDay = (Integer) j.next(); 1.147 try { 1.148 @@ -969,8 +984,7 @@ 1.149 // if BYYEARDAY or BYMONTHDAY is specified filter existing 1.150 // list.. 1.151 if (!getYearDayList().isEmpty() || !getMonthDayList().isEmpty()) { 1.152 - final Calendar cal = Dates.getCalendarInstance(date); 1.153 - cal.setTime(date); 1.154 + final Calendar cal = getCalendarInstance(date, true); 1.155 if (weekDay.equals(WeekDay.getWeekDay(cal))) { 1.156 weekDayDates.add(date); 1.157 } 1.158 @@ -991,15 +1005,7 @@ 1.159 * @return 1.160 */ 1.161 private List getAbsWeekDays(final Date date, final Value type, final WeekDay weekDay) { 1.162 - final Calendar cal = Dates.getCalendarInstance(date); 1.163 - // default week start is Monday per RFC5545 1.164 - int calendarWeekStartDay = Calendar.MONDAY; 1.165 - if (weekStartDay != null) { 1.166 - calendarWeekStartDay = WeekDay.getCalendarDay(new WeekDay(weekStartDay)); 1.167 - } 1.168 - cal.setFirstDayOfWeek(calendarWeekStartDay); 1.169 - cal.setTime(date); 1.170 - 1.171 + final Calendar cal = getCalendarInstance(date, true); 1.172 final DateList days = new DateList(type); 1.173 if (date instanceof DateTime) { 1.174 if (((DateTime) date).isUtc()) { 1.175 @@ -1095,8 +1101,7 @@ 1.176 final DateList hourlyDates = getDateListInstance(dates); 1.177 for (final Iterator i = dates.iterator(); i.hasNext();) { 1.178 final Date date = (Date) i.next(); 1.179 - final Calendar cal = Dates.getCalendarInstance(date); 1.180 - cal.setTime(date); 1.181 + final Calendar cal = getCalendarInstance(date, true); 1.182 for (final Iterator j = getHourList().iterator(); j.hasNext();) { 1.183 final Integer hour = (Integer) j.next(); 1.184 cal.set(Calendar.HOUR_OF_DAY, hour.intValue()); 1.185 @@ -1119,8 +1124,7 @@ 1.186 final DateList minutelyDates = getDateListInstance(dates); 1.187 for (final Iterator i = dates.iterator(); i.hasNext();) { 1.188 final Date date = (Date) i.next(); 1.189 - final Calendar cal = Dates.getCalendarInstance(date); 1.190 - cal.setTime(date); 1.191 + final Calendar cal = getCalendarInstance(date, true); 1.192 for (final Iterator j = getMinuteList().iterator(); j.hasNext();) { 1.193 final Integer minute = (Integer) j.next(); 1.194 cal.set(Calendar.MINUTE, minute.intValue()); 1.195 @@ -1143,8 +1147,7 @@ 1.196 final DateList secondlyDates = getDateListInstance(dates); 1.197 for (final Iterator i = dates.iterator(); i.hasNext();) { 1.198 final Date date = (Date) i.next(); 1.199 - final Calendar cal = Dates.getCalendarInstance(date); 1.200 - cal.setTime(date); 1.201 + final Calendar cal = getCalendarInstance(date, true); 1.202 for (final Iterator j = getSecondList().iterator(); j.hasNext();) { 1.203 final Integer second = (Integer) j.next(); 1.204 cal.set(Calendar.SECOND, second.intValue()); 1.205 @@ -1218,6 +1221,23 @@ 1.206 } 1.207 1.208 /** 1.209 + * Construct a Calendar object and sets the time. 1.210 + * @param date 1.211 + * @param lenient 1.212 + * @return 1.213 + */ 1.214 + private Calendar getCalendarInstance(final Date date, final boolean lenient) { 1.215 + Calendar cal = Dates.getCalendarInstance(date); 1.216 + // A week should have at least 4 days to be considered as such per RFC5545 1.217 + cal.setMinimalDaysInFirstWeek(4); 1.218 + cal.setFirstDayOfWeek(calendarWeekStartDay); 1.219 + cal.setLenient(lenient); 1.220 + cal.setTime(date); 1.221 + 1.222 + return cal; 1.223 + } 1.224 + 1.225 + /** 1.226 * @param stream 1.227 * @throws IOException 1.228 * @throws ClassNotFoundException 1.229 @@ -1233,7 +1253,7 @@ 1.230 * @param origList 1.231 * @return a new empty list. 1.232 */ 1.233 - private static final DateList getDateListInstance(final DateList origList) { 1.234 + private static DateList getDateListInstance(final DateList origList) { 1.235 final DateList list = new DateList(origList.getType()); 1.236 if (origList.isUtc()) { 1.237 list.setUtc(true);