/*
 * Copyright 2008 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.google.gwt.emultest.java.util;

import com.google.gwt.junit.client.GWTTestCase;
import com.google.gwt.testing.TestUtils;

import java.util.ArrayList;
import java.util.Date;

/**
 * Tests for GWT's emulation of the JRE Date class.
 */
@SuppressWarnings("deprecation")
public class DateTest extends GWTTestCase {
  public static final String CURRENT = "CURRENT";
  public static final String TO_STRING_PATTERN =
      "\\w{3} \\w{3} \\d{2} \\d{2}:\\d{2}:\\d{2}( .+)? \\d{4}";
  public static final long DAY_MILLISECONDS_SHIFT = 27;
  public static final String FUTURE = "FUTURE";
  public static final String PAST = "PAST";
  public static final long SECOND_MILLISECONDS_SHIFT = 10;

  Date theDate = new Date();

  /**
   * Sets module name so that javascript compiler can operate.
   */
  @Override
  public String getModuleName() {
    return "com.google.gwt.emultest.EmulSuite";
  }

  /** Testing for public boolean java.util.Date.after(java.util.Date). */
  public void testAfter() {

    // /////////////////////////////
    // Current
    // /////////////////////////////
    Date accum0 = create();
    Date arg10 = create();
    boolean a0 = accum0.after(arg10);
    assertFalse(a0);
    // /////////////////////////////
    // Past
    // /////////////////////////////
    Date accum1 = create(PAST);
    Date arg20 = create();
    boolean a1 = accum1.after(arg20);
    assertFalse(a1);

    // /////////////////////////////
    // Future
    // /////////////////////////////
    Date accum2 = create(FUTURE);
    Date arg30 = create();
    boolean a2 = accum2.after(arg30);
    assertTrue(a2);
  }

  /** Testing for public boolean java.util.Date.before(java.util.Date). */
  public void testBefore() {

    // /////////////////////////////
    // Current
    // /////////////////////////////
    Date accum0 = create();
    Date arg10 = create();
    boolean a0 = accum0.before(arg10);
    assertFalse(a0);
    // /////////////////////////////
    // Past
    // /////////////////////////////
    Date accum1 = create(PAST);
    Date arg20 = create();
    boolean a1 = accum1.before(arg20);
    assertTrue(a1);

    // /////////////////////////////
    // Future
    // /////////////////////////////
    Date accum2 = create(FUTURE);
    Date arg30 = create();
    boolean a2 = accum2.before(arg30);
    assertFalse(a2);
  }

  /**
   * Tests that if daylight savings time occurs tomorrow, the current date isn't
   * affected.
   */
  public void testClockForwardNextDay() {
    int[] monthDayHour = new int[3];
    if (!findClockForwardTime(2009, monthDayHour)) {
      return;
    }

    int month = monthDayHour[0];
    int day = monthDayHour[1] - 1; // Day before.
    int hour = monthDayHour[2];
    Date d = new Date(2009 - 1900, month, day, hour, 0, 0);
    assertEquals(day, d.getDate());
    assertEquals(hour, d.getHours());

    // Change the minutes, which triggers fixDaylightSavings.
    d.setMinutes(10);
    assertEquals(day, d.getDate());
    assertEquals(hour, d.getHours());

    // Change the seconds, which triggers fixDaylightSavings.
    d.setSeconds(10);
    assertEquals(day, d.getDate());
    assertEquals(hour, d.getHours());

    // Change the minutes by more than an hour.
    d.setMinutes(80);
    assertEquals(day, d.getDate());
    assertEquals(hour + 1, d.getHours());
  }

  /** Testing for public java.lang.Object java.util.Date.clone(). */
  public void testClone() {

    // /////////////////////////////
    // Current
    // /////////////////////////////
    Date accum0 = create();
    Object a0 = accum0.clone();
    assertFalse(a0 == accum0);
    assertEquals(a0, accum0);
    // /////////////////////////////
    // Past
    // /////////////////////////////
    Date accum1 = create(PAST);
    Object a1 = accum1.clone();
    assertFalse(a1 == accum1);
    assertEquals(a1, accum1);

    // /////////////////////////////
    // Future
    // /////////////////////////////
    Date accum2 = create(FUTURE);
    Object a2 = accum2.clone();
    assertFalse(a2 == accum2);
    assertEquals(a2, accum2);
  }

