I am converting my time calculations from self implemented code to Java 8 Time API.
I need to have the start and end time in milliseconds from a java.time.Year
or java.time.Month
class, which I plan to use later in another layer for JFreeChart.
I need functions like getFirstMillisecond()
& getLastMilliSecond()
from org.jfree.data.time.RegularTimePeriod
class of JFreeChart.
I have already implemented code something like-
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;
}
But it looks really complicated to me. Is there any better/shorter way to get these values?
YearMonth
Yes, there is a slightly better way. Use the YearMonth
class included with java.time.
Also, break up that call-chain into separate statements to make it more readable and easier to trace/debug. Trust the JVM to optimize on your behalf; only use call-chaining where it makes your code more readable and easier to understand.
Going through TimeZone
to get the JVM’s current default time zone is unnecessary. Instead, call ZoneId.systemDefault()
.
Set up some input values.
// Inputs
Year year = Year.of ( 2015 );
Month month = Month.DECEMBER;
The core part of your method.
// 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 ();
Dump to console.
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
Even better, pass the YearMonth
instance to your method rather than the pair of Year
and Month
objects. If your other business logic is using the Year
+ Month
pair, use YearMonth
instead – that’s what it’s for.
Aside from the questionable practices of returning 0
when given a null
year and trusting system default timezone, you can rewrite your methods as follows:
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();
};
To expand, it is likely better to just throw a NullPointerException
if year is null
. If you are passed a null year, there's probably a bug in upstream code. Returning a meaningless 0 only pushes the bug further down the road and makes it harder to track down. This principle is called "fail fast".
Relying on system default timezone is a bad idea for serious production code because it tends to cause problems as servers might be configured unpredictably (e.g. GMT) or you can have issues when geographically distributed servers are in different timezones. It prevents headaches to give a careful consideration to the question of "what timezone am I computing all these times against?"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With