diff --git a/source/java/org/alfresco/repo/calendar/CalendarHelpersTest.java b/source/java/org/alfresco/repo/calendar/CalendarHelpersTest.java index 60f8560504..ecaae5b8e9 100644 --- a/source/java/org/alfresco/repo/calendar/CalendarHelpersTest.java +++ b/source/java/org/alfresco/repo/calendar/CalendarHelpersTest.java @@ -787,7 +787,7 @@ public class CalendarHelpersTest * Checks we correctly build the Timezone for somewhere * that doesn't have DST (eg Brisbane) */ - @Test public void simpleTimezoneNoDST() + @Test public void simpleTimeZoneNoDST() { SimpleTimeZone tz = CalendarTimezoneHelper.buildTimeZone(ICAL_TZ_BRISBANE); @@ -803,6 +803,95 @@ public class CalendarHelpersTest assertEquals(10*60*60*1000, tz.getOffset(date(2011,11,1).getTime())); } + /** + * Checks we correctly build the Timezone for somewhere + * in the northern hemisphere with DST (eg London) + */ + @Test public void simpleTimeZoneNorthern() + { + SimpleTimeZone tz = CalendarTimezoneHelper.buildTimeZone(ICAL_TZ_LONDON); + + assertNotNull(tz); + assertEquals("Europe/London", tz.getID()); + + // Does do DST + assertEquals(true, tz.useDaylightTime()); + + // In 2003, DST was 30th March - 26th October + assertEquals(0*60*60*1000, tz.getOffset(date(2003,3,1).getTime())); + assertEquals(1*60*60*1000, tz.getOffset(date(2003,3,31).getTime())); + assertEquals(1*60*60*1000, tz.getOffset(date(2003,9,1).getTime())); + assertEquals(1*60*60*1000, tz.getOffset(date(2003,10,25).getTime())); + assertEquals(0*60*60*1000, tz.getOffset(date(2003,11,1).getTime())); + + // In 2007, DST was 25th March - 28th October + assertEquals(0*60*60*1000, tz.getOffset(date(2007,3,1).getTime())); + assertEquals(1*60*60*1000, tz.getOffset(date(2007,3,26).getTime())); + assertEquals(1*60*60*1000, tz.getOffset(date(2007,3,31).getTime())); + assertEquals(1*60*60*1000, tz.getOffset(date(2007,9,1).getTime())); + assertEquals(1*60*60*1000, tz.getOffset(date(2007,10,28).getTime())); + assertEquals(0*60*60*1000, tz.getOffset(date(2007,10,29).getTime())); + assertEquals(0*60*60*1000, tz.getOffset(date(2007,11,1).getTime())); + + // In 2011, DST was 27th March - 30th October + assertEquals(0*60*60*1000, tz.getOffset(date(2011,3,1).getTime())); + assertEquals(1*60*60*1000, tz.getOffset(date(2011,3,31).getTime())); + assertEquals(1*60*60*1000, tz.getOffset(date(2011,9,1).getTime())); + assertEquals(1*60*60*1000, tz.getOffset(date(2011,10,25).getTime())); + assertEquals(0*60*60*1000, tz.getOffset(date(2011,11,1).getTime())); + } + + /** + * Checks we correctly build the Timezone for somewhere + * in the southern hemisphere with DST (eg Sydney) + * Note - Sydney is GMT+11 in December, GMT+10 in June + */ + @Test public void simpleTimeZoneSouthern() + { + SimpleTimeZone tz = CalendarTimezoneHelper.buildTimeZone(ICAL_TZ_SYDNEY); + + assertNotNull(tz); + assertEquals("Canberra, Melbourne, Sydney", tz.getID()); + + // Does do DST + assertEquals(true, tz.useDaylightTime()); + + // Note - things changed in 2008! + // In 2002-2003, DST was 27th October 2002 - 30th March 2003 + // In 2005-2006, DST was 30th October 2005 - 2nd April 2006 + // In 2007-2008, DST was 28th October 2007 - 6th April 2008 + + // In 2008-2009, DST was 5th October 2008 - 5th April 2009 + assertEquals(10*60*60*1000, tz.getOffset(date(2008,6,1).getTime())); + assertEquals(10*60*60*1000, tz.getOffset(date(2008,10,1).getTime())); + assertEquals(11*60*60*1000, tz.getOffset(date(2008,10,6).getTime())); + assertEquals(11*60*60*1000, tz.getOffset(date(2008,12,1).getTime())); + assertEquals(11*60*60*1000, tz.getOffset(date(2009,1,5).getTime())); + assertEquals(11*60*60*1000, tz.getOffset(date(2009,4,4).getTime())); + assertEquals(10*60*60*1000, tz.getOffset(date(2009,4,6).getTime())); + assertEquals(10*60*60*1000, tz.getOffset(date(2009,5,1).getTime())); + + // In 2009-2010, DST was 4th October 2009 - 4th April 2010 + assertEquals(10*60*60*1000, tz.getOffset(date(2009,6,1).getTime())); + assertEquals(10*60*60*1000, tz.getOffset(date(2009,10,1).getTime())); + assertEquals(11*60*60*1000, tz.getOffset(date(2009,10,6).getTime())); + assertEquals(11*60*60*1000, tz.getOffset(date(2009,12,1).getTime())); + assertEquals(11*60*60*1000, tz.getOffset(date(2010,1,5).getTime())); + assertEquals(11*60*60*1000, tz.getOffset(date(2010,4,3).getTime())); + assertEquals(10*60*60*1000, tz.getOffset(date(2010,4,5).getTime())); + assertEquals(10*60*60*1000, tz.getOffset(date(2010,5,1).getTime())); + + // In 2010-2011, DST was 3rd Oct 2010 - 3rd April 2011 + assertEquals(10*60*60*1000, tz.getOffset(date(2010,6,1).getTime())); + assertEquals(10*60*60*1000, tz.getOffset(date(2010,10,1).getTime())); + assertEquals(11*60*60*1000, tz.getOffset(date(2010,10,6).getTime())); + assertEquals(11*60*60*1000, tz.getOffset(date(2010,12,1).getTime())); + assertEquals(11*60*60*1000, tz.getOffset(date(2011,1,5).getTime())); + assertEquals(11*60*60*1000, tz.getOffset(date(2011,4,2).getTime())); + assertEquals(10*60*60*1000, tz.getOffset(date(2011,4,4).getTime())); + assertEquals(10*60*60*1000, tz.getOffset(date(2011,5,1).getTime())); + } + private static class RecurrenceHelper extends CalendarRecurrenceHelper { protected static void buildDailyRecurrences(Calendar currentDate, List dates, diff --git a/source/java/org/alfresco/service/cmr/calendar/CalendarTimezoneHelper.java b/source/java/org/alfresco/service/cmr/calendar/CalendarTimezoneHelper.java index 96d4abb096..5f194d7720 100644 --- a/source/java/org/alfresco/service/cmr/calendar/CalendarTimezoneHelper.java +++ b/source/java/org/alfresco/service/cmr/calendar/CalendarTimezoneHelper.java @@ -113,7 +113,8 @@ public class CalendarTimezoneHelper { tzID = "(unknown)"; } - tzID.replaceAll("\\\\", ""); + // De-escape commans + tzID = tzID.replace("\\,", ","); // Does it have daylight savings? if (tzDaylight.isEmpty()) @@ -123,8 +124,22 @@ public class CalendarTimezoneHelper return new SimpleTimeZone(offset, tzID); } - // TODO - return null; + // Get the offsets + int stdOffset = getOffset(tzDaylight.get("TZOFFSETFROM")); + int dstOffset = getOffset(tzDaylight.get("TZOFFSETTO")); + + // Turn the rules into SimpleTimeZone ones + int[] stdRules = getRuleForSimpleTimeZone(tzStandard.get("RRULE")); + int[] dstRules = getRuleForSimpleTimeZone(tzDaylight.get("RRULE")); + + // Build it up + return new SimpleTimeZone( + stdOffset, tzID, + dstRules[0], dstRules[1], dstRules[2], // When DST starts + 1*60*60*1000, // TODO Pull out the exact change time from DTSTART + stdRules[0], stdRules[1], stdRules[2], // When DST ends + 2*60*60*1000 // TODO Pull out the exact change time from DTSTART + ); } /** @@ -156,6 +171,62 @@ public class CalendarTimezoneHelper return offset; } + /** + * Turn an iCal repeating rule like + * "FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10" into a SimpleTimeZone rule + * like Month=March, StartDay=0, StartDayOfWeek=-Sunday + * See the JavaDocs of {@link SimpleTimeZone} for how to express + * the different requirements in the required int formats + */ + private static int[] getRuleForSimpleTimeZone(String rule) + { + // Turn the rule into chunks + Map params = new HashMap(); + for (String p : rule.split(";")) + { + int splitAt = p.indexOf('='); + if (splitAt == -1) + { + logger.info("Skipping invalid param " + p + " in recurrence rule " + rule); + } + else + { + params.put(p.substring(0,splitAt), p.substring(splitAt+1)); + } + } + + // Java months are 1 less than normal + int month = Integer.parseInt(params.get("BYMONTH")) - 1; + + // Should end with a day of the week + String byDay = params.get("BYDAY"); + String dow = byDay.substring(byDay.length()-2); + int dayOfWeek = CalendarRecurrenceHelper.d2cd.get(dow); + + // Where in the month does it come? + int dayOfMonth = 0; + if (byDay.startsWith("-1")) + { + // Last in month + dayOfMonth = -1; + } + else if (byDay.startsWith("1")) + { + // First in month + dayOfMonth = 1; + dayOfWeek = 0 - dayOfWeek; + } + else + { + // Nth day in month + dayOfMonth = 1 + (Integer.parseInt(byDay.substring(0,1)) - 1)*7; + dayOfWeek = 0 - dayOfWeek; + } + + // All done + return new int[] {month, dayOfMonth, dayOfWeek}; + } + /** * Turns an iCal event into event + timezone parameters. * This is very closely tied to the SPP / VTI implementation,