Merge "Build proper TimeZoneInformation strings for upsync; fix bugs"

This commit is contained in:
Marc Blank 2010-02-03 14:42:23 -08:00 committed by Android (Google) Code Review
commit 7c6c1fd519
5 changed files with 295 additions and 102 deletions

View File

@ -98,7 +98,7 @@ public class CalendarSyncAdapterService extends Service {
}
if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD)) {
Cursor c = cr.query(Events.CONTENT_URI,
new String[] {Events._ID}, Events._SYNC_ID+ " ISNULL", null, null);
new String[] {Events._ID}, Events._SYNC_DIRTY + "=1", null, null);
try {
if (!c.moveToFirst()) {
if (logging) {

View File

@ -112,10 +112,6 @@ public class CalendarSyncAdapter extends AbstractSyncAdapter {
} finally {
c.close();
}
if (mCalendarId == -1) {
mCalendarId = Long.parseLong(mailbox.mSyncStatus);
}
}
@Override
@ -291,7 +287,7 @@ public class CalendarSyncAdapter extends AbstractSyncAdapter {
cv.put(Events.DESCRIPTION, getValue());
break;
case Tags.CALENDAR_TIME_ZONE:
TimeZone tz = CalendarUtilities.parseTimeZone(getValue());
TimeZone tz = CalendarUtilities.tziStringToTimeZone(getValue());
if (tz != null) {
cv.put(Events.EVENT_TIMEZONE, tz.getID());
} else {
@ -653,6 +649,8 @@ public class CalendarSyncAdapter extends AbstractSyncAdapter {
}
}
// Handle null data without error
if (body == null) return "";
// Remove \r's from any body text
return body.replace("\r\n", "\n");
}
@ -1012,7 +1010,7 @@ public class CalendarSyncAdapter extends AbstractSyncAdapter {
} else {
timeZoneName = TimeZone.getDefault().getID();
}
String x = CalendarUtilities.timeZoneToTZIString(timeZoneName);
String x = CalendarUtilities.timeZoneToTziString(TimeZone.getTimeZone(timeZoneName));
s.data(Tags.CALENDAR_TIME_ZONE, x);
if (entityValues.containsKey(Events.DESCRIPTION)) {
@ -1180,7 +1178,8 @@ public class CalendarSyncAdapter extends AbstractSyncAdapter {
// EAS 2.5 needs: BusyStatus DtStamp EndTime Sensitivity StartTime TimeZone UID
// We can generate all but what we're testing for below
if (!entityValues.containsKey(Events.DTSTART)
|| !entityValues.containsKey(Events.DURATION)) {
|| (!entityValues.containsKey(Events.DURATION) &&
!entityValues.containsKey(Events.DTEND))) {
continue;
}
// TODO Handle BusyStatus for EAS 2.5

View File

@ -51,6 +51,9 @@ public abstract class Parser {
private boolean logging = false;
private boolean capture = false;
private String logTag = "EAS Parser";
// Where tags start in a page
private static final int TAG_BASE = 5;
private ArrayList<Integer> captureArray;
@ -199,6 +202,13 @@ public abstract class Parser {
public String getValue() throws IOException {
// The false argument tells getNext to return the value as a String
getNext(false);
// This means there was no value given, just <Foo/>; we'll return empty string for now
if (type == END) {
if (logging) {
log("No value for tag: " + tagTable[startTag - TAG_BASE]);
}
return "";
}
// Save the value
String val = text;
// Read the next token; it had better be the end of the current tag
@ -220,6 +230,9 @@ public abstract class Parser {
public int getValueInt() throws IOException {
// The true argument to getNext indicates the desire for an integer return value
getNext(true);
if (type == END) {
return 0;
}
// Save the value
int val = num;
// Read the next token; it had better be the end of the current tag
@ -394,7 +407,7 @@ public abstract class Parser {
text = readInlineString();
}
if (logging) {
name = tagTable[startTag - 5];
name = tagTable[startTag - TAG_BASE];
log(name + ": " + (asInt ? Integer.toString(num) : text));
}
break;
@ -408,7 +421,7 @@ public abstract class Parser {
noContent = (id & 0x40) == 0;
depth++;
if (logging) {
name = tagTable[startTag - 5];
name = tagTable[startTag - TAG_BASE];
//log('<' + name + '>');
nameArray[depth] = name;
}

View File

@ -14,12 +14,6 @@
* limitations under the License.
*/
/**
* Tests of EAS Calendar Utilities
* You can run this entire test case with:
* runtest -c com.android.exchange.utility.CalendarUtilitiesTests email
*/
package com.android.exchange.utility;
import com.android.exchange.Eas;
@ -45,6 +39,7 @@ public class CalendarUtilities {
static final int SECONDS = 1000;
static final int MINUTES = SECONDS*60;
static final int HOURS = MINUTES*60;
static final long DAYS = HOURS*24;
// NOTE All Microsoft data structures are little endian
@ -87,6 +82,8 @@ public class CalendarUtilities {
// TimeZone cache; we parse/decode as little as possible, because the process is quite slow
private static HashMap<String, TimeZone> sTimeZoneCache = new HashMap<String, TimeZone>();
// TZI string cache; we keep around our encoded TimeZoneInformation strings
private static HashMap<TimeZone, String> sTziStringCache = new HashMap<TimeZone, String>();
// There is no type 4 (thus, the "")
static final String[] sTypeToFreq =
@ -98,6 +95,9 @@ public class CalendarUtilities {
static final String[] sTwoCharacterNumbers =
new String[] {"00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"};
static final int sCurrentYear = new GregorianCalendar().get(Calendar.YEAR);
static final TimeZone sGmtTimeZone = TimeZone.getTimeZone("GMT");
// Return a 4-byte long from a byte array (little endian)
static int getLong(byte[] bytes, int offset) {
return (bytes[offset++] & 0xFF) | ((bytes[offset++] & 0xFF) << 8) |
@ -135,6 +135,28 @@ public class CalendarUtilities {
int minute;
}
// Write SYSTEMTIME data into a byte array (this will either be for the standard or daylight
// transition)
static void putTimeInMillisIntoSystemTime(byte[] bytes, int offset, long millis) {
GregorianCalendar cal = new GregorianCalendar(TimeZone.getDefault());
// Round to the next highest minute; we always write seconds as zero
cal.setTimeInMillis(millis + 30*SECONDS);
// MSFT months are 1 based; TimeZone is 0 based
setWord(bytes, offset + MSFT_SYSTEMTIME_MONTH, cal.get(Calendar.MONTH) + 1);
// MSFT day of week starts w/ Sunday = 0; TimeZone starts w/ Sunday = 1
setWord(bytes, offset + MSFT_SYSTEMTIME_DAY_OF_WEEK, cal.get(Calendar.DAY_OF_WEEK) - 1);
// Get the "day" in TimeZone format
int wom = cal.get(Calendar.DAY_OF_WEEK_IN_MONTH);
// 5 means "last" in MSFT land; for TimeZone, it's -1
setWord(bytes, offset + MSFT_SYSTEMTIME_DAY, wom < 0 ? 5 : wom);
// Turn hours/minutes into ms from midnight (per TimeZone)
setWord(bytes, offset + MSFT_SYSTEMTIME_HOUR, cal.get(Calendar.HOUR));
setWord(bytes, offset + MSFT_SYSTEMTIME_MINUTE, cal.get(Calendar.MINUTE));
}
// Build a TimeZoneDate structure from a SYSTEMTIME within a byte array at a given offset
static TimeZoneDate getTimeZoneDateFromSystemTime(byte[] bytes, int offset) {
TimeZoneDate tzd = new TimeZoneDate();
@ -195,7 +217,7 @@ public class CalendarUtilities {
*/
static GregorianCalendar getCheckCalendar(TimeZone timeZone, TimeZoneDate tzd) {
GregorianCalendar testCalendar = new GregorianCalendar(timeZone);
testCalendar.set(GregorianCalendar.YEAR, 2009);
testCalendar.set(GregorianCalendar.YEAR, sCurrentYear);
testCalendar.set(GregorianCalendar.MONTH, tzd.month);
testCalendar.set(GregorianCalendar.DAY_OF_WEEK, tzd.dayOfWeek);
testCalendar.set(GregorianCalendar.DAY_OF_WEEK_IN_MONTH, tzd.day);
@ -204,21 +226,147 @@ public class CalendarUtilities {
return testCalendar;
}
/**
* Find a standard/daylight transition between a start time and an end time
* @param tz a TimeZone
* @param startTime the start time for the test
* @param endTime the end time for the test
* @param startInDaylightTime whether daylight time is in effect at the startTime
* @return the time in millis of the first transition, or 0 if none
*/
static private long findTransition(TimeZone tz, long startTime, long endTime,
boolean startInDaylightTime) {
long startingEndTime = endTime;
Date date = null;
while ((endTime - startTime) > MINUTES) {
long checkTime = ((startTime + endTime) / 2) + 1;
date = new Date(checkTime);
if (tz.inDaylightTime(date) != startInDaylightTime) {
endTime = checkTime;
} else {
startTime = checkTime;
}
}
if (endTime == startingEndTime) {
// Really, this shouldn't happen
return 0;
}
return startTime;
}
/**
* Return a Base64 representation of a MSFT TIME_ZONE_INFORMATION structure from a TimeZone
* that might be found in an Event; use cached result, if possible
* @param tz the TimeZone
* @return the Base64 String representing a Microsoft TIME_ZONE_INFORMATION element
*/
static public String timeZoneToTziString(TimeZone tz) {
String tziString = sTziStringCache.get(tz);
if (tziString != null) {
if (Eas.USER_LOG) {
Log.d(TAG, "TZI string for " + tz.getDisplayName() + " found in cache.");
}
return tziString;
}
tziString = timeZoneToTziStringImpl(tz);
sTziStringCache.put(tz, tziString);
return tziString;
}
/**
* Calculate the Base64 representation of a MSFT TIME_ZONE_INFORMATION structure from a TimeZone
* that might be found in an Event. Since the internal representation of the TimeZone is hidden
* from us we'll find the DST transitions and build the structure from that information
* @param tz the TimeZone
* @return the Base64 String representing a Microsoft TIME_ZONE_INFORMATION element
*/
static public String timeZoneToTziStringImpl(TimeZone tz) {
String tziString;
long time = System.currentTimeMillis();
byte[] tziBytes = new byte[MSFT_TIME_ZONE_SIZE];
int standardBias = - tz.getRawOffset();
standardBias /= 60*SECONDS;
setLong(tziBytes, MSFT_TIME_ZONE_BIAS_OFFSET, standardBias);
// If this time zone has daylight savings time, we need to do a bunch more work
if (tz.useDaylightTime()) {
long standardTransition = 0;
long daylightTransition = 0;
GregorianCalendar cal = new GregorianCalendar();
cal.set(sCurrentYear, Calendar.JANUARY, 1, 0, 0, 0);
cal.setTimeZone(tz);
long startTime = cal.getTimeInMillis();
// Calculate rough end of year; no need to do the calculation
long endOfYearTime = startTime + 365*DAYS;
Date date = new Date(startTime);
boolean startInDaylightTime = tz.inDaylightTime(date);
// Find the first transition, and store
startTime = findTransition(tz, startTime, endOfYearTime, startInDaylightTime);
if (startInDaylightTime) {
standardTransition = startTime;
} else {
daylightTransition = startTime;
}
// Find the second transition, and store
startTime = findTransition(tz, startTime, endOfYearTime, !startInDaylightTime);
if (startInDaylightTime) {
daylightTransition = startTime;
} else {
standardTransition = startTime;
}
if (standardTransition != 0 && daylightTransition != 0) {
putTimeInMillisIntoSystemTime(tziBytes, MSFT_TIME_ZONE_STANDARD_DATE_OFFSET,
standardTransition);
putTimeInMillisIntoSystemTime(tziBytes, MSFT_TIME_ZONE_DAYLIGHT_DATE_OFFSET,
daylightTransition);
int dstOffset = tz.getDSTSavings();
setLong(tziBytes, MSFT_TIME_ZONE_DAYLIGHT_BIAS_OFFSET, - dstOffset / MINUTES);
}
}
// TODO Use a more efficient Base64 API
byte[] tziEncodedBytes = Base64.encode(tziBytes);
tziString = new String(tziEncodedBytes);
if (Eas.USER_LOG) {
Log.d(TAG, "Calculated TZI String for " + tz.getDisplayName() + " in " +
(System.currentTimeMillis() - time) + "ms");
}
return tziString;
}
/**
* Given a String as directly read from EAS, returns a TimeZone corresponding to that String
* @param timeZoneString the String read from the server
* @return the TimeZone, or TimeZone.getDefault() if not found
*/
static public TimeZone parseTimeZone(String timeZoneString) {
static public TimeZone tziStringToTimeZone(String timeZoneString) {
// If we have this time zone cached, use that value and return
TimeZone timeZone = sTimeZoneCache.get(timeZoneString);
if (timeZone != null) {
if (Eas.USER_LOG) {
Log.d(TAG, "TimeZone " + timeZone.getID() + " in cache: " + timeZone.getDisplayName());
Log.d(TAG, " Using cached TimeZone " + timeZone.getDisplayName());
}
return timeZone;
} else {
timeZone = tziStringToTimeZoneImpl(timeZoneString);
if (timeZone == null) {
// If we don't find a match, we just return the current TimeZone. In theory, this
// shouldn't be happening...
Log.w(TAG, "TimeZone not found using default: " + timeZoneString);
timeZone = TimeZone.getDefault();
}
sTimeZoneCache.put(timeZoneString, timeZone);
}
return timeZone;
}
/**
* Given a String as directly read from EAS, tries to find a TimeZone in the database of all
* time zones that corresponds to that String.
* @param timeZoneString the String read from the server
* @return the TimeZone, or TimeZone.getDefault() if not found
*/
static public TimeZone tziStringToTimeZoneImpl(String timeZoneString) {
TimeZone timeZone = null;
// TODO Remove after we're comfortable with performance
long time = System.currentTimeMillis();
// First, we need to decode the base64 string
byte[] timeZoneBytes = Base64.decode(timeZoneString);
@ -245,13 +393,12 @@ public class CalendarUtilities {
if (Eas.USER_LOG) {
Log.d(TAG, "TimeZone without DST found by offset: " + dn);
}
return timeZone;
} else {
TimeZoneDate dstStart = getTimeZoneDateFromSystemTime(timeZoneBytes,
TimeZoneDate dstStart = getTimeZoneDateFromSystemTime(timeZoneBytes,
MSFT_TIME_ZONE_DAYLIGHT_DATE_OFFSET);
// See comment above for bias...
long dstSavings =
-1 * getLong(timeZoneBytes, MSFT_TIME_ZONE_DAYLIGHT_BIAS_OFFSET) * 60*SECONDS;
-1 * getLong(timeZoneBytes, MSFT_TIME_ZONE_DAYLIGHT_BIAS_OFFSET) * MINUTES;
// We'll go through each time zone to find one with the same DST transitions and
// savings length
@ -265,20 +412,23 @@ public class CalendarUtilities {
// of dst. That's the best we can do for now, since there's no other info
// provided by EAS (i.e. we can't get dynamic transitions, etc.)
int testSavingsMinutes = timeZone.getDSTSavings() / MINUTES;
int errorBoundsMinutes = (testSavingsMinutes * 2) + 1;
// Check start DST transition
GregorianCalendar testCalendar = getCheckCalendar(timeZone, dstStart);
testCalendar.add(GregorianCalendar.MINUTE, -1);
testCalendar.add(GregorianCalendar.MINUTE, - errorBoundsMinutes);
Date before = testCalendar.getTime();
testCalendar.add(GregorianCalendar.MINUTE, 2);
testCalendar.add(GregorianCalendar.MINUTE, 2*errorBoundsMinutes);
Date after = testCalendar.getTime();
if (timeZone.inDaylightTime(before)) continue;
if (!timeZone.inDaylightTime(after)) continue;
// Check end DST transition
testCalendar = getCheckCalendar(timeZone, dstEnd);
testCalendar.add(GregorianCalendar.HOUR, -2);
testCalendar.add(GregorianCalendar.MINUTE, - errorBoundsMinutes);
before = testCalendar.getTime();
testCalendar.add(GregorianCalendar.HOUR, 2);
testCalendar.add(GregorianCalendar.MINUTE, 2*errorBoundsMinutes);
after = testCalendar.getTime();
if (!timeZone.inDaylightTime(before)) continue;
if (timeZone.inDaylightTime(after)) continue;
@ -288,38 +438,16 @@ public class CalendarUtilities {
// If we're here, it's the right time zone, modulo dynamic DST
String dn = timeZone.getDisplayName();
sTimeZoneCache.put(timeZoneString, timeZone);
// TODO Remove timing when we're comfortable with performance
if (Eas.USER_LOG) {
Log.d(TAG, "TimeZone found by rules: " + dn);
Log.d(TAG, "TimeZone found by rules: " + dn + " in " +
(System.currentTimeMillis() - time) + "ms");
}
return timeZone;
break;
}
}
}
// If we don't find a match, we just return the current TimeZone. In theory, this
// shouldn't be happening...
Log.w(TAG, "TimeZone not found with bias = " + bias + ", using default.");
return TimeZone.getDefault();
}
/**
* Generate a Base64 representation of a MSFT TIME_ZONE_INFORMATION structure from a TimeZone
* ID that might be found in an Event. For now, we'll just use the standard bias, and we'll
* tackle DST later
* @param name the name of the TimeZone
* @return the Base64 String representing a Microsoft TIME_ZONE_INFORMATION element
*/
static public String timeZoneToTZIString(String name) {
// TODO Handle DST (ugh)
TimeZone tz = TimeZone.getTimeZone(name);
byte[] tziBytes = new byte[MSFT_TIME_ZONE_SIZE];
int standardBias = - tz.getRawOffset();
standardBias /= 60*SECONDS;
setLong(tziBytes, MSFT_TIME_ZONE_BIAS_OFFSET, standardBias);
byte[] tziEncodedBytes = Base64.encode(tziBytes);
return new String(tziEncodedBytes);
return timeZone;
}
/**
@ -327,7 +455,7 @@ public class CalendarUtilities {
* @param DateTime string from Exchange server
* @return the time in milliseconds (since Jan 1, 1970)
*/
static public long parseDateTimeToMillis(String date) {
static public long parseDateTimeToMillis(String date) {
// Format for calendar date strings is 20090211T180303Z
GregorianCalendar cal = new GregorianCalendar(Integer.parseInt(date.substring(0, 4)),
Integer.parseInt(date.substring(4, 6)) - 1, Integer.parseInt(date.substring(6, 8)),
@ -337,44 +465,57 @@ public class CalendarUtilities {
return cal.getTimeInMillis();
}
/**
* Generate a GregorianCalendar from a date string that represents a date/time in GMT
* @param DateTime string from Exchange server
* @return the GregorianCalendar
*/
static public GregorianCalendar parseDateTimeToCalendar(String date) {
// Format for calendar date strings is 20090211T180303Z
GregorianCalendar cal = new GregorianCalendar(Integer.parseInt(date.substring(0, 4)),
Integer.parseInt(date.substring(4, 6)) - 1, Integer.parseInt(date.substring(6, 8)),
Integer.parseInt(date.substring(9, 11)), Integer.parseInt(date.substring(11, 13)),
Integer.parseInt(date.substring(13, 15)));
cal.setTimeZone(TimeZone.getTimeZone("GMT"));
return cal;
}
/**
* Generate a GregorianCalendar from a date string that represents a date/time in GMT
* @param DateTime string from Exchange server
* @return the GregorianCalendar
*/
static public GregorianCalendar parseDateTimeToCalendar(String date) {
// Format for calendar date strings is 20090211T180303Z
GregorianCalendar cal = new GregorianCalendar(Integer.parseInt(date.substring(0, 4)),
Integer.parseInt(date.substring(4, 6)) - 1, Integer.parseInt(date.substring(6, 8)),
Integer.parseInt(date.substring(9, 11)), Integer.parseInt(date.substring(11, 13)),
Integer.parseInt(date.substring(13, 15)));
cal.setTimeZone(TimeZone.getTimeZone("GMT"));
return cal;
}
static String formatTwo(int num) {
if (num <= 12) {
return sTwoCharacterNumbers[num];
} else
return Integer.toString(num);
}
static String formatTwo(int num) {
if (num <= 12) {
return sTwoCharacterNumbers[num];
} else
return Integer.toString(num);
}
static public String millisToEasDateTime(long millis) {
StringBuilder sb = new StringBuilder();
GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
cal.setTimeInMillis(millis);
sb.append(cal.get(Calendar.YEAR));
sb.append(formatTwo(cal.get(Calendar.MONTH) + 1));
sb.append(formatTwo(cal.get(Calendar.DAY_OF_MONTH)));
sb.append('T');
sb.append(formatTwo(cal.get(Calendar.HOUR_OF_DAY)));
sb.append(formatTwo(cal.get(Calendar.MINUTE)));
sb.append(formatTwo(cal.get(Calendar.SECOND)));
sb.append('Z');
return sb.toString();
}
/**
* Generate an EAS formatted date/time string based on GMT. See below for details.
*/
static public String millisToEasDateTime(long millis) {
return millisToEasDateTime(millis, sGmtTimeZone);
}
static void addByDay(StringBuilder rrule, int dow, int wom) {
/**
* Generate an EAS formatted local date/time string from a time and a time zone
* @param millis a time in milliseconds
* @param tz a time zone
* @return an EAS formatted string indicating the date/time in the given time zone
*/
static public String millisToEasDateTime(long millis, TimeZone tz) {
StringBuilder sb = new StringBuilder();
GregorianCalendar cal = new GregorianCalendar(tz);
cal.setTimeInMillis(millis);
sb.append(cal.get(Calendar.YEAR));
sb.append(formatTwo(cal.get(Calendar.MONTH) + 1));
sb.append(formatTwo(cal.get(Calendar.DAY_OF_MONTH)));
sb.append('T');
sb.append(formatTwo(cal.get(Calendar.HOUR_OF_DAY)));
sb.append(formatTwo(cal.get(Calendar.MINUTE)));
sb.append(formatTwo(cal.get(Calendar.SECOND)));
sb.append('Z');
return sb.toString();
}
static void addByDay(StringBuilder rrule, int dow, int wom) {
rrule.append(";BYDAY=");
boolean addComma = false;
for (int i = 0; i < 7; i++) {
@ -420,6 +561,12 @@ public class CalendarUtilities {
return Integer.toString(bits);
}
/**
* Extract the value of a token in an RRULE string
* @param rrule an RRULE string
* @param token a token to look for in the RRULE
* @return the value of that token
*/
static String tokenFromRrule(String rrule, String token) {
int start = rrule.indexOf(token);
if (start < 0) return null;
@ -433,7 +580,7 @@ public class CalendarUtilities {
if (end == len) end++;
return rrule.substring(start, end -1);
}
} while (true);
} while (true);
}
/**
@ -447,7 +594,7 @@ public class CalendarUtilities {
// Calendar app UI, which is a small subset of possible recurrence types
// This code must be updated when the Calendar adds new functionality
static public void recurrenceFromRrule(String rrule, long startTime, Serializer s)
throws IOException {
throws IOException {
Log.d("RRULE", "rule: " + rrule);
String freq = tokenFromRrule(rrule, "FREQ=");
// If there's no FREQ=X, then we don't write a recurrence
@ -514,10 +661,22 @@ public class CalendarUtilities {
s.data(Tags.CALENDAR_RECURRENCE_DAYOFMONTH, byMonthDay);
s.data(Tags.CALENDAR_RECURRENCE_MONTHOFYEAR, byMonth);
s.end();
}
}
}
}
/**
* Build an RRULE String from EAS recurrence information
* @param type the type of recurrence
* @param occurrences how many recurrences (instances)
* @param interval the interval between recurrences
* @param dow day of the week
* @param dom day of the month
* @param wom week of the month
* @param moy month of the year
* @param until the last recurrence time
* @return a valid RRULE String
*/
static public String rruleFromRecurrence(int type, int occurrences, int interval, int dow,
int dom, int wom, int moy, String until) {
StringBuilder rrule = new StringBuilder("FREQ=" + sTypeToFreq[type]);

View File

@ -20,13 +20,23 @@ import android.test.AndroidTestCase;
import java.util.TimeZone;
/**
* Tests of EAS Calendar Utilities
* You can run this entire test case with:
* runtest -c com.android.exchange.utility.CalendarUtilitiesTests email
*
* Please see RFC2445 for RRULE definition
* http://www.ietf.org/rfc/rfc2445.txt
*/
public class CalendarUtilitiesTests extends AndroidTestCase {
// Some prebuilt time zones, Base64 encoded (as they arrive from EAS)
private static final String ISRAEL_STANDARD_TIME =
"iP///ygARwBNAFQAKwAwADIAOgAwADAAKQAgAEoAZQByAHUAcwBhAGwAZQBtAAAAAAAAAAAAAAAAAAAAAAAA" +
"AAAAAAAAAAkAAAAFAAIAAAAAAAAAAAAAACgARwBNAFQAKwAwADIAOgAwADAAKQAgAEoAZQByAHUAcwBhAGwA" +
"ZQBtAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMABQAFAAIAAAAAAAAAxP///w==";
// More time zones to be added over time
// Not all time zones are appropriate for testing. For example, ISRAEL_STANDARD_TIME cannot be
// used because DST is determined from year to year in a non-standard way (related to the lunar
// calendar); therefore, the test would only work during the year in which it was created
private static final String INDIA_STANDARD_TIME =
"tv7//0kAbgBkAGkAYQAgAFMAdABhAG4AZABhAHIAZAAgAFQAaQBtAGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEkAbgBkAGkAYQAgAEQAYQB5AGwAaQBnAGgAdAAgAFQAaQBtAGUA" +
@ -56,19 +66,20 @@ public class CalendarUtilitiesTests extends AndroidTestCase {
}
public void testParseTimeZoneEndToEnd() {
TimeZone tz = CalendarUtilities.parseTimeZone(PACIFIC_STANDARD_TIME);
TimeZone tz = CalendarUtilities.tziStringToTimeZone(PACIFIC_STANDARD_TIME);
assertEquals("Pacific Standard Time", tz.getDisplayName());
tz = CalendarUtilities.parseTimeZone(INDIA_STANDARD_TIME);
tz = CalendarUtilities.tziStringToTimeZone(INDIA_STANDARD_TIME);
assertEquals("India Standard Time", tz.getDisplayName());
tz = CalendarUtilities.parseTimeZone(ISRAEL_STANDARD_TIME);
assertEquals("Israel Standard Time", tz.getDisplayName());
}
public void testGenerateEasDayOfWeek() {
String byDay = "TU;WE;SA";
// TU = 4, WE = 8; SA = 64;
assertEquals("76", CalendarUtilities.generateEasDayOfWeek(byDay));
// MO = 2, TU = 4; WE = 8; TH = 16; FR = 32
byDay = "MO;TU;WE;TH;FR";
assertEquals("62", CalendarUtilities.generateEasDayOfWeek(byDay));
// SU = 1
byDay = "SU";
assertEquals("1", CalendarUtilities.generateEasDayOfWeek(byDay));
}
@ -81,7 +92,18 @@ public class CalendarUtilitiesTests extends AndroidTestCase {
assertNull(CalendarUtilities.tokenFromRrule(rrule, "UNTIL="));
}
// TODO In progress
// Tests in progress...
// public void testTimeZoneToTziString() {
// for (String timeZoneId: TimeZone.getAvailableIDs()) {
// TimeZone timeZone = TimeZone.getTimeZone(timeZoneId);
// if (timeZone != null) {
// String tzs = CalendarUtilities.timeZoneToTziString(timeZone);
// TimeZone newTimeZone = CalendarUtilities.tziStringToTimeZone(tzs);
// System.err.println("In: " + timeZone.getDisplayName() + ", Out: " + newTimeZone.getDisplayName());
// }
// }
// }
// public void testParseTimeZone() {
// GregorianCalendar cal = getTestCalendar(parsedTimeZone, dstStart);
// cal.add(GregorianCalendar.MINUTE, -1);