package com.plusonelabs.calendar;
import android.test.InstrumentationTestCase;
import android.util.Log;
import com.plusonelabs.calendar.calendar.CalendarQueryRow;
import com.plusonelabs.calendar.calendar.MockCalendarContentProvider;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.LocalDateTime;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
/**
* @author yvolk@yurivolkov.com
*/
public class IllegalInstantDueToTimeZoneTransitionTest extends InstrumentationTestCase {
private static final String TAG = IllegalInstantDueToTimeZoneTransitionTest.class.getSimpleName();
private MockCalendarContentProvider provider = null;
private EventRemoteViewsFactory factory = null;
private int eventId = 0;
@Override
protected void setUp() throws Exception {
super.setUp();
provider = MockCalendarContentProvider.getContentProvider(this);
factory = new EventRemoteViewsFactory(provider.getContext(), provider.getWidgetId());
assertTrue(factory.getWidgetEntries().isEmpty());
}
@Override
protected void tearDown() throws Exception {
provider.tearDown();
super.tearDown();
}
/**
* Issue 186
* See http://joda-time.sourceforge.net/faq.html#illegalinstant
* http://stackoverflow.com/questions/25233776/unable-to-create-a-specific-joda-datetime-illegalinstantexception
* http://beust.com/weblog/2013/03/30/the-time-that-never-was/
* <p>
* I couldn't reproduce the problem though.
*/
public void testIllegalInstantDueToTimeZoneOffsetTransition() {
reproducedTimeZoneOffsetTransitionException();
oneTimeDst("2014-09-07T00:00:00+00:00");
oneTimeDst("2015-03-29T00:00:00+00:00");
oneTimeDst("2015-10-25T00:00:00+00:00");
oneTimeDst("2011-03-27T00:00:00+00:00");
oneTimeDst("1980-04-06T00:00:00+00:00");
factory.onDataSetChanged();
factory.logWidgetEntries(TAG);
assertEquals(1, provider.getQueriesCount());
}
private void oneTimeDst(String iso8601time) {
long millis = toMillis(iso8601time);
String title = "DST";
for (int ind = -25; ind < 26; ind++) {
provider.addRow(new CalendarQueryRow().setEventId(++eventId).setTitle(title + " " + ind)
.setBegin(millis + TimeUnit.HOURS.toMillis(ind)).setAllDay(1));
}
}
/**
* from http://stackoverflow.com/a/5451245/297710
*/
private void reproducedTimeZoneOffsetTransitionException() {
final DateTimeZone dateTimeZone = DateTimeZone.forID("CET");
LocalDateTime localDateTime = new LocalDateTime(dateTimeZone)
.withYear(2011)
.withMonthOfYear(3)
.withDayOfMonth(27)
.withHourOfDay(2);
// this is just here to illustrate I'm solving the problem;
// don't need in operational code
try {
DateTime myDateBroken = localDateTime.toDateTime(dateTimeZone);
fail("No exception for " + localDateTime + " -> " + myDateBroken);
} catch (IllegalArgumentException iae) {
Log.v(TAG, "Sure enough, invalid instant due to time zone offset transition: "
+ localDateTime);
}
if (dateTimeZone.isLocalDateTimeGap(localDateTime)) {
localDateTime = localDateTime.withHourOfDay(3);
}
DateTime myDate = localDateTime.toDateTime(dateTimeZone);
Log.v(TAG, "No problem with this date: " + myDate);
}
/**
* http://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html
*/
private long toMillis(String iso8601time) {
Date date;
try {
date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssz", Locale.GERMANY).parse(iso8601time);
} catch (ParseException e) {
throw new IllegalArgumentException(iso8601time, e);
}
return date.getTime();
}
}