  /** Testing for public int java.util.Date.compareTo(java.util.Date). */
  public void testCompareTo() {

    // /////////////////////////////
    // Current
    // /////////////////////////////
    Date accum0 = create();
    Date arg10 = create();
    int a0 = accum0.compareTo(arg10);
    assertEquals(0, a0);
    // /////////////////////////////
    // Past
    // /////////////////////////////
    Date accum1 = create();
    Date arg20 = create(PAST);
    int a1 = accum1.compareTo(arg20);
    assertEquals(1, a1);
    // /////////////////////////////
    // Future
    // /////////////////////////////
    Date accum2 = create();
    Date arg30 = create(FUTURE);
    int a2 = accum2.compareTo(arg30);
    assertEquals(-1, a2);
  }

  /** Testing for public int java.util.Date.getDate(). */
  public void testGetDate() {

    // /////////////////////////////
    // Past
    // /////////////////////////////
    Date accum1 = create(PAST);
    int a1 = accum1.getDate();
    int expectedValue = 5;
    if (needsToChangeDate(accum1, 0)) {
      expectedValue--;
    }
    assertEquals(expectedValue, a1);
    // /////////////////////////////
    // Future
    // /////////////////////////////
    Date accum2 = create(FUTURE);
    int a2 = accum2.getDate();
    expectedValue = 30;
    if (needsToChangeDate(accum2, 3 * 60 + 4)) {
      expectedValue--;
    }
    assertEquals(expectedValue, a2);
  }

  private boolean needsToChangeDate(Date d, int minuteOffset) {
    int timezoneOffset = d.getTimezoneOffset();
    return timezoneOffset > minuteOffset;
  }

  /** Testing for public int java.util.Date.getDay(). */
  public void testGetDay() {

    // /////////////////////////////
    // Current
    // /////////////////////////////
    Date accum0 = create();
    int a0 = accum0.getDay();

    // /////////////////////////////
    // Past
    // /////////////////////////////
    Date accum1 = create(PAST);
    int a1 = accum1.getDay();

    // /////////////////////////////
    // Future
    // /////////////////////////////
    Date accum2 = create(FUTURE);
    int a2 = accum2.getDay();
  }

  /** Testing for public int java.util.Date.getHours(). */
  public void testGetHours() {
    // Cannot be done because each time zone will give a different
    // answer
  }

  /** Testing for public int java.util.Date.getMinutes(). */
  public void testGetMinutes() {

    // /////////////////////////////
    // Past
    // /////////////////////////////
    Date accum1 = create(PAST);
    int a1 = accum1.getMinutes();
    assertEquals(0, a1);
    // /////////////////////////////
    // Future
    // /////////////////////////////
    Date accum2 = create(FUTURE);
    int a2 = accum2.getMinutes();
    assertEquals(4, a2);
  }

  /** Testing for public int java.util.Date.getMonth(). */
  public void testGetMonth() {

    // /////////////////////////////
    // Past
    // /////////////////////////////
    Date accum1 = create(PAST);
    int a1 = accum1.getMonth();
    assertEquals(0, a1);

    // /////////////////////////////
    // Future
    // /////////////////////////////
    Date accum2 = create(FUTURE);
    int a2 = accum2.getMonth();
    assertEquals(11, a2);
  }

  /** Testing for public int java.util.Date.getSeconds(). */
  public void testGetSeconds() {

    // /////////////////////////////
    // Past
    // /////////////////////////////
    Date accum1 = create(PAST);
    int a1 = accum1.getSeconds();
    assertEquals(0, a1);

    // /////////////////////////////
    // Future
    // /////////////////////////////
    Date accum2 = create(FUTURE);
    int a2 = accum2.getSeconds();
    assertEquals(5, a2);
  }

