Commit
Instead of relying on a fixed +1/-1 day offset, ignore completely DST changes for LocalDate <-> DateTime transformations. TestDateAndTimeZoneContext changes: * Remove computeOffsetFromUtc tests (functionality removed) * Add more DST tests TestWithTimeZones changes: * The cancellation date will now use the PDT timezone (one hour earlier) to compute the effective cancellation datetime Signed-off-by: Pierre-Alexandre Meyer <pierre@mouraf.org>
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,6 +27,9 @@ | |
// TODO Cache the accountTimeZone, reference time and clock in the context | ||
public class TimeAwareContext { | ||
|
||
/// Generic functions | ||
/// TODO Move to ClockUtil | ||
|
||
// From JDK to Joda (see http://www.joda.org/joda-time/userguide.html#JDK_Interoperability) | ||
public DateTime toUTCDateTime(final Date date) { | ||
return toUTCDateTime(new DateTime(date)); | ||
|
@@ -37,31 +40,43 @@ public DateTime toUTCDateTime(final DateTime dateTime) { | |
return toDateTime(dateTime, DateTimeZone.UTC); | ||
} | ||
|
||
// Create a DateTime object using the specified reference time and timezone (usually, the one on the account) | ||
public DateTime toUTCDateTime(final LocalDate localDate, final DateTime referenceDateTime, final DateTimeZone accountTimeZone) { | ||
return toUTCDateTime(toDateTime(localDate, referenceDateTime, accountTimeZone)); | ||
// Create a DateTime object using the specified timezone (usually, the one on the account) | ||
public DateTime toDateTime(final DateTime dateTime, final DateTimeZone accountTimeZone) { | ||
return dateTime.toDateTime(accountTimeZone); | ||
} | ||
|
||
/// DateTime <-> LocalDate transformations | ||
|
||
// Create a DateTime object using the specified reference time and timezone (usually, the one on the account) | ||
public DateTime toDateTime(final LocalDate localDate, final DateTime referenceDateTime, final DateTimeZone accountTimeZone) { | ||
final LocalTime referenceLocalTime = toDateTime(referenceDateTime, accountTimeZone).toLocalTime(); | ||
public DateTime toUTCDateTime(final LocalDate localDate, final DateTime referenceDateTime, final DateTimeZone accountTimeZone) { | ||
final DateTimeZone normalizedAccountTimezone = getNormalizedAccountTimezone(referenceDateTime, accountTimeZone); | ||
|
||
final LocalTime referenceLocalTime = toDateTime(referenceDateTime, normalizedAccountTimezone).toLocalTime(); | ||
|
||
final DateTime targetDateTime = new DateTime(localDate.getYear(), | ||
localDate.getMonthOfYear(), | ||
localDate.getDayOfMonth(), | ||
referenceLocalTime.getHourOfDay(), | ||
referenceLocalTime.getMinuteOfHour(), | ||
referenceLocalTime.getSecondOfMinute(), | ||
normalizedAccountTimezone); | ||
|
||
return new DateTime(localDate.getYear(), | ||
localDate.getMonthOfYear(), | ||
localDate.getDayOfMonth(), | ||
referenceLocalTime.getHourOfDay(), | ||
referenceLocalTime.getMinuteOfHour(), | ||
referenceLocalTime.getSecondOfMinute(), | ||
accountTimeZone); | ||
return toUTCDateTime(targetDateTime); | ||
} | ||
|
||
// Create a DateTime object using the specified timezone (usually, the one on the account) | ||
public DateTime toDateTime(final DateTime dateTime, final DateTimeZone accountTimeZone) { | ||
return dateTime.toDateTime(accountTimeZone); | ||
// Create a LocalDate object using the specified timezone (usually, the one on the account), respecting the offset at the time of the referenceDateTime | ||
public LocalDate toLocalDate(final DateTime dateTime, final DateTime referenceDateTime, final DateTimeZone accountTimeZone) { | ||
final DateTimeZone normalizedAccountTimezone = getNormalizedAccountTimezone(referenceDateTime, accountTimeZone); | ||
return new LocalDate(dateTime, normalizedAccountTimezone); | ||
} | ||
|
||
// Create a LocalDate object using the specified timezone (usually, the one on the account) | ||
public LocalDate toLocalDate(final DateTime dateTime, final DateTimeZone accountTimeZone) { | ||
return new LocalDate(dateTime, accountTimeZone); | ||
private DateTimeZone getNormalizedAccountTimezone(final DateTime referenceDateTime, final DateTimeZone accountTimeZone) { | ||
// Check if DST was in effect at the reference date time | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong. |
||
final boolean shouldUseDST = !accountTimeZone.isStandardOffset(referenceDateTime.getMillis()); | ||
if (shouldUseDST) { | ||
return DateTimeZone.forOffsetMillis(accountTimeZone.getOffset(referenceDateTime.getMillis())); | ||
} else { | ||
return DateTimeZone.forOffsetMillis(accountTimeZone.getStandardOffset(referenceDateTime.getMillis())); | ||
} | ||
} | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
pierre
Author
Member
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -107,7 +107,7 @@ public void testWithDayLightSaving() throws Exception { | |
} | ||
} | ||
|
||
// Verify cancellation logic when we exit daylight savind period | ||
// Verify cancellation logic when we exit daylight saving period | ||
@Test(groups = "slow") | ||
public void testCancellationFrom_PDT_to_PST() throws Exception { | ||
// Start with a date in daylight saving period (PDT) and make sure we use a time of 7 hour so that we we reach standard time (PST) | ||
|
@@ -140,22 +140,14 @@ public void testCancellationFrom_PDT_to_PST() throws Exception { | |
// Cancel the next month specifying just a LocalDate | ||
final LocalDate cancellationDate = new LocalDate("2015-12-01", tz); | ||
entitlement = entitlement.cancelEntitlementWithDate(cancellationDate, true, ImmutableList.<PluginProperty>of(), callContext); | ||
assertListenerStatus(); | ||
|
||
// Verify first entitlement is correctly cancelled on the right date | ||
Assert.assertEquals(entitlement.getEffectiveEndDate(), cancellationDate); | ||
|
||
busHandler.pushExpectedEvent(NextEvent.NULL_INVOICE); | ||
// We move the clock to the date of the next invoice notification 2015-12-01 07:01:01 (invoice is using a fixed offset of 7 hours and all billing events are converted using that offset) | ||
clock.setTime(new DateTime("2015-12-01T07:01:02")); | ||
assertListenerStatus(); | ||
|
||
// We now move the clock to the date of the cancellation (one hour later), which match the cancellation day from the client point of view | ||
// | ||
// For the curious reader, the reason why the time end up being '8:01:0' comes from (https://github.com/killbill/killbill-commons/blob/master/clock/src/main/java/org/killbill/clock/ClockUtil.java#L51): | ||
// We compute a DateTime in the account timezone by specifying explicitly the year-month-day we want to end up in, and shoving *a time*. The reason why we end up on an 8:01:02 is not necessarily so important, | ||
// What's important is that by construction that DateTime is guaranteed to match a LocalDate of 2015-12-01 | ||
busHandler.pushExpectedEvents(NextEvent.CANCEL, NextEvent.BLOCK, NextEvent.NULL_INVOICE); | ||
clock.setTime(new DateTime("2015-12-01T08:01:02")); | ||
// We now move the clock to the date of the cancellation, which match the cancellation day from the client point of view | ||
busHandler.pushExpectedEvents(NextEvent.NULL_INVOICE, NextEvent.CANCEL, NextEvent.BLOCK, NextEvent.NULL_INVOICE); | ||
clock.setTime(new DateTime("2015-12-01T07:01:02Z")); | ||
assertListenerStatus(); | ||
This comment has been minimized.
Sorry, something went wrong.
sbrossie
Member
|
||
|
||
// Verify second that there was no repair (so the cancellation did correctly happen on the "2015-12-01") | ||
|
1 comment
on commit 9ec7913
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
Once we have decided where exactly all that lives we should add unit test for that specific method (maybe you already have them).