Revision: 6676 Author: r...@google.com Date: Wed Nov 4 14:08:43 2009 Log: Merge tr...@6674 into the branch Fix dates that fall into non-existent time ranges due to daylight savings svn merge --ignore-ancestry -c 6674 https://google-web-toolkit.googlecode.com/svn/trunk/ .
http://code.google.com/p/google-web-toolkit/source/detail?r=6676 Modified: /releases/2.0/branch-info.txt /releases/2.0/user/super/com/google/gwt/emul/java/util/Date.java /releases/2.0/user/test/com/google/gwt/emultest/java/util/DateTest.java ======================================= --- /releases/2.0/branch-info.txt Wed Nov 4 13:43:21 2009 +++ /releases/2.0/branch-info.txt Wed Nov 4 14:08:43 2009 @@ -307,3 +307,7 @@ Hosted mode fixes for SingleJsoImpl svn merge --ignore-ancestry -c6669,6670 https://google-web-toolkit.googlecode.com/svn/trunk/ . +tr...@6674 was merged into the branch + Fix dates that fall into non-existent time ranges due to daylight savings + svn merge --ignore-ancestry -c 6674 https://google-web-toolkit.googlecode.com/svn/trunk/ . + ======================================= --- /releases/2.0/user/super/com/google/gwt/emul/java/util/Date.java Thu Aug 13 12:43:26 2009 +++ /releases/2.0/user/super/com/google/gwt/emul/java/util/Date.java Wed Nov 4 14:08:43 2009 @@ -105,7 +105,7 @@ int min, int sec) /*-{ return Date.UTC(year + 1900, month, date, hrs, min, sec); }-*/; - + /** * JavaScript Date instance. */ @@ -206,7 +206,7 @@ public native int getYear() /*-{ th...@java.util.date::checkJsDate()(); - return th...@java.util.date::jsdate.getFullYear()-1900; + return th...@java.util.date::jsdate.getFullYear() - 1900; }-*/; @Override @@ -216,27 +216,36 @@ public native void setDate(int date) /*-{ th...@java.util.date::checkJsDate()(); + var hours = th...@java.util.date::jsdate.getHours() th...@java.util.date::jsdate.setDate(date); + th...@java.util.date::fixDaylightSavings(I)(hours); }-*/; public native void setHours(int hours) /*-{ th...@java.util.date::checkJsDate()(); th...@java.util.date::jsdate.setHours(hours); + th...@java.util.date::fixDaylightSavings(I)(hours); }-*/; public native void setMinutes(int minutes) /*-{ th...@java.util.date::checkJsDate()(); + var hours = th...@java.util.date::jsdate.getHours() + minutes / 60; th...@java.util.date::jsdate.setMinutes(minutes); + th...@java.util.date::fixDaylightSavings(I)(hours); }-*/; public native void setMonth(int month) /*-{ th...@java.util.date::checkJsDate()(); + var hours = th...@java.util.date::jsdate.getHours(); th...@java.util.date::jsdate.setMonth(month); + th...@java.util.date::fixDaylightSavings(I)(hours); }-*/; public native void setSeconds(int seconds) /*-{ th...@java.util.date::checkJsDate()(); + var hours = th...@java.util.date::jsdate.getHours() + seconds / (60 * 60); th...@java.util.date::jsdate.setSeconds(seconds); + th...@java.util.date::fixDaylightSavings(I)(hours); }-*/; public void setTime(long time) { @@ -245,7 +254,9 @@ public native void setYear(int year) /*-{ th...@java.util.date::checkJsDate()(); + var hours = th...@java.util.date::jsdate.getHours() th...@java.util.date::jsdate.setFullYear(year + 1900); + th...@java.util.date::fixDaylightSavings(I)(hours); }-*/; public native String toGMTString() /*-{ @@ -306,6 +317,57 @@ + th...@java.util.date::jsdate); } }-*/; + + /* + * Some browsers have the following behavior: + * + * // Assume a U.S. time zone with daylight savings + * // Set a non-existent time: 2:00 am Sunday March 8, 2009 + * var date = new Date(2009, 2, 8, 2, 0, 0); + * var hours = date.getHours(); // returns 1 + * + * The equivalent Java code will return 3. To compensate, we determine the + * amount of daylight savings adjustment by comparing the time zone offsets + * for the requested time and a time one day later, and add the adjustment to + * the hours and minutes of the requested time. + */ + + /** + * Detects if the requested time falls into a non-existent time range due to + * local time advancing into daylight savings time. If so, push the requested + * time forward out of the non-existent range. + */ + @SuppressWarnings("unused") // called by JSNI + private native void fixDaylightSavings(int hours) /*-{ + if ((th...@java.util.date::jsdate.getHours() % 24) != (hours % 24)) { + // Find the change in time zone offset between the current + // time and the same time the following day + var d = new Date(); + d.setTime(th...@java.util.date::jsdate.getTime()); + var noff = d.getTimezoneOffset(); + d.setDate(d.getDate() + 1); + var loff = d.getTimezoneOffset(); + var timeDiff = noff - loff; + + // If the time zone offset is changing, advance the hours and + // minutes from the initially requested time by the change amount + if (timeDiff > 0) { + var year = th...@java.util.date::jsdate.getYear() + 1900; + var month = th...@java.util.date::jsdate.getMonth(); + var day = th...@java.util.date::jsdate.getDate(); + var badHours = th...@java.util.date::jsdate.getHours(); + var minute = th...@java.util.date::jsdate.getMinutes(); + var second = th...@java.util.date::jsdate.getSeconds(); + if (badHours + timeDiff / 60 >= 24) { + day++; + } + var newTime = new Date(year, month, day, + hours + timeDiff / 60, + minute + timeDiff % 60, second); + th...@java.util.date::jsdate.setTime(newTime.getTime()); + } + } + }-*/; private native double getTime0() /*-{ th...@java.util.date::checkJsDate()(); @@ -326,6 +388,9 @@ th...@java.util.date::checkJsDate()(); th...@java.util.date::jsdate.setFullYear(year + 1900, month, date); th...@java.util.date::jsdate.setHours(hrs, min, sec, 0); + + // Set the expected hour. + th...@java.util.date::fixDaylightSavings(I)(hrs); }-*/; private native void setTime0(double time) /*-{ ======================================= --- /releases/2.0/user/test/com/google/gwt/emultest/java/util/DateTest.java Mon Jun 1 22:59:06 2009 +++ /releases/2.0/user/test/com/google/gwt/emultest/java/util/DateTest.java Wed Nov 4 14:08:43 2009 @@ -18,6 +18,7 @@ import com.google.gwt.core.client.GWT; import com.google.gwt.junit.client.GWTTestCase; +import java.util.ArrayList; import java.util.Date; /** @@ -638,6 +639,218 @@ int arg35 = 0; long a2 = accum2.UTC(arg30, arg31, arg32, arg33, arg34, arg35); } + + // Month and date of days with time shifts + private ArrayList<Integer> timeShiftMonth = new ArrayList<Integer>(); + private ArrayList<Integer> timeShiftDate = new ArrayList<Integer>(); + + private boolean containsTimeShift(Date start, int days) { + long startTime = start.getTime(); + Date end = new Date(); + end.setTime(startTime); + end.setDate(start.getDate() + days); + long endTime = end.getTime(); + return (endTime - startTime) != ((long) days * 24 * 60 * 60 * 1000); + } + + private void findTimeShift(Date start, int days) { + assertTrue(days != 0); + + // Found a shift day + if (days == 1) { + timeShiftMonth.add(start.getMonth()); + timeShiftDate.add(start.getDate()); + return; + } + + // Recurse over the first half of the period + if (containsTimeShift(start, days / 2)) { + findTimeShift(start, days / 2); + } + + // Recurse over the second half of the period + Date mid = new Date(); + mid.setTime(start.getTime()); + mid.setDate(start.getDate() + days / 2); + if (containsTimeShift(mid, days - days / 2)) { + findTimeShift(mid, days - days / 2); + } + } + + private void findTimeShifts(int year) { + timeShiftMonth.clear(); + timeShiftDate.clear(); + Date start = new Date(year - 1900, 0, 1, 12, 0, 0); + Date end = new Date(year + 1 - 1900, 0, 1, 12, 0, 0); + int days = (int) ((end.getTime() - start.getTime()) / + (24 * 60 * 60 * 1000)); + findTimeShift(start, days); + } + + private boolean findClockBackwardTime(int year, int[] monthDayHour) { + findTimeShifts(year); + int numShifts = timeShiftMonth.size(); + for (int i = 0; i < numShifts; i++) { + int month = timeShiftMonth.get(i); + int day = timeShiftDate.get(i); + + long start = new Date(year - 1900, month, day, 0, 30, 0).getTime(); + long end = new Date(year - 1900, month, day + 1, 23, 30, 0).getTime(); + int lastHour = -1; + for (long time = start; time < end; time += 60 * 60 * 1000) { + Date d = new Date(); + d.setTime(time); + int hour = d.getHours(); + if (hour == lastHour) { + monthDayHour[0] = d.getMonth(); + monthDayHour[1] = d.getDate(); + monthDayHour[2] = d.getHours(); + return true; + } + lastHour = hour; + } + } + + return false; + } + + private boolean findClockForwardTime(int year, int[] monthDayHour) { + findTimeShifts(year); + int numShifts = timeShiftMonth.size(); + for (int i = 0; i < numShifts; i++) { + int month = timeShiftMonth.get(i); + int startDay = timeShiftDate.get(i); + + for (int day = startDay; day <= startDay + 1; day++) { + for (int hour = 0; hour < 24; hour++) { + Date d = new Date(year - 1900, month, day, hour, 0, 0); + int h = d.getHours(); + if ((h % 24) == ((hour + 1) % 24)) { + monthDayHour[0] = month; + monthDayHour[1] = day; + monthDayHour[2] = hour; + return true; + } + } + } + } + + return false; + } + + public void testClockBackwardTime() { + int[] monthDayHour = new int[3]; + if (!findClockBackwardTime(2009, monthDayHour)) { + return; + } + + Date d; + int month = monthDayHour[0]; + int day = monthDayHour[1]; + int hour = monthDayHour[2]; + + // Check that this is the later of the two times having the + // same hour:minute:second + d = new Date(2009 - 1900, month, day, hour, 30, 0); + assertEquals(hour, d.getHours()); + d.setTime(d.getTime() - 60 * 60 * 1000); + assertEquals(hour, d.getHours()); + } + + public void testClockForwardTime() { + int[] monthDayHour = new int[3]; + if (!findClockForwardTime(2009, monthDayHour)) { + return; + } + + Date d; + int month = monthDayHour[0]; + int day = monthDayHour[1]; + int hour = monthDayHour[2]; + + d = new Date(2009 - 1900, month, day, hour, 0, 0); + assertEquals(hour + 1, d.getHours()); + + // Test year change -- assume the previous year changes on a different day + d = new Date(2008 - 1900, month, day, hour, 0, 0); + assertEquals(hour, d.getHours()); + d.setYear(2009 - 1900); + assertEquals(hour + 1, d.getHours()); + + // Test month change + d = new Date(2009 - 1900, month + 1, day, hour, 0, 0); + assertEquals(hour, d.getHours()); + d.setMonth(month); + assertEquals(3, d.getHours()); + + // Test day change + d = new Date(2009 - 1900, month, day + 1, hour, 0, 0); + assertEquals(hour, d.getHours()); + d.setDate(day); + assertEquals(hour + 1, d.getHours()); + + // Test hour setting + d = new Date(2009 - 1900, month, day, hour + 2, 0, 0); + assertEquals(hour + 2, d.getHours()); + d.setHours(hour); + assertEquals(hour + 1, d.getHours()); + + // Test changing hour by minutes = +- 60 + d = new Date(2009 - 1900, month, day, hour + 2, 0, 0); + assertEquals(hour + 2, d.getHours()); + d.setMinutes(-60); + assertEquals(hour + 1, d.getHours()); + + d = new Date(2009 - 1900, month, day, hour - 1, 0, 0); + assertEquals(hour - 1, d.getHours()); + d.setMinutes(60); + assertEquals(hour + 1, d.getHours()); + + // Test changing hour by minutes = +- 120 + d = new Date(2009 - 1900, month, day, hour + 2, 0, 0); + assertEquals(hour + 2, d.getHours()); + d.setMinutes(-120); + assertEquals(hour + 1, d.getHours()); + + d = new Date(2009 - 1900, month, day, hour - 2, 0, 0); + assertEquals(hour - 2, d.getHours()); + d.setMinutes(120); + assertEquals(hour + 1, d.getHours()); + + // Test changing hour by seconds = +- 3600 + d = new Date(2009 - 1900, month, day, hour + 2, 0, 0); + assertEquals(hour + 2, d.getHours()); + d.setSeconds(-3600); + assertEquals(hour + 1, d.getHours()); + + d = new Date(2009 - 1900, month, day, hour - 1, 0, 0); + assertEquals(hour - 1, d.getHours()); + d.setSeconds(3600); + assertEquals(hour + 1, d.getHours()); + + // Test changing hour by seconds = +- 7200 + d = new Date(2009 - 1900, month, day, hour + 2, 0, 0); + assertEquals(hour + 2, d.getHours()); + d.setSeconds(-7200); + assertEquals(hour + 1, d.getHours()); + + d = new Date(2009 - 1900, month, day, hour - 2, 0, 0); + assertEquals(hour - 2, d.getHours()); + d.setSeconds(7200); + assertEquals(hour + 1, d.getHours()); + + d = new Date(2009 - 1900, month, day, hour + 2, 0, 0); + d.setHours(hour); + d.setMinutes(30); + assertEquals(hour + 1, d.getHours()); + assertEquals(30, d.getMinutes()); + + d = new Date(2009 - 1900, month, day, hour + 2, 0, 0); + d.setMinutes(30); + d.setHours(hour); + assertEquals(hour + 1, d.getHours()); + assertEquals(30, d.getMinutes()); + } Date create() { return (Date) theDate.clone(); --~--~---------~--~----~------------~-------~--~----~ http://groups.google.com/group/Google-Web-Toolkit-Contributors -~----------~----~----~----~------~----~------~--~---