  /** Testing for public long java.util.Date.getTime(). */
  public void testGetTime() {

    // /////////////////////////////
    // Past
    // /////////////////////////////
    Date accum1 = create(PAST);
    long a1 = accum1.getTime();
    assertEquals(-2524176000000L, a1);

    // /////////////////////////////
    // Future
    // /////////////////////////////
    Date accum2 = create(FUTURE);
    long a2 = accum2.getTime();
    assertEquals(1924830245000L, a2);
  }

  /** Testing for public int java.util.Date.getTimezoneOffset(). */
  public void testGetTimezoneOffset() {

    // /////////////////////////////
    // Current
    // /////////////////////////////
    Date accum0 = create();
    int a0 = accum0.getTimezoneOffset();

    // /////////////////////////////
    // Past
    // /////////////////////////////
    Date accum1 = create(PAST);
    int a1 = accum1.getTimezoneOffset();

    // /////////////////////////////
    // Future
    // /////////////////////////////
    Date accum2 = create(FUTURE);
    int a2 = accum2.getTimezoneOffset();
  }

  /** Testing for public int java.util.Date.getYear(). */
  public void testGetYear() {

    // /////////////////////////////
    // Past
    // /////////////////////////////
    Date accum1 = create(PAST);
    int a1 = accum1.getYear();
    assertEquals(-10, a1);

    // /////////////////////////////
    // Future
    // /////////////////////////////
    Date accum2 = create(FUTURE);
    int a2 = accum2.getYear();
    assertEquals(130, a2);
  }

  /**
   * Testing to that if we set the day number to 31 for a month that only has 30
   * days in it, that the date rolls over to the first day of the next month in
   * sequence.
   */
  public void testInvalidDateForMonth() {
    int monthNum = 3; // April
    int numDaysInOldMonth = 30;
    int newDayNum = 31;
    Date dateWithThirtyDays = new Date(2006, monthNum, 30);
    dateWithThirtyDays.setDate(newDayNum);
    assertEquals(dateWithThirtyDays.getMonth(), monthNum + 1);
    assertEquals(dateWithThirtyDays.getDate(), newDayNum - numDaysInOldMonth);
  }

  /** Testing for public static long java.util.Date.parse(java.lang.String). */
  public void testParse() {
    try {
      Date.parse(null);
      fail("Should have thrown exception");
    } catch (IllegalArgumentException e) {
      // Expected
    }

    try {
      Date.parse("");
    } catch (IllegalArgumentException e) {
      // Expected
    }

    // /////////////////////////////
    // Current
    // /////////////////////////////

    // TODO(b/146498060): Reenable the roundtrip once toLocaleString is fixed for ie11.
    if (false) {
      Date accum0 = create();
      String arg10 = createString(CURRENT);
      long a0 = Date.parse(arg10);
      assertEquals(roundToDay(accum0.getTime()), roundToDay(a0));
    }

    // /////////////////////////////
    // Past
    // /////////////////////////////
    Date accum1 = create(PAST);
    String arg20 = createString(PAST);
    long a1 = Date.parse(arg20);
    assertEquals(-2524521600000L, a1);

    // /////////////////////////////
    // Future
    // /////////////////////////////
    Date accum2 = create(FUTURE);
    String arg30 = createString(FUTURE);
    long a2 = Date.parse(arg30);
    assertEquals(1924830245000L, a2);
  }

  /** Testing for public void java.util.Date.setDate(int). */
  public void testSetDate() {
    // We only go through dates from 0-28 here. There are some months that do
    // not
    // have 29, 30, or 31 days - so our assertion would be wrong in the cases
    // where
    // the current month did not have 29,30,or 31 days
    for (int i = 1; i < 29; i++) {
      Date accum0 = create();
      accum0.setDate(i);
      assertEquals(accum0.getDate(), i);
    }
  }

