2

我有一个Java方法如下:

private static boolean isDateBetweenRange(DataSet obj, MyClass dataSource, ConditionContext context) {
    FilterContext fc = dataSource.getData();

    LocalDate dateFieldToCheck = obj.getDate(fc.getDateField()).toInstant()
                                   .atZone(ZoneId.systemDefault()).toLocalDate();
    LocalDate minDate = fc.getMinDateValue();
    LocalDate maxDate = fc.getMaxDateValue();

    if (minDate == null || maxDate == null) {
        minDate = context.getStartDate().toInstant().atZone(ZoneId.systemDefault())
                    .toLocalDate();
        maxDate = context.getEndDate().toInstant().atZone(ZoneId.systemDefault())
                    .toLocalDate();
    }

    boolean result = (dateFieldToCheck.isAfter(minDate) || dateFieldToCheck.isEqual(minDate))
            && (dateFieldToCheck.isBefore(maxDate) || dateFieldToCheck.isEqual(maxDate));

    return result;
}

我也想为同样的逻辑LocalDateTimeLocalDateTime如果我重载该方法,它将是完全相同的代码。

如何使该方法具有泛型以使用泛型LocalDateLocalDateTime任何其他机制?

如何根据我拥有的类型制作context.getXXXDate()... toLocalDate()toLocalDateTime()通用代码?

4

3 回答 3

6

TemporalAccessor接口可用于执行此操作。但是,请注意,这TemporalAccessor是一个高级接口,不应在低级实用程序代码之外使用。

boolean api(TemporalAccessor temporal1, TemporalAccessor temporal2) {
  LocalDate date1 = LocalDate.from(temporal1);
  LocalDate date2 = LocalDate.from(temporal2);
  return ...;
}

此代码现在将接受LocalDate,和LocalDateTime, 。OffsetDateTimeZonedDateTime

正如评论中提到的,ZoneId.systemDefault()在一个业务逻辑中只调用一次是至关重要的,因为值可以改变。

于 2017-06-22T12:09:36.087 回答
3

嗯...LocalDateTime是 和 的不可变LocalDate组合LocalTime。它有方法toLocalDate()toLocalTime()你可以用它“分解它”。反之亦然 - 你也可以很容易地创建一个组合 - 见LocalDate.atTime(LocalTime)

在我看来,如果您在逻辑中考虑时间,您提取的 API 应该接受LocalDateTime. 在可疑情况下:) 您出于某种原因想要使用LocalDate-s 提供此 API,您可以显式使用,例如LocalDate.atStartOfDay. 像这样的东西:

boolean api(LocalDate aDate, LocalDate anotherDate)
{
    return this.api(aDate.atStartOfDay(), anotherDate.atStartOfDay());
}

boolean api(LocalDateTime aDateTime, LocalDateTime anotherDateTime)
{
    return ...;
}

虽然这看起来有点难看,但仍然可以很好地工作,不是吗?

于 2017-06-22T10:13:57.843 回答
0

您可以让您的方法接收一个asjava.time.temporal.Temporal参数(或@JodaStephen 建议的一个)。此代码适用于两者。TemporalAccessor

我必须承认,我更愿意像@Lachezar Balev 的回答所暗示的那样去做,但无论如何,这是一个替代方案。

由于 aTemporal可以是任何类型(LocalDateLocalDateTimeZonedDateTime等),但您只需要LocalDateand LocalDateTime,我正在执行以下操作:

  • 尝试创建LocalDateTime第一个:它需要比LocalDate(小时/分钟/秒)更多的字段,所以如果可以创建,它是首选类型
  • 如果无法创建,我会捕获相应的异常并尝试创建一个LocalDate

另一个细节是我还简化了您的比较:

dateFieldToCheck.isAfter(minDate) || dateFieldToCheck.isEqual(minDate)

相当于:

! dateFieldToCheck.isBefore(minDate)

与 比较时进行了类似的简化maxDate

