Search code examples
datejava-8java-timedayofweekzoneddatetime

Getting time range between the first day of current week and current time JDK 8


I can easilly calculate time period between the first day of month and current time:

/**
 * Returns the time range between the first day of month and current time in milliseconds.
 *
 * @param zoneId time zone ID.
 * @return a {@code long} array, where at index: 0 - the first day of month midnight time; 1 - current time.
 */

public static long[] monthDateRange(ZoneId zoneId) {
    long[] toReturn = new long[2];

ZonedDateTime nowZdt = LocalDateTime.now().atZone(zoneId);
ZonedDateTime startZdt = nowZdt.withDayOfMonth(1);
toReturn[0] = startZdt.toInstant().toEpochMilli();
toReturn[1] = nowZdt.toInstant().toEpochMilli();
return toReturn;
}

But how to start counting at the first day (midnight) of current week?


Solution

  • tl;dr

    ZonedDateTime
        .now( ZoneId.of( "Asia/Kolkata" ) )                            // Current moment in a particular time zone.
        .toLocalDate()                                                 // Extract date-only value, losing the time-of-day and time zone components.
        .with( TemporalAdjusters.previousOrSame( DayOfWeek.SUNDAY ) )  // Move to another day-of-week, or same date if this is the desired day-of-week.
        .atStartOfDay( ZoneId.of( "Asia/Kolkata" ) )                   // Determine the first moment of the day. Do *not* assume this time-of-day is 00:00:00 as anomalies such as Daylight Saving Time (DST) may mean otherwise such as 01:00:00. 
        .toInstant()                                                   // Adjust into UTC, same moment, same point on the timeline, but viewed through the lens of UTC time zone.
        .toEpochMilli()                                                // Extract a count-from-epoch in milliseconds. I do *not* recommend tracking date-time this way, but the Question requires this number.
    

    Details

    The Answer by Gruodis is good, but here's an alternative that is a bit more direct and flexible.

    Get current moment as a ZonedDateTime.

    ZoneId z = ZoneId.of( "Pacific/Auckland" ) ;
    ZonedDateTime now = ZonedDateTime.now( z ) ;
    

    TemporalAdjuster

    The TemporalAdjuster interface lets you manipulate a date-time value to get a fresh date-time value. The TemporalAdjusters class (note plural s) provides several handy implementations. Use the DayOfWeek enum to specify what day you consider to be the first day of the week.

    DayOfWeek dowStartOfWeek = DayOfWeek.MONDAY ; 
    LocalDate weekStartDate = now.toLocalDate().with( TemporalAdjusters.previousOrSame( DayOfWeek.MONDAY ) ) ;
    ZonedDateTime start = weekStartDate.atStartOfDay( z ) ;  // Determine first moment of the day. Note: *not* always 00:00:00.
    

    See this code run live at IdeOne.com.

    2017-08-21T00:00+12:00[Pacific/Auckland] 2017-08-21T08:44:46.439+12:00[Pacific/Auckland]

    Span of time

    To report your span of time, pou could indeed extract a count-from-epoch of whole seconds, if required.

    long epochSeconds = start.toEpochSecond() ; 
    

    Or extract milliseconds via Instant.

    long epochMillis = start.toInstant().toEpochMilli() ;
    

    But keep in mind that both those numbers truncate any further fractional second, as the java.time types resolve to nanoseconds.

    Besides truncation, there are other reasons to avoid tracking date-time as a count-from-epoch. Since such values are meaningless to the human eye, debugging is much more difficult and faulty data may escape your notice. Also, you may assume the epoch is 1970-01-01T00:00:00Z, but there are at least another couple dozen epochs is use by common software systems. Yet another problem is ambiguity over the granularity of the count, where some systems use whole seconds, others use milliseconds, others use microseconds, others nanoseconds, and still others use other resolutions.

    Interval

    So instead of returning mere long integer numbers, I suggest returning an object. A pair of Instant objects work, which is what is used by the Interval class in the ThreeTen-Extra project. That class has several very handy methods I expect the calling code may find useful such as contains, encloses, abuts, overlaps, span, isEmpty, and more.

    org.threeten.extra.Interval interval = Interval.of( start.toInstant() , now.toInstant() ) ;
    

    You can apply a time zone to view either the beginning or ending through the lens of a region’s own wall-clock time.

    ZonedDateTime zdtStart = interval.getStart().atZone( z );  // Or `getEnd()`.