public void testUTCIntervalRounding() {
    Rounding tzRounding = Rounding.builder(TimeValue.timeValueHours(12)).build();
    DateTimeZone tz = DateTimeZone.UTC;
    assertThat(
        tzRounding.round(time("2009-02-03T01:01:01")),
        isDate(time("2009-02-03T00:00:00.000Z"), tz));
    assertThat(
        tzRounding.nextRoundingValue(time("2009-02-03T00:00:00.000Z")),
        isDate(time("2009-02-03T12:00:00.000Z"), tz));
    assertThat(
        tzRounding.round(time("2009-02-03T13:01:01")),
        isDate(time("2009-02-03T12:00:00.000Z"), tz));
    assertThat(
        tzRounding.nextRoundingValue(time("2009-02-03T12:00:00.000Z")),
        isDate(time("2009-02-04T00:00:00.000Z"), tz));

    tzRounding = Rounding.builder(TimeValue.timeValueHours(48)).build();
    assertThat(
        tzRounding.round(time("2009-02-03T01:01:01")),
        isDate(time("2009-02-03T00:00:00.000Z"), tz));
    assertThat(
        tzRounding.nextRoundingValue(time("2009-02-03T00:00:00.000Z")),
        isDate(time("2009-02-05T00:00:00.000Z"), tz));
    assertThat(
        tzRounding.round(time("2009-02-05T13:01:01")),
        isDate(time("2009-02-05T00:00:00.000Z"), tz));
    assertThat(
        tzRounding.nextRoundingValue(time("2009-02-05T00:00:00.000Z")),
        isDate(time("2009-02-07T00:00:00.000Z"), tz));
  }
  public void testTimeUnitRoundingDST() {
    Rounding tzRounding;
    // testing savings to non savings switch
    DateTimeZone cet = DateTimeZone.forID("CET");
    tzRounding = Rounding.builder(DateTimeUnit.HOUR_OF_DAY).timeZone(cet).build();
    assertThat(
        tzRounding.round(time("2014-10-26T01:01:01", cet)),
        isDate(time("2014-10-26T01:00:00+02:00"), cet));
    assertThat(
        tzRounding.nextRoundingValue(time("2014-10-26T01:00:00", cet)),
        isDate(time("2014-10-26T02:00:00+02:00"), cet));
    assertThat(
        tzRounding.nextRoundingValue(time("2014-10-26T02:00:00", cet)),
        isDate(time("2014-10-26T02:00:00+01:00"), cet));

    // testing non savings to savings switch
    tzRounding = Rounding.builder(DateTimeUnit.HOUR_OF_DAY).timeZone(cet).build();
    assertThat(
        tzRounding.round(time("2014-03-30T01:01:01", cet)),
        isDate(time("2014-03-30T01:00:00+01:00"), cet));
    assertThat(
        tzRounding.nextRoundingValue(time("2014-03-30T01:00:00", cet)),
        isDate(time("2014-03-30T03:00:00", cet), cet));
    assertThat(
        tzRounding.nextRoundingValue(time("2014-03-30T03:00:00", cet)),
        isDate(time("2014-03-30T04:00:00", cet), cet));

    // testing non savings to savings switch (America/Chicago)
    DateTimeZone chg = DateTimeZone.forID("America/Chicago");
    Rounding tzRounding_utc =
        Rounding.builder(DateTimeUnit.HOUR_OF_DAY).timeZone(DateTimeZone.UTC).build();
    assertThat(
        tzRounding.round(time("2014-03-09T03:01:01", chg)),
        isDate(time("2014-03-09T03:00:00", chg), chg));

    Rounding tzRounding_chg = Rounding.builder(DateTimeUnit.HOUR_OF_DAY).timeZone(chg).build();
    assertThat(
        tzRounding_chg.round(time("2014-03-09T03:01:01", chg)),
        isDate(time("2014-03-09T03:00:00", chg), chg));

    // testing savings to non savings switch 2013 (America/Chicago)
    assertThat(
        tzRounding_utc.round(time("2013-11-03T06:01:01", chg)),
        isDate(time("2013-11-03T06:00:00", chg), chg));
    assertThat(
        tzRounding_chg.round(time("2013-11-03T06:01:01", chg)),
        isDate(time("2013-11-03T06:00:00", chg), chg));

    // testing savings to non savings switch 2014 (America/Chicago)
    assertThat(
        tzRounding_utc.round(time("2014-11-02T06:01:01", chg)),
        isDate(time("2014-11-02T06:00:00", chg), chg));
    assertThat(
        tzRounding_chg.round(time("2014-11-02T06:01:01", chg)),
        isDate(time("2014-11-02T06:00:00", chg), chg));
  }
 /** test for #10025, strict local to UTC conversion can cause joda exceptions on DST start */
 public void testLenientConversionDST() {
   DateTimeZone tz = DateTimeZone.forID("America/Sao_Paulo");
   long start = time("2014-10-18T20:50:00.000", tz);
   long end = time("2014-10-19T01:00:00.000", tz);
   Rounding tzRounding = new Rounding.TimeUnitRounding(DateTimeUnit.MINUTES_OF_HOUR, tz);
   Rounding dayTzRounding = new Rounding.TimeIntervalRounding(60000, tz);
   for (long time = start; time < end; time = time + 60000) {
     assertThat(tzRounding.nextRoundingValue(time), greaterThan(time));
     assertThat(dayTzRounding.nextRoundingValue(time), greaterThan(time));
   }
 }
  public void testTimeRounding() {
    // hour unit
    DateTimeZone tz = DateTimeZone.forOffsetHours(-2);
    Rounding tzRounding = Rounding.builder(DateTimeUnit.HOUR_OF_DAY).timeZone(tz).build();
    assertThat(tzRounding.round(0), equalTo(0L));
    assertThat(tzRounding.nextRoundingValue(0L), equalTo(TimeValue.timeValueHours(1L).getMillis()));

    assertThat(
        tzRounding.round(time("2009-02-03T01:01:01")), isDate(time("2009-02-03T01:00:00"), tz));
    assertThat(
        tzRounding.nextRoundingValue(time("2009-02-03T01:00:00")),
        isDate(time("2009-02-03T02:00:00"), tz));
  }
  /**
   * perform a number on assertions and checks on {@link TimeUnitRounding} intervals
   *
   * @param rounded the expected low end of the rounding interval
   * @param unrounded a date in the interval to be checked for rounding
   * @param nextRoundingValue the expected upper end of the rounding interval
   * @param rounding the rounding instance
   */
  private static void assertInterval(
      long rounded, long unrounded, long nextRoundingValue, Rounding rounding, DateTimeZone tz) {
    assert rounded <= unrounded && unrounded <= nextRoundingValue;
    assertThat("rounding should be idempotent ", rounding.round(rounded), isDate(rounded, tz));
    assertThat(
        "rounded value smaller or equal than unrounded" + rounding,
        rounded,
        lessThanOrEqualTo(unrounded));
    assertThat(
        "values less than rounded should round further down" + rounding,
        rounding.round(rounded - 1),
        lessThan(rounded));
    assertThat(
        "nextRounding value should be greater than date" + rounding,
        nextRoundingValue,
        greaterThan(unrounded));
    assertThat(
        "nextRounding value should be a rounded date",
        rounding.round(nextRoundingValue),
        isDate(nextRoundingValue, tz));
    assertThat(
        "values above nextRounding should round down there",
        rounding.round(nextRoundingValue + 1),
        isDate(nextRoundingValue, tz));

    long dateBetween = dateBetween(rounded, nextRoundingValue);
    assertThat(
        "dateBetween should round down to roundedDate",
        rounding.round(dateBetween),
        isDate(rounded, tz));
    assertThat(
        "dateBetween should round up to nextRoundingValue",
        rounding.nextRoundingValue(dateBetween),
        isDate(nextRoundingValue, tz));
  }
  /** test TimeIntervalRounding, (interval &lt; 12h) with time zone shift */
  public void testTimeIntervalRounding() {
    DateTimeZone tz = DateTimeZone.forOffsetHours(-1);
    Rounding tzRounding = Rounding.builder(TimeValue.timeValueHours(6)).timeZone(tz).build();
    assertThat(
        tzRounding.round(time("2009-02-03T00:01:01")),
        isDate(time("2009-02-02T19:00:00.000Z"), tz));
    assertThat(
        tzRounding.nextRoundingValue(time("2009-02-02T19:00:00.000Z")),
        isDate(time("2009-02-03T01:00:00.000Z"), tz));

    assertThat(
        tzRounding.round(time("2009-02-03T13:01:01")),
        isDate(time("2009-02-03T13:00:00.000Z"), tz));
    assertThat(
        tzRounding.nextRoundingValue(time("2009-02-03T13:00:00.000Z")),
        isDate(time("2009-02-03T19:00:00.000Z"), tz));
  }
  public void testUTCTimeUnitRounding() {
    Rounding tzRounding = Rounding.builder(DateTimeUnit.MONTH_OF_YEAR).build();
    DateTimeZone tz = DateTimeZone.UTC;
    assertThat(
        tzRounding.round(time("2009-02-03T01:01:01")),
        isDate(time("2009-02-01T00:00:00.000Z"), tz));
    assertThat(
        tzRounding.nextRoundingValue(time("2009-02-01T00:00:00.000Z")),
        isDate(time("2009-03-01T00:00:00.000Z"), tz));

    tzRounding = Rounding.builder(DateTimeUnit.WEEK_OF_WEEKYEAR).build();
    assertThat(
        tzRounding.round(time("2012-01-10T01:01:01")),
        isDate(time("2012-01-09T00:00:00.000Z"), tz));
    assertThat(
        tzRounding.nextRoundingValue(time("2012-01-09T00:00:00.000Z")),
        isDate(time("2012-01-16T00:00:00.000Z"), tz));
  }
  public void testDayRounding() {
    int timezoneOffset = -2;
    Rounding tzRounding =
        Rounding.builder(DateTimeUnit.DAY_OF_MONTH)
            .timeZone(DateTimeZone.forOffsetHours(timezoneOffset))
            .build();
    assertThat(
        tzRounding.round(0), equalTo(0L - TimeValue.timeValueHours(24 + timezoneOffset).millis()));
    assertThat(
        tzRounding.nextRoundingValue(0L - TimeValue.timeValueHours(24 + timezoneOffset).millis()),
        equalTo(0L - TimeValue.timeValueHours(timezoneOffset).millis()));

    DateTimeZone tz = DateTimeZone.forID("-08:00");
    tzRounding = Rounding.builder(DateTimeUnit.DAY_OF_MONTH).timeZone(tz).build();
    assertThat(
        tzRounding.round(time("2012-04-01T04:15:30Z")), isDate(time("2012-03-31T08:00:00Z"), tz));

    tzRounding = Rounding.builder(DateTimeUnit.MONTH_OF_YEAR).timeZone(tz).build();
    assertThat(
        tzRounding.round(time("2012-04-01T04:15:30Z")), equalTo(time("2012-03-01T08:00:00Z")));

    // date in Feb-3rd, but still in Feb-2nd in -02:00 timezone
    tz = DateTimeZone.forID("-02:00");
    tzRounding = Rounding.builder(DateTimeUnit.DAY_OF_MONTH).timeZone(tz).build();
    assertThat(
        tzRounding.round(time("2009-02-03T01:01:01")), isDate(time("2009-02-02T02:00:00"), tz));
    assertThat(
        tzRounding.nextRoundingValue(time("2009-02-02T02:00:00")),
        isDate(time("2009-02-03T02:00:00"), tz));

    // date in Feb-3rd, also in -02:00 timezone
    tzRounding = Rounding.builder(DateTimeUnit.DAY_OF_MONTH).timeZone(tz).build();
    assertThat(
        tzRounding.round(time("2009-02-03T02:01:01")), isDate(time("2009-02-03T02:00:00"), tz));
    assertThat(
        tzRounding.nextRoundingValue(time("2009-02-03T02:00:00")),
        isDate(time("2009-02-04T02:00:00"), tz));
  }
  /**
   * Randomized test on TimeUnitRounding. Test uses random {@link DateTimeUnit} and {@link
   * DateTimeZone} and often (50% of the time) chooses test dates that are exactly on or close to
   * offset changes (e.g. DST) in the chosen time zone.
   *
   * <p>It rounds the test date down and up and performs various checks on the rounding unit
   * interval that is defined by this. Assumptions tested are described in {@link
   * #assertInterval(long, long, long, Rounding, DateTimeZone)}
   */
  public void testRoundingRandom() {
    for (int i = 0; i < 1000; ++i) {
      DateTimeUnit timeUnit = randomTimeUnit();
      DateTimeZone tz = randomDateTimeZone();
      Rounding rounding = new Rounding.TimeUnitRounding(timeUnit, tz);
      long date =
          Math.abs(
              randomLong()
                  % (2 * (long) 10e11)); // 1970-01-01T00:00:00Z - 2033-05-18T05:33:20.000+02:00
      long unitMillis = timeUnit.field(tz).getDurationField().getUnitMillis();
      if (randomBoolean()) {
        nastyDate(date, tz, unitMillis);
      }
      final long roundedDate = rounding.round(date);
      final long nextRoundingValue = rounding.nextRoundingValue(roundedDate);

      assertInterval(roundedDate, date, nextRoundingValue, rounding, tz);

      // check correct unit interval width for units smaller than a day, they should be fixed size
      // except for transitions
      if (unitMillis <= DateTimeConstants.MILLIS_PER_DAY) {
        // if the interval defined didn't cross timezone offset transition, it should cover
        // unitMillis width
        if (tz.getOffset(roundedDate - 1) == tz.getOffset(nextRoundingValue + 1)) {
          assertThat(
              "unit interval width not as expected for ["
                  + timeUnit
                  + "], ["
                  + tz
                  + "] at "
                  + new DateTime(roundedDate),
              nextRoundingValue - roundedDate,
              equalTo(unitMillis));
        }
      }
    }
  }
  /** randomized test on {@link TimeIntervalRounding} with random interval and time zone offsets */
  public void testIntervalRoundingRandom() {
    for (int i = 0; i < 1000; i++) {
      TimeUnit unit = randomFrom(new TimeUnit[] {TimeUnit.MINUTES, TimeUnit.HOURS, TimeUnit.DAYS});
      long interval = unit.toMillis(randomIntBetween(1, 365));
      DateTimeZone tz = randomDateTimeZone();
      Rounding rounding = new Rounding.TimeIntervalRounding(interval, tz);
      long mainDate =
          Math.abs(
              randomLong()
                  % (2 * (long) 10e11)); // 1970-01-01T00:00:00Z - 2033-05-18T05:33:20.000+02:00
      if (randomBoolean()) {
        mainDate = nastyDate(mainDate, tz, interval);
      }
      // check two intervals around date
      long previousRoundedValue = Long.MIN_VALUE;
      for (long date = mainDate - 2 * interval;
          date < mainDate + 2 * interval;
          date += interval / 2) {
        try {
          final long roundedDate = rounding.round(date);
          final long nextRoundingValue = rounding.nextRoundingValue(roundedDate);
          assertThat(
              "Rounding should be idempotent", roundedDate, equalTo(rounding.round(roundedDate)));
          assertThat(
              "Rounded value smaller or equal than unrounded",
              roundedDate,
              lessThanOrEqualTo(date));
          assertThat(
              "Values smaller than rounded value should round further down",
              rounding.round(roundedDate - 1),
              lessThan(roundedDate));
          assertThat(
              "Rounding should be >= previous rounding value",
              roundedDate,
              greaterThanOrEqualTo(previousRoundedValue));

          if (tz.isFixed()) {
            assertThat(
                "NextRounding value should be greater than date",
                nextRoundingValue,
                greaterThan(roundedDate));
            assertThat(
                "NextRounding value should be interval from rounded value",
                nextRoundingValue - roundedDate,
                equalTo(interval));
            assertThat(
                "NextRounding value should be a rounded date",
                nextRoundingValue,
                equalTo(rounding.round(nextRoundingValue)));
          }
          previousRoundedValue = roundedDate;
        } catch (AssertionError e) {
          logger.error(
              "Rounding error at {}, timezone {}, interval: {},",
              new DateTime(date, tz),
              tz,
              interval);
          throw e;
        }
      }
    }
  }