使用 Java 8 Time API 获取以毫秒为单位的第一次和最后一次

Getting the first and last time in milliseconds with Java 8 Time API

我正在将我的时间计算从自行实现的代码转换为 Java 8 次 API。

我需要 java.time.Yearjava.time.Month class 的开始和结束时间(以毫秒为单位),我计划稍后在 JFreeChart 的另一层中使用它。

我需要 JFreeChart org.jfree.data.time.RegularTimePeriod class 中的 getFirstMillisecond()getLastMilliSecond() 等函数。

我已经实现了类似于-

的代码
public static long getStartTimeInMillis(java.time.Year year, java.time.Month month) {       
    if (year != null && month != null) {
        return LocalDate.of(year.getValue(), month, 1).with(TemporalAdjusters.firstDayOfMonth()).
                atStartOfDay().atZone(TimeZone.getDefault().toZoneId()).toInstant().toEpochMilli();
    } else if (year != null) {
        return LocalDate.of(year.getValue(), java.time.Month.JANUARY, 1).with(TemporalAdjusters.firstDayOfMonth()).
                atStartOfDay().atZone(TimeZone.getDefault().toZoneId()).toInstant().toEpochMilli();
    }       
    return 0;
}

public static long getEndTimeInMillis(java.time.Year year, java.time.Month month) {
    if (year != null && month != null) {
        return LocalDate.of(year.getValue(), month, 1).with(TemporalAdjusters.lastDayOfMonth()).
                atTime(OffsetTime.MAX).toLocalDateTime().atZone(TimeZone.getDefault().toZoneId()).toInstant().toEpochMilli();
    } else if (year != null) {
        return  LocalDate.of(year.getValue(), java.time.Month.DECEMBER, 1).with(TemporalAdjusters.lastDayOfMonth()).
                atTime(OffsetTime.MAX).toLocalDateTime().atZone(TimeZone.getDefault().toZoneId()).toInstant().toEpochMilli();
    }       
    return 0;
}

但我觉得它真的很复杂。有什么 better/shorter 方法可以得到这些值吗?

我会这样重构

public static long getStartTimeInMillis(java.time.Year year, Optional<Month> monthOpt) {
    Month month = monthOpt.orElse(Month.JANUARY);

    if (year != null) {
        return LocalDate.of(year.getValue(), month, 1)
                .with(TemporalAdjusters.firstDayOfMonth())
                .atStartOfDay()
                .atZone(TimeZone.getDefault().toZoneId())
                .toInstant()
                .toEpochMilli();
    }

    return 0;
}

YearMonth

是的,有一个稍微好一点的方法。使用 java.time.

中包含的 YearMonth class

此外,将该调用链分解为单独的语句,使其更易读且更易于 trace/debug。相信 JVM 会代表您进行优化;仅在可以使您的代码更具可读性和更易于理解的地方使用调用链。

无需通过 TimeZone 获取 JVM 当前的默认时区。相反,调用 ZoneId.systemDefault().

设置一些输入值。

// Inputs
Year year = Year.of ( 2015 );
Month month = Month.DECEMBER;

你方法的核心部分。

// Code for your method.
YearMonth yearMonth = year.atMonth ( month ); // Instantiate a YearMonth from a Year and a Month.
LocalDate localDate = yearMonth.atDay ( 1 ); // First day of month.
ZoneId zoneId = ZoneId.systemDefault (); // Or… ZoneId.of("America/Montreal");
ZonedDateTime zdt = localDate.atStartOfDay ( zoneId );
long millis = zdt.toInstant ().toEpochMilli ();

转储到控制台。

System.out.println ( "year: " + year + " | month: " + month + " | yearMonth: " + yearMonth + " | zoneId:" + zoneId + " | zdt: " + zdt + " | millis: " + millis );

year: 2015 | month: DECEMBER | yearMonth: 2015-12 | zoneId:America/Los_Angeles | zdt: 2015-12-01T00:00-08:00[America/Los_Angeles] | millis: 1448956800000

更好的是,将 YearMonth 实例而不是一对 YearMonth 对象传递给您的方法。如果您的其他业务逻辑正在使用 Year + Month 对,请改用 YearMonth - 这就是它的用途。

除了在给定 null 年并信任系统默认时区时返回 0 的可疑做法外,您可以按如下方式重写您的方法:

public static long getStartTimeInMillis(java.time.Year year, java.time.Month month) {
    if (year == null) {
        return 0;
    }

    if (month == null) {
        month = Month.JANUARY;
    }

    return year.atMonth(month)
            .atDay(1)
            .atStartOfDay()
            .atZone(ZoneId.systemDefault())
            .toInstant()
            .toEpochMilli();

}

public static long getEndTimeInMillis(java.time.Year year, java.time.Month month) {
    if (year == null) {
        return 0;
    }

    if (month == null) {
        month = Month.JANUARY;
    }

    return year.atMonth(month)
            .atEndOfMonth()
            .atTime(LocalTime.MAX)
            .atZone(ZoneId.systemDefault())
            .toInstant()
            .toEpochMilli();
};

为了扩展,如果年份是 null,最好只抛出一个 NullPointerException。如果您通过了一个空年份,则上游代码中可能存在错误。返回一个无意义的 0 只会将 bug 推得更远,并且更难追踪。这个原则叫做"fail fast".

依赖系统默认时区对于严肃的生产代码来说不是一个好主意,因为它往往会导致问题,因为服务器可能配置不可预测(例如格林威治标准时间),或者当地理分布的服务器处于不同时区时,您可能会遇到问题。仔细考虑一下"what timezone am I computing all these times against?"

的问题就不会头疼了