  /** Testing for public void java.util.Date.setHours(int). */
  public void testSetHours() {
    for (int i = 0; i < 24; i++) {
      Date accum0 = create();
      if (isDst(accum0)) {
        // This test fails on the day of DST, so skip it.
        return;
      }
      accum0.setHours(i);
      assertEquals(accum0.getHours(), i);
    }
  }

  /**
   * We want to test to see that if we are currently in a month with 31 days and
   * we set the month to one which has less than 31 days, that the month
   * returned by the date class will be one higher than the month that we
   * originally set (according to the spec of java.util.date).
   */
  public void testSetInvalidMonthForDate() {
    int dayNum = 31;
    int newMonthNum = 1;
    int numDaysInNewMonth = 28;
    Date dateWithThirtyOneDays = new Date(2006, 12, dayNum);
    dateWithThirtyOneDays.setMonth(newMonthNum);
    assertEquals(dateWithThirtyOneDays.getMonth(), newMonthNum + 1);
    assertEquals(dateWithThirtyOneDays.getDate(), dayNum - numDaysInNewMonth);
  }

  /**
   * We want to test to see that if the date is Feb 29th (in a leap year) and we
   * set the year to a non-leap year, that the month and day will roll over to
   * March 1st.
   */
  public void testSetInvalidYearForDate() {
    int dayNum = 29;
    int monthNum = 1; // February
    int newYearNum = 2005;
    int numDaysInFebInNewYear = 28;
    Date leapYearDate = new Date(2004, monthNum, dayNum);
    leapYearDate.setYear(newYearNum);
    assertEquals(leapYearDate.getYear(), newYearNum);
    assertEquals(leapYearDate.getMonth(), monthNum + 1);
    assertEquals(leapYearDate.getDate(), dayNum - numDaysInFebInNewYear);
  }

  /** Testing for public void java.util.Date.setMinutes(int). */
  public void testSetMinutes() {
    for (int i = 0; i < 24; i++) {
      Date accum0 = create();
      accum0.setMinutes(i);
      assertEquals(accum0.getMinutes(), i);
    }
  }

  /** Testing for public void java.util.Date.setMonth(int). */
  public void testSetMonth() {
    for (int i = 0; i < 12; i++) {
      // We want to use a fixed date here. If we use the current date, the
      // assertion may fail
      // when the date is the 29th, 30th, or 31st, and we set the month to one
      // which does
      // not have 29, 30, or 31 days in it, respectively.
      Date accum0 = new Date(2006, 12, 1);
      accum0.setMonth(i);
      assertEquals(accum0.getMonth(), i);
    }
  }

  /** Testing for public void java.util.Date.setSeconds(int). */
  public void testSetSeconds() {
    for (int i = 0; i < 24; i++) {
      Date accum0 = create();
      accum0.setSeconds(i);
      assertEquals(accum0.getSeconds(), i);
    }
  }

  /** Testing for public void java.util.Date.setTime(long). */
  public void testSetTime() {
    long[] values = new long[] {-100000000000L, -100L, 0, 100L, 1000000000L};
    for (int i = 0; i < values.length; i++) {
      Date accum0 = create();
      accum0.setTime(values[i]);
      assertEquals(accum0.getTime(), values[i]);
    }
  }

  /**
   * We want to test to see that if the date is Feb 29th (in a leap year) and we
   * set the year to another leap year, that the month and day will be retained.
   */
  public void testSetValidLeapYearForDate() {
    int dayNum = 29;
    int monthNum = 1; // February
    int yearNum = 2004;
    int newYearNum = yearNum + 4;
    Date leapYearDate = new Date(yearNum, monthNum, dayNum);
    leapYearDate.setYear(newYearNum);
    assertEquals(leapYearDate.getYear(), newYearNum);
    assertEquals(leapYearDate.getMonth(), monthNum);
    assertEquals(leapYearDate.getDate(), dayNum);
  }

