am 4c8adbc4: am 027a6ddf: Merge "Fix bugs related to TZ handling for all-day events" into froyo

Merge commit '4c8adbc4aa81308e57ae129e9587ec50483af6a8' into kraken

* commit '4c8adbc4aa81308e57ae129e9587ec50483af6a8':
  Fix bugs related to TZ handling for all-day events
This commit is contained in:
Marc Blank 2010-05-25 13:10:35 -07:00 committed by Android Git Automerger
commit 719d29bdcd
3 changed files with 140 additions and 47 deletions

View File

@ -128,6 +128,10 @@ public class CalendarSyncAdapter extends AbstractSyncAdapter {
private static final Object sSyncKeyLock = new Object();
private static final TimeZone UTC_TIMEZONE = TimeZone.getTimeZone("UTC");
private final TimeZone mLocalTimeZone = TimeZone.getDefault();
// Change this to use the constant in Calendar, when that constant is defined
private static final String EVENT_TIMEZONE2_COLUMN = "eventTimeZone2";
private long mCalendarId = -1;
private String mCalendarIdString;
@ -313,17 +317,10 @@ public class CalendarSyncAdapter extends AbstractSyncAdapter {
// If this is an all-day event, set hour, minute, and second to zero, and use UTC
if (allDayEvent != 0) {
GregorianCalendar cal = new GregorianCalendar(UTC_TIMEZONE);
cal.setTimeInMillis(startTime);
cal.set(GregorianCalendar.HOUR_OF_DAY, 0);
cal.set(GregorianCalendar.MINUTE, 0);
cal.set(GregorianCalendar.SECOND, 0);
startTime = cal.getTimeInMillis();
cal.setTimeInMillis(endTime);
cal.set(GregorianCalendar.HOUR_OF_DAY, 0);
cal.set(GregorianCalendar.MINUTE, 0);
cal.set(GregorianCalendar.SECOND, 0);
endTime = cal.getTimeInMillis();
startTime = CalendarUtilities.getUtcAllDayCalendarTime(startTime, mLocalTimeZone);
endTime = CalendarUtilities.getUtcAllDayCalendarTime(endTime, mLocalTimeZone);
String originalTimeZone = cv.getAsString(Events.EVENT_TIMEZONE);
cv.put(EVENT_TIMEZONE2_COLUMN, originalTimeZone);
cv.put(Events.EVENT_TIMEZONE, UTC_TIMEZONE.getID());
}
@ -381,6 +378,7 @@ public class CalendarSyncAdapter extends AbstractSyncAdapter {
long eventId = -1;
long startTime = -1;
long endTime = -1;
TimeZone timeZone = null;
// Keep track of the attendees; exceptions will need them
ArrayList<ContentValues> attendeeValues = new ArrayList<ContentValues>();
@ -434,6 +432,18 @@ public class CalendarSyncAdapter extends AbstractSyncAdapter {
switch (tag) {
case Tags.CALENDAR_ALL_DAY_EVENT:
allDayEvent = getValueInt();
if (allDayEvent != 0 && timeZone != null) {
// If the event doesn't start at midnight local time, we won't consider
// this an all-day event in the local time zone (this is what OWA does)
GregorianCalendar cal = new GregorianCalendar(mLocalTimeZone);
cal.setTimeInMillis(startTime);
userLog("All-day event arrived in: " + timeZone.getID());
if (cal.get(GregorianCalendar.HOUR_OF_DAY) != 0 ||
cal.get(GregorianCalendar.MINUTE) != 0) {
allDayEvent = 0;
userLog("Not an all-day event locally: " + mLocalTimeZone.getID());
}
}
cv.put(Events.ALL_DAY, allDayEvent);
break;
case Tags.CALENDAR_ATTENDEES:
@ -447,12 +457,11 @@ public class CalendarSyncAdapter extends AbstractSyncAdapter {
cv.put(Events.DESCRIPTION, getValue());
break;
case Tags.CALENDAR_TIME_ZONE:
TimeZone tz = CalendarUtilities.tziStringToTimeZone(getValue());
if (tz != null) {
cv.put(Events.EVENT_TIMEZONE, tz.getID());
} else {
cv.put(Events.EVENT_TIMEZONE, TimeZone.getDefault().getID());
timeZone = CalendarUtilities.tziStringToTimeZone(getValue());
if (timeZone == null) {
timeZone = mLocalTimeZone;
}
cv.put(Events.EVENT_TIMEZONE, timeZone.getID());
break;
case Tags.CALENDAR_START_TIME:
startTime = Utility.parseDateTimeToMillis(getValue());
@ -1334,10 +1343,19 @@ public class CalendarSyncAdapter extends AbstractSyncAdapter {
boolean isChange = entityValues.containsKey(Events._SYNC_ID);
Double version = mService.mProtocolVersionDouble;
boolean allDay = false;
if (entityValues.containsKey(Events.ALL_DAY)) {
Integer ade = entityValues.getAsInteger(Events.ALL_DAY);
if (ade != null && ade != 0) {
allDay = true;
}
}
// Get the event's time zone
String timeZoneName = entityValues.getAsString(Events.EVENT_TIMEZONE);
String timeZoneName =
entityValues.getAsString(allDay ? EVENT_TIMEZONE2_COLUMN : Events.EVENT_TIMEZONE);
if (timeZoneName == null) {
timeZoneName = TimeZone.getDefault().getID();
timeZoneName = mLocalTimeZone.getID();
}
TimeZone eventTimeZone = TimeZone.getTimeZone(timeZoneName);
@ -1348,14 +1366,7 @@ public class CalendarSyncAdapter extends AbstractSyncAdapter {
s.data(Tags.CALENDAR_TIME_ZONE, timeZone);
}
boolean allDay = false;
if (entityValues.containsKey(Events.ALL_DAY)) {
Integer ade = entityValues.getAsInteger(Events.ALL_DAY);
if (ade != null && ade != 0) {
allDay = true;
}
s.data(Tags.CALENDAR_ALL_DAY_EVENT, ade.toString());
}
s.data(Tags.CALENDAR_ALL_DAY_EVENT, allDay ? "1" : "0");
// DTSTART is always supplied
long startTime = entityValues.getAsLong(Events.DTSTART);
@ -1370,6 +1381,7 @@ public class CalendarSyncAdapter extends AbstractSyncAdapter {
Duration duration = new Duration();
try {
duration.parse(entityValues.getAsString(Events.DURATION));
durationMillis = duration.getMillis();
} catch (ParseException e) {
// Can't do much about this; use the default (1 hour)
}
@ -1377,9 +1389,9 @@ public class CalendarSyncAdapter extends AbstractSyncAdapter {
endTime = startTime + durationMillis;
}
if (allDay) {
TimeZone tz = TimeZone.getDefault();
startTime = CalendarUtilities.getAllDayCalendar(startTime, tz).getTimeInMillis();
endTime = CalendarUtilities.getAllDayCalendar(endTime, tz).getTimeInMillis();
TimeZone tz = mLocalTimeZone;
startTime = CalendarUtilities.getLocalAllDayCalendarTime(startTime, tz);
endTime = CalendarUtilities.getLocalAllDayCalendarTime(endTime, tz);
}
s.data(Tags.CALENDAR_START_TIME, CalendarUtilities.millisToEasDateTime(startTime));
s.data(Tags.CALENDAR_END_TIME, CalendarUtilities.millisToEasDateTime(endTime));

View File

@ -114,6 +114,8 @@ public class CalendarUtilities {
// TZI string cache; we keep around our encoded TimeZoneInformation strings
private static HashMap<TimeZone, String> sTziStringCache = new HashMap<TimeZone, String>();
private static final TimeZone UTC_TIMEZONE = TimeZone.getTimeZone("UTC");
// There is no type 4 (thus, the "")
static final String[] sTypeToFreq =
new String[] {"DAILY", "WEEKLY", "MONTHLY", "MONTHLY", "", "YEARLY", "YEARLY"};
@ -740,10 +742,9 @@ public class CalendarUtilities {
// In this case, there is no daylight savings time, so the only interesting data
// is the offset, and we know that all of the zoneId's match; we'll take the first
timeZone = TimeZone.getTimeZone(zoneIds[0]);
String dn = timeZone.getDisplayName();
sTimeZoneCache.put(timeZoneString, timeZone);
if (Eas.USER_LOG) {
SyncManager.log(TAG, "TimeZone without DST found by offset: " + dn);
SyncManager.log(TAG, "TimeZone without DST found by offset: " +
timeZone.getDisplayName());
}
return timeZone;
} else {
@ -785,6 +786,14 @@ public class CalendarUtilities {
if (dstSavings != timeZone.getDSTSavings()) continue;
return timeZone;
}
// In this case, there is no daylight savings time, so the only interesting data
// is the offset, and we know that all of the zoneId's match; we'll take the first
timeZone = TimeZone.getTimeZone(zoneIds[0]);
if (Eas.USER_LOG) {
SyncManager.log(TAG, "No TimeZone with correct DST settings; using first: " +
timeZone.getDisplayName());
}
return timeZone;
}
}
return null;
@ -894,23 +903,33 @@ public class CalendarUtilities {
}
/**
* Create a GregorianCalendar representing the year, month, and day for the given time in
* milliseconds and the local time zone. Hours, minutes, and seconds will be set to zero
* @param time the time in millis
* @param timeZone the time zone to be used
* @return a GregorianCalendar with the data required for an all-day event
* Returns a UTC calendar with year/month/day from local calendar and h/m/s/ms = 0
* @param time the time in seconds of an all-day event in local time
* @return the time in seconds in UTC
*/
static public GregorianCalendar getAllDayCalendar(long time, TimeZone timeZone) {
// Calendar gives us times in GMT
GregorianCalendar calendar = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
calendar.setTimeInMillis(time);
// But we must send back to EAS in the event's time zone
GregorianCalendar allDayCalendar = new GregorianCalendar(timeZone);
static public long getUtcAllDayCalendarTime(long time, TimeZone localTimeZone) {
return transposeAllDayTime(time, localTimeZone, UTC_TIMEZONE);
}
/**
* Returns a local calendar with year/month/day from UTC calendar and h/m/s/ms = 0
* @param time the time in seconds of an all-day event in UTC
* @return the time in seconds in local time
*/
static public long getLocalAllDayCalendarTime(long time, TimeZone localTimeZone) {
return transposeAllDayTime(time, UTC_TIMEZONE, localTimeZone);
}
static private long transposeAllDayTime(long time, TimeZone fromTimeZone,
TimeZone toTimeZone) {
GregorianCalendar fromCalendar = new GregorianCalendar(fromTimeZone);
fromCalendar.setTimeInMillis(time);
GregorianCalendar toCalendar = new GregorianCalendar(toTimeZone);
// Set this calendar with correct year, month, and day, but zero hour, minute, and seconds
allDayCalendar.set(calendar.get(GregorianCalendar.YEAR),
calendar.get(GregorianCalendar.MONTH),
calendar.get(GregorianCalendar.DATE), 0, 0, 0);
return allDayCalendar;
toCalendar.set(fromCalendar.get(GregorianCalendar.YEAR),
fromCalendar.get(GregorianCalendar.MONTH),
fromCalendar.get(GregorianCalendar.DATE), 0, 0, 0);
return toCalendar.getTimeInMillis();
}
static void addByDay(StringBuilder rrule, int dow, int wom) {

View File

@ -84,6 +84,13 @@ public class CalendarUtilitiesTests extends AndroidTestCase {
"AAAAAAAAAAoAAAAFAAMAAAAAAAAAAAAAAFIAdQBzAHMAaQBhAG4AIABEAGEAeQBsAGkAZwBoAHQAIABUAGkA" +
"bQBlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAFAAIAAAAAAAAAxP///w==";
// Test a timezone with GMT bias but bogus DST parameters (there is no equivalent time zone
// in the database)
private static final String GMT_UNKNOWN_DAYLIGHT_TIME =
"AAAAACgARwBNAFQAKwAwADAAOgAwADAAKQAgAFQAaQBtAGUAIABaAG8AbgBlAAAAAAAAAAAAAAAAAAAAAAAA" +
"AAAAAAAAAAEAAAABAAAAAAAAAAAAAAAAACgARwBNAFQAKwAwADAAOgAwADAAKQAgAFQAaQBtAGUAIABaAG8A" +
"bgBlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAFAAEAAAAAAAAAxP///w==";
private static final String ORGANIZER = "organizer@server.com";
private static final String ATTENDEE = "attendee@server.com";
@ -115,6 +122,9 @@ public class CalendarUtilitiesTests extends AndroidTestCase {
assertEquals("Australia/ACT", tz.getID());
tz = CalendarUtilities.tziStringToTimeZone(EUROPE_MOSCOW_TIME);
assertEquals("Europe/Moscow", tz.getID());
tz = CalendarUtilities.tziStringToTimeZone(GMT_UNKNOWN_DAYLIGHT_TIME);
int bias = tz.getOffset(System.currentTimeMillis());
assertEquals(0, bias);
}
public void testGenerateEasDayOfWeek() {
@ -820,6 +830,58 @@ public class CalendarUtilitiesTests extends AndroidTestCase {
CalendarUtilities.busyStatusFromAttendeeStatus(
Attendees.ATTENDEE_STATUS_ACCEPTED));
}
public void testGetUtcAllDayCalendarTime() {
GregorianCalendar correctUtc = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
correctUtc.set(2011, 2, 10, 0, 0, 0);
long correctUtcTime = correctUtc.getTimeInMillis();
TimeZone localTimeZone = TimeZone.getTimeZone("GMT-0700");
GregorianCalendar localCalendar = new GregorianCalendar(localTimeZone);
localCalendar.set(2011, 2, 10, 12, 23, 34);
long localTimeMillis = localCalendar.getTimeInMillis();
long convertedUtcTime =
CalendarUtilities.getUtcAllDayCalendarTime(localTimeMillis, localTimeZone);
// Milliseconds aren't zeroed out and may not be the same
assertEquals(convertedUtcTime/1000, correctUtcTime/1000);
localTimeZone = TimeZone.getTimeZone("GMT+0700");
localCalendar = new GregorianCalendar(localTimeZone);
localCalendar.set(2011, 2, 10, 12, 23, 34);
localTimeMillis = localCalendar.getTimeInMillis();
convertedUtcTime =
CalendarUtilities.getUtcAllDayCalendarTime(localTimeMillis, localTimeZone);
assertEquals(convertedUtcTime/1000, correctUtcTime/1000);
}
public void testGetLocalAllDayCalendarTime() {
TimeZone utcTimeZone = TimeZone.getTimeZone("UTC");
TimeZone localTimeZone = TimeZone.getTimeZone("GMT-0700");
GregorianCalendar correctLocal = new GregorianCalendar(localTimeZone);
correctLocal.set(2011, 2, 10, 0, 0, 0);
long correctLocalTime = correctLocal.getTimeInMillis();
GregorianCalendar utcCalendar = new GregorianCalendar(utcTimeZone);
utcCalendar.set(2011, 2, 10, 12, 23, 34);
long utcTimeMillis = utcCalendar.getTimeInMillis();
long convertedLocalTime =
CalendarUtilities.getLocalAllDayCalendarTime(utcTimeMillis, localTimeZone);
// Milliseconds aren't zeroed out and may not be the same
assertEquals(convertedLocalTime/1000, correctLocalTime/1000);
localTimeZone = TimeZone.getTimeZone("GMT+0700");
correctLocal = new GregorianCalendar(localTimeZone);
correctLocal.set(2011, 2, 10, 0, 0, 0);
correctLocalTime = correctLocal.getTimeInMillis();
utcCalendar = new GregorianCalendar(utcTimeZone);
utcCalendar.set(2011, 2, 10, 12, 23, 34);
utcTimeMillis = utcCalendar.getTimeInMillis();
convertedLocalTime =
CalendarUtilities.getLocalAllDayCalendarTime(utcTimeMillis, localTimeZone);
// Milliseconds aren't zeroed out and may not be the same
assertEquals(convertedLocalTime/1000, correctLocalTime/1000);
}
}
// TODO Planned unit tests