Search code examples
javajava-8jfreechartjava-time

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


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?


Solution

  • 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.