/* * Copyright 2014-2016 Groupon, Inc * Copyright 2014-2016 The Billing Project, LLC * * The Billing Project licenses this file to you 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 org.killbill.billing.util.callcontext; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.joda.time.LocalDate; import org.joda.time.format.DateTimeFormatter; import org.joda.time.format.ISODateTimeFormat; import org.killbill.billing.account.api.Account; import org.killbill.billing.callcontext.TimeAwareContext; import org.killbill.billing.mock.MockAccountBuilder; import org.killbill.billing.util.UtilTestSuiteNoDB; import org.killbill.billing.util.account.AccountDateTimeUtils; import org.testng.Assert; import org.testng.annotations.Test; import com.google.common.collect.ImmutableList; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; // // There are two categories of tests, one that test the offset calculation and one that calculates // how to get a DateTime from a LocalDate (in account time zone) // // Tests {1, 2, 3} use an account timezone with a negative offset (-8) and tests {A, B, C} use an account timezone with a positive offset (+8) // public class TestTimeAwareContext extends UtilTestSuiteNoDB { private final DateTimeFormatter DATE_TIME_FORMATTER = ISODateTimeFormat.dateTimeParser(); private final String effectiveDateTime1 = "2012-01-20T07:30:42.000Z"; private final String effectiveDateTime2 = "2012-01-20T08:00:00.000Z"; private final String effectiveDateTime3 = "2012-01-20T08:45:33.000Z"; private final String effectiveDateTimeA = "2012-01-20T16:30:42.000Z"; private final String effectiveDateTimeB = "2012-01-20T16:00:00.000Z"; private final String effectiveDateTimeC = "2012-01-20T15:30:42.000Z"; @Test(groups = "fast") public void testComputeUTCDateTimeFromLocalDate1() { final DateTime effectiveDateTime = DATE_TIME_FORMATTER.parseDateTime(effectiveDateTime1); final DateTimeZone timeZone = DateTimeZone.forOffsetHours(-8); refreshCallContext(effectiveDateTime, timeZone); final LocalDate endDate = new LocalDate(2013, 01, 19); final DateTime endDateTimeInUTC = internalCallContext.toUTCDateTime(endDate); assertTrue(endDateTimeInUTC.compareTo(effectiveDateTime.plusYears(1)) == 0); } @Test(groups = "fast") public void testComputeUTCDateTimeFromLocalDate2() { final DateTime effectiveDateTime = DATE_TIME_FORMATTER.parseDateTime(effectiveDateTime2); final DateTimeZone timeZone = DateTimeZone.forOffsetHours(-8); refreshCallContext(effectiveDateTime, timeZone); final LocalDate endDate = new LocalDate(2013, 01, 20); final DateTime endDateTimeInUTC = internalCallContext.toUTCDateTime(endDate); assertTrue(endDateTimeInUTC.compareTo(effectiveDateTime.plusYears(1)) == 0); } @Test(groups = "fast") public void testComputeUTCDateTimeFromLocalDate3() { final DateTime effectiveDateTime = DATE_TIME_FORMATTER.parseDateTime(effectiveDateTime3); final DateTimeZone timeZone = DateTimeZone.forOffsetHours(-8); refreshCallContext(effectiveDateTime, timeZone); final LocalDate endDate = new LocalDate(2013, 01, 20); final DateTime endDateTimeInUTC = internalCallContext.toUTCDateTime(endDate); assertTrue(endDateTimeInUTC.compareTo(effectiveDateTime.plusYears(1)) == 0); } @Test(groups = "fast") public void testComputeUTCDateTimeFromLocalDateA() { final DateTime effectiveDateTime = DATE_TIME_FORMATTER.parseDateTime(effectiveDateTimeA); final DateTimeZone timeZone = DateTimeZone.forOffsetHours(8); refreshCallContext(effectiveDateTime, timeZone); final LocalDate endDate = new LocalDate(2013, 01, 21); final DateTime endDateTimeInUTC = internalCallContext.toUTCDateTime(endDate); assertTrue(endDateTimeInUTC.compareTo(effectiveDateTime.plusYears(1)) == 0); } @Test(groups = "fast") public void testComputeUTCDateTimeFromLocalDateB() { final DateTime effectiveDateTime = DATE_TIME_FORMATTER.parseDateTime(effectiveDateTimeB); final DateTimeZone timeZone = DateTimeZone.forOffsetHours(8); refreshCallContext(effectiveDateTime, timeZone); final LocalDate endDate = new LocalDate(2013, 01, 21); final DateTime endDateTimeInUTC = internalCallContext.toUTCDateTime(endDate); assertTrue(endDateTimeInUTC.compareTo(effectiveDateTime.plusYears(1)) == 0); } @Test(groups = "fast") public void testComputeUTCDateTimeFromLocalDateC() { final DateTime effectiveDateTime = DATE_TIME_FORMATTER.parseDateTime(effectiveDateTimeC); final DateTimeZone timeZone = DateTimeZone.forOffsetHours(8); refreshCallContext(effectiveDateTime, timeZone); final LocalDate endDate = new LocalDate(2013, 01, 20); final DateTime endDateTimeInUTC = internalCallContext.toUTCDateTime(endDate); assertTrue(endDateTimeInUTC.compareTo(effectiveDateTime.plusYears(1)) == 0); } @Test(groups = "fast") public void testComputeTargetDateWithDayLightSaving() { final DateTime dateTime1 = new DateTime("2015-01-01T08:01:01.000Z"); final DateTime dateTime2 = new DateTime("2015-09-01T08:01:01.000Z"); final DateTime dateTime3 = new DateTime("2015-12-01T08:01:01.000Z"); // Alaska Standard Time final DateTimeZone timeZone = DateTimeZone.forID("America/Juneau"); // Time zone is AKDT (UTC-8h) between March and November final DateTime referenceDateTimeWithDST = new DateTime("2015-09-01T08:01:01.000Z"); refreshCallContext(referenceDateTimeWithDST, timeZone); assertEquals(internalCallContext.toLocalDate(dateTime1), new LocalDate("2015-01-01")); assertEquals(internalCallContext.toLocalDate(dateTime2), new LocalDate("2015-09-01")); assertEquals(internalCallContext.toLocalDate(dateTime3), new LocalDate("2015-12-01")); // Time zone is AKST (UTC-9h) otherwise final DateTime referenceDateTimeWithoutDST = new DateTime("2015-02-01T08:01:01.000Z"); refreshCallContext(referenceDateTimeWithoutDST, timeZone); assertEquals(internalCallContext.toLocalDate(dateTime1), new LocalDate("2014-12-31")); assertEquals(internalCallContext.toLocalDate(dateTime2), new LocalDate("2015-08-31")); assertEquals(internalCallContext.toLocalDate(dateTime3), new LocalDate("2015-11-30")); } @Test(groups = "fast") public void testIdempotencyOfDatesManipulation() throws Exception { final ImmutableList.Builder<DateTimeZone> dateTimeZoneBuilder = ImmutableList.<DateTimeZone>builder(); dateTimeZoneBuilder.add(DateTimeZone.forID("HST")); dateTimeZoneBuilder.add(DateTimeZone.forID("PST8PDT")); dateTimeZoneBuilder.add(DateTimeZone.forID("MST")); dateTimeZoneBuilder.add(DateTimeZone.forID("CST6CDT")); dateTimeZoneBuilder.add(DateTimeZone.forID("EST")); dateTimeZoneBuilder.add(DateTimeZone.forID("Brazil/DeNoronha")); dateTimeZoneBuilder.add(DateTimeZone.forID("UTC")); dateTimeZoneBuilder.add(DateTimeZone.forID("CET")); dateTimeZoneBuilder.add(DateTimeZone.forID("Europe/Istanbul")); dateTimeZoneBuilder.add(DateTimeZone.forID("Singapore")); dateTimeZoneBuilder.add(DateTimeZone.forID("Japan")); dateTimeZoneBuilder.add(DateTimeZone.forID("Australia/Sydney")); dateTimeZoneBuilder.add(DateTimeZone.forID("Pacific/Tongatapu")); final Iterable<DateTimeZone> dateTimeZones = dateTimeZoneBuilder.build(); final ImmutableList.Builder<DateTime> referenceDateTimeBuilder = ImmutableList.<DateTime>builder(); referenceDateTimeBuilder.add(new DateTime(2012, 1, 1, 1, 1, 1, DateTimeZone.UTC)); referenceDateTimeBuilder.add(new DateTime(2012, 3, 15, 12, 42, 0, DateTimeZone.forID("PST8PDT"))); referenceDateTimeBuilder.add(new DateTime(2012, 11, 15, 12, 42, 0, DateTimeZone.forID("PST8PDT"))); final Iterable<DateTime> referenceDateTimes = referenceDateTimeBuilder.build(); DateTime currentDateTime = new DateTime(2015, 1, 1, 1, 1, DateTimeZone.UTC); final DateTime endDateTime = new DateTime(2020, 1, 1, 1, 1, DateTimeZone.UTC); while (currentDateTime.compareTo(endDateTime) <= 0) { for (final DateTimeZone dateTimeZone : dateTimeZones) { for (final DateTime referenceDateTime : referenceDateTimes) { final TimeAwareContext timeAwareContext = new TimeAwareContext(dateTimeZone, referenceDateTime); final LocalDate computedLocalDate = timeAwareContext.toLocalDate(currentDateTime); final DateTime computedDateTime = timeAwareContext.toUTCDateTime(computedLocalDate); final LocalDate computedLocalDate2 = timeAwareContext.toLocalDate(computedDateTime); final String msg = String.format("currentDateTime=%s, localDate=%s, dateTime=%s, dateTimeZone=%s, referenceDateTime=%s", currentDateTime, computedLocalDate, computedDateTime, dateTimeZone, referenceDateTime); Assert.assertEquals(computedLocalDate2, computedLocalDate, msg); } } currentDateTime = currentDateTime.plusHours(1); } } private void refreshCallContext(final DateTime effectiveDateTime, final DateTimeZone timeZone) { final Account account = new MockAccountBuilder().timeZone(timeZone) .createdDate(effectiveDateTime) .build(); internalCallContext.setFixedOffsetTimeZone(AccountDateTimeUtils.getFixedOffsetTimeZone(account)); internalCallContext.setReferenceTime(AccountDateTimeUtils.getReferenceDateTime(account)); } }