public boolean check(Temporal dateFieldToCheck, Temporal minDate, Temporal maxDate) {
    // try to get as LocalDateTime
    try {
        LocalDateTime dtCheck = LocalDateTime.from(dateFieldToCheck);
        LocalDateTime dtMin = LocalDateTime.from(minDate);
        LocalDateTime dtMax = LocalDateTime.from(maxDate);

        return (!dtCheck.isBefore(dtMin)) && (!dtCheck.isAfter(dtMax));
    } catch (DateTimeException e) {
        // exception: one of the Temporal objects above doesn't have all fields required by a LocalDateTime
        // trying a LocaDate instead
        LocalDate dCheck = LocalDate.from(dateFieldToCheck);
        LocalDate dMin = LocalDate.from(minDate);
        LocalDate dMax = LocalDate.from(maxDate);

        return (!dCheck.isBefore(dMin)) && (!dCheck.isAfter(dMax));
    }
}

LocalDate现在您可以使用 a和 a调用此方法LocalDateTime

check(LocalDate.of(2017, 6, 19), LocalDate.of(2017, 6, 18), LocalDate.of(2017, 6, 29)); // true
check(LocalDate.of(2017, 6, 17), LocalDate.of(2017, 6, 18), LocalDate.of(2017, 6, 29)); // false
check(LocalDateTime.of(2017, 6, 29, 9, 0), LocalDateTime.of(2017, 6, 29, 8, 0), LocalDateTime.of(2017, 6, 29, 11, 0)); // true
check(LocalDateTime.of(2017, 6, 29, 7, 0), LocalDateTime.of(2017, 6, 29, 8, 0), LocalDateTime.of(2017, 6, 29, 11, 0)); // false

如果需要(例如LocalTime),您可以扩展代码以识别其他类型。

您还可以添加另一个参数来指示将使用的类型,如下所示:

public boolean check(Temporal dateFieldToCheck, Temporal minDate, Temporal maxDate, int type) {
    switch (type) {
        case 1:
            // try to get as LocalDateTime
            LocalDateTime dtCheck = LocalDateTime.from(dateFieldToCheck);
            LocalDateTime dtMin = LocalDateTime.from(minDate);
            LocalDateTime dtMax = LocalDateTime.from(maxDate);

            return (!dtCheck.isBefore(dtMin)) && (!dtCheck.isAfter(dtMax));
        case 2:
            // trying a LocaDate instead
            LocalDate dCheck = LocalDate.from(dateFieldToCheck);
            LocalDate dMin = LocalDate.from(minDate);
            LocalDate dMax = LocalDate.from(maxDate);

            return (!dCheck.isBefore(dMin)) && (!dCheck.isAfter(dMax));
        // ... and so on
    }

    // invalid type, return false?
    return false;
}

所以你可以用 a 调用它ZonedDateTime并强制它使用 a LocalDate(然后你可以调整你的代码并用你从中获得的对象调用这个方法atZone(ZoneId.systemDefault())):

ZonedDateTime z1 = obj.getDate(fc.getDateField()).toInstant().atZone(ZoneId.systemDefault());
ZonedDateTime z2 = context.getStartDate().toInstant().atZone(ZoneId.systemDefault());
ZonedDateTime z3 = context.getEndDate().toInstant().atZone(ZoneId.systemDefault());
check(z1, z2, z3, 2);

此代码也适用于不同的类型:

ZonedDateTime z1 = ...
LocalDate d = ...
LocalDateTime dt = ...
// converts all to LocalDate
System.out.println(check(z1, dt, d, 2));

PS:当然你可以创建一个Enum而不是一个int来定义类型。或者使用aClass<? extends Temporal>直接表示类型:

public boolean check(Temporal dateFieldToCheck, Temporal minDate, Temporal maxDate, Class<? extends Temporal> type) {
    if (type == LocalDate.class) {
        // code for LocalDate (use LocalDate.from() as above)
    }
    if (type == LocalDateTime.class) {
        // code for LocalDateTime (use LocalDateTime.from() as above)
    }
}

// convert all objects to LocalDate
check(z1, dt, d, LocalDate.class);
于 2017-06-22T12:54:02.900 回答