  /** Testing for public void java.util.Date.setYear(int). */
  public void testSetYear() {
    for (int i = 1880; i < 2030; i++) {
      // We want to use a fixed date here. If we use the current date, the
      // assertion may fail
      // when the date is February 29th, and we set the year to a non-leap year
      Date accum0 = new Date(2006, 12, 01);
      accum0.setYear(i);
      assertEquals(accum0.getYear(), i);
    }
  }

  /** Testing for public java.lang.String java.util.Date.toGMTString(). */
  public void testToGMTString() {

    // We can't rely on the JRE's toString, as it is an implementation detail.
    if (!TestUtils.isJvm()) {
      // /////////////////////////////
      // Past
      // /////////////////////////////
      Date accum1 = create(PAST);
      String a1 = accum1.toGMTString();
      assertEquals("5 Jan 1890 00:00:00 GMT", a1);

      // /////////////////////////////
      // Future
      // /////////////////////////////
      Date accum2 = create(FUTURE);
      String a2 = accum2.toGMTString();
      assertEquals("30 Dec 2030 03:04:05 GMT", a2);
    }
  }

  /** Testing for public java.lang.String java.util.Date.toLocaleString(). */
  public void testToLocaleString() {

    // We can't rely on the JRE's toString, as it is an implementation detail.
    if (!TestUtils.isJvm()) {
      // /////////////////////////////
      // Past
      // /////////////////////////////
      Date accum1 = create(PAST);
      String a1 = accum1.toLocaleString();
      assertTrue(a1.indexOf("1890") != -1);
      // /////////////////////////////
      // Future
      // /////////////////////////////
      Date accum2 = create(FUTURE);
      String a2 = accum2.toLocaleString();
      assertTrue(a2.indexOf("2030") != -1);
    }
  }

  /** Date docs specify an exact format for toString(). */
  public void testToString() {

    // We can't rely on the JRE's toString, as it is an implementation detail.
    if (!TestUtils.isJvm()) {
      // /////////////////////////////
      // Past
      // /////////////////////////////
      Date d = create(PAST);
      String s = d.toString();

      assertTrue("Bad format " + s, s.matches(TO_STRING_PATTERN));
      assertEquals("Parsing returned unequal dates from " + s, d, new Date(
          Date.parse(s)));

      // /////////////////////////////
      // Future
      // /////////////////////////////
      d = create(FUTURE);
      s = d.toString();

      assertTrue("Bad format " + s, s.matches(TO_STRING_PATTERN));
      assertEquals("Parsing returned unequal dates from " + s, d, new Date(
          Date.parse(s)));
    }
  }

  /** Testing for public static long java.util.Date.UTC(int,int,int,int,int,int). */
  public void testUTC() {

    // /////////////////////////////
    // Current
    // /////////////////////////////
    Date accum0 = create();
    int arg10 = 0;
    int arg11 = 0;
    int arg12 = 0;
    int arg13 = 0;
    int arg14 = 0;
    int arg15 = 0;
    long a0 = accum0.UTC(arg10, arg11, arg12, arg13, arg14, arg15);

    // /////////////////////////////
    // Past
    // /////////////////////////////
    Date accum1 = create(PAST);
    int arg20 = 0;
    int arg21 = 0;
    int arg22 = 0;
    int arg23 = 0;
    int arg24 = 0;
    int arg25 = 0;
    long a1 = accum1.UTC(arg20, arg21, arg22, arg23, arg24, arg25);

    // /////////////////////////////
    // Future
    // /////////////////////////////
    Date accum2 = create(FUTURE);
    int arg30 = 0;
    int arg31 = 0;
    int arg32 = 0;
    int arg33 = 0;
    int arg34 = 0;
    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;
  }

  /**
   * Check if daylight saving time occurs on the date.
   *
   * @param date the date to check
   * @return true if DST occurs on the date, false if not
   */
  private boolean isDst(Date date) {
    int[] monthDayHour = new int[3];
    if (!findClockForwardTime(date.getYear() + 1900, monthDayHour)) {
      return false;
    }
    return monthDayHour[0] == date.getMonth()
        && monthDayHour[1] == date.getDate();
  }

  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(normalizeHour(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(normalizeHour(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(normalizeHour(hour + 1), d.getHours());

    // Test day change
    d = new Date(2009 - 1900, month, day + 1, hour, 0, 0);
    assertEquals(hour, d.getHours());
    d.setDate(day);
    assertEquals(normalizeHour(hour + 1), d.getHours());

    // Test hour setting
    d = new Date(2009 - 1900, month, day, hour + 2, 0, 0);
    assertEquals(normalizeHour(hour + 2), d.getHours());
    d.setHours(hour);
    assertEquals(normalizeHour(hour + 1), d.getHours());

    // Test changing hour by minutes = +- 60
    d = new Date(2009 - 1900, month, day, hour + 2, 0, 0);
    assertEquals(normalizeHour(hour + 2), d.getHours());
    d.setMinutes(-60);
    assertEquals(normalizeHour(hour + 1), d.getHours());

    d = new Date(2009 - 1900, month, day, hour - 1, 0, 0);
    assertEquals(normalizeHour(hour - 1), d.getHours());
    d.setMinutes(60);
    assertEquals(normalizeHour(hour + 1), d.getHours());

    // Test changing hour by minutes = +- 120
    d = new Date(2009 - 1900, month, day, hour + 2, 0, 0);
    assertEquals(normalizeHour(hour + 2), d.getHours());
    d.setMinutes(-120);
    assertEquals(normalizeHour(hour + 1), d.getHours());

    d = new Date(2009 - 1900, month, day, hour - 2, 0, 0);
    assertEquals(normalizeHour(hour - 2), d.getHours());
    d.setMinutes(120);
    assertEquals(normalizeHour(hour + 1), d.getHours());

    // Test changing hour by seconds = +- 3600
    d = new Date(2009 - 1900, month, day, hour + 2, 0, 0);
    assertEquals(normalizeHour(hour + 2), d.getHours());
    d.setSeconds(-3600);
    assertEquals(normalizeHour(hour + 1), d.getHours());

    d = new Date(2009 - 1900, month, day, hour - 1, 0, 0);
    assertEquals(normalizeHour(hour - 1), d.getHours());
    d.setSeconds(3600);
    assertEquals(normalizeHour(hour + 1), d.getHours());

    // Test changing hour by seconds = +- 7200
    d = new Date(2009 - 1900, month, day, hour + 2, 0, 0);
    assertEquals(normalizeHour(hour + 2), d.getHours());
    d.setSeconds(-7200);
    assertEquals(normalizeHour(hour + 1), d.getHours());

    d = new Date(2009 - 1900, month, day, hour - 2, 0, 0);
    assertEquals(normalizeHour(hour - 2), d.getHours());
    d.setSeconds(7200);
    assertEquals(normalizeHour(hour + 1), d.getHours());

    d = new Date(2009 - 1900, month, day, hour + 2, 0, 0);
    d.setHours(hour);
    d.setMinutes(30);
    assertEquals(normalizeHour(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(normalizeHour(hour + 1), d.getHours());
    assertEquals(30, d.getMinutes());
  }

  int normalizeHour(int i) {
    return (i + 24) % 24;
  }

  Date create() {
    return (Date) theDate.clone();
  }

  Date create(String s) {
    if (s.equals(FUTURE)) {
      return new Date("12/30/2030 3:4:5 GMT");
    } else if (s.equals(PAST)) {
      return new Date("1/5/1890 GMT");
    } else {
      return (Date) theDate.clone();
    }
  }

  private String createString(String s) {
    if (s.equals(FUTURE)) {
      return "12/30/2030 3:4:5 GMT";
    } else if (s.equals(PAST)) {
      return "1/1/1890 GMT";
    } else {
      return theDate.toLocaleString();
    }
  }

  private long roundToDay(long accum0) {
    return accum0 >> DAY_MILLISECONDS_SHIFT << DAY_MILLISECONDS_SHIFT;
  }
}
