Merge commit '4c8adbc4aa81308e57ae129e9587ec50483af6a8' into kraken * commit '4c8adbc4aa81308e57ae129e9587ec50483af6a8': Fix bugs related to TZ handling for all-day events
This commit is contained in:
commit
719d29bdcd
@ -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));
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user