使用 Java 8 Time API 获取以毫秒为单位的第一次和最后一次
Getting the first and last time in milliseconds with Java 8 Time API
我正在将我的时间计算从自行实现的代码转换为 Java 8 次 API。
我需要 java.time.Year
或 java.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
实例而不是一对 Year
和 Month
对象传递给您的方法。如果您的其他业务逻辑正在使用 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?"
的问题就不会头疼了
我正在将我的时间计算从自行实现的代码转换为 Java 8 次 API。
我需要 java.time.Year
或 java.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
实例而不是一对 Year
和 Month
对象传递给您的方法。如果您的其他业务逻辑正在使用 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?"
的问题就不会头疼了