简体   繁体   中英

Get the week start and end date given a current date and week start

If possible I would prefer a joda or non-joda solution for the scenario below

Lets say if my week starts on 02/05/2012 and the given current date is 02/22/2011. I need to calculate the week start and end date for the given current date. So my solution should have the week start as 02/19 and week ends at 02/25. For simplicity, I have set my week start here as 02/05/2011 but it could be any day potentially and my week always has 7 days.

My existing code is below but doesnt seem to work as expected.

public Interval getWeekInterval(Date calendarStartDate, Date date)
{
    Calendar sDate = Calendar.getInstance();
    sDate.setTime(getMidnightDate(calendarStartDate));

    Calendar eDate = Calendar.getInstance();
    eDate.setTime(date);

    Calendar weekStartDate = (Calendar) sDate.clone();
    logger.debug("Date:" + sDate.getTime());
    while (sDate.before(eDate)) {
        weekStartDate = sDate;
        sDate.add(Calendar.DAY_OF_WEEK_IN_MONTH, 1);
    }

    return new Interval(weekStartDate.getTime(), sDate.getTime());
}

Defining A Week

If you are using date-time objects, you should define a week as up to but not including the first moment of the day after the end of week. As seen in this diagram.

时间线显示(> =第一天开始)和(<第8天开始)

This approach is known as Half-Open . This approach is commonly used for working with spans of time.

The reason is because, logically, that last moment of the day before the new day is infinitely divisible as a fraction of a second. You may think that using ".999" would handle that for milliseconds, but then you'd mistaken when writing for the new java.time.* classes in Java 8 that have nanosecond resolution rather than millisecond. You may think think that using ".999999999" would handle that case, but then you'd be mistaken when handling date-time values from many databases such as Postgres that use microsecond resolution, ".999999".

In the third-party open-source Joda-Time library, this Half-Open logic is how its Interval class works. The beginning is inclusive and the ending is exclusive. This works out nicely. Similarly, calling plusWeeks(1) on a DateTime to add a week to the first moment of a day gives you the first moment of the 8th day later (see example below).

Time Zone

The question and other answers ignores the issue of time zone. If you do not specify, you'll be getting the default time zone. Usually better to specify a time zone, using a proper time zone name (not 3-letter code).

Joda-Time

Avoid the java.util.Date & Calendar classes bundled with Java. They are notoriously troublesome.

Here is some example code using Joda-Time 2.3.

CAVEAT : I have not tested of of the below code thoroughly. Just my first take, a rough draft. May well be flawed.

Standard Week (Monday-Sunday)

The Joda-Time library is built around the ISO 8601 standard. That standard defines the first day of the week as Monday, last day as Sunday.

If that meets your definition of a week, then getting the beginning and ending is easy.

UPDATE As an alternative to the discussion below, see this very clever and very simple one-liner solution by SpaceTrucker .

Simply forcing the day-of-week works because Joda-Time assumes you want:

  • Monday to be before (or same as) today.
  • Sunday to be after (or same as) today.
DateTimeZone timeZone = DateTimeZone.forID( "Europe/Paris" );
DateTime now = new DateTime( timeZone );

DateTime weekStart = now.withDayOfWeek( DateTimeConstants.MONDAY ).withTimeAtStartOfDay();
DateTime weekEnd = now.withDayOfWeek(DateTimeConstants.SUNDAY).plusDays( 1 ).withTimeAtStartOfDay();
Interval week = new Interval( weekStart, weekEnd );

Dump to console…

System.out.println( "now: " + now );
System.out.println( "weekStart: " + weekStart );
System.out.println( "weekEnd: " + weekEnd );
System.out.println( "week: " + week );

When run…

now: 2014-01-24T06:29:23.043+01:00
weekStart: 2014-01-20T00:00:00.000+01:00
weekEnd: 2014-01-27T00:00:00.000+01:00
week: 2014-01-20T00:00:00.000+01:00/2014-01-27T00:00:00.000+01:00

To see if a date-time lands inside that interval, call the contains method.

boolean weekContainsDate = week.contains( now );

Non-Standard Week

If that does not meet your definition of a week, you a twist on that code.

DateTimeZone timeZone = DateTimeZone.forID( "America/New_York" );
DateTime now = new DateTime( timeZone );

DateTime weekStart = now.withDayOfWeek( DateTimeConstants.SUNDAY ).withTimeAtStartOfDay();
if ( now.isBefore( weekStart )) {
    // If we got next Sunday, go back one week to last Sunday.
    weekStart = weekStart.minusWeeks( 1 );
}
DateTime weekEnd = weekStart.plusWeeks( 1 );
Interval week = new Interval( weekStart, weekEnd );

Dump to console…

System.out.println( "now: " + now );
System.out.println( "weekStart: " + weekStart );
System.out.println( "weekEnd: " + weekEnd );
System.out.println( "week: " + week );

When run…

now: 2014-01-24T00:54:27.092-05:00
weekStart: 2014-01-19T00:00:00.000-05:00
weekEnd: 2014-01-26T00:00:00.000-05:00
week: 2014-01-19T00:00:00.000-05:00/2014-01-26T00:00:00.000-05:00

First day of week depends on the country. What makes the calculation fragile, is that one may break the year boundary, and the week number ( Calendar.WEEK_OF_YEAR ). The following would do:

    Calendar currentDate = Calendar.getInstance(Locale.US);
    int firstDayOfWeek = currentDate.getFirstDayOfWeek();

    Calendar startDate = Calendar.getInstance(Locale.US);
    startDate.setTime(currentDate.getTime());
    //while (startDate.get(Calendar.DAY_OF_WEEK) != firstDayOfWeek) {
    //    startDate.add(Calendar.DATE, -1);
    //}
    int days = (startDate.get(Calendar.DAY_OF_WEEK) + 7 - firstDayOfWeek) % 7;
    startDate.add(Calendar.DATE, -days);

    Calendar endDate = Calendar.getInstance(Locale.US);
    endDate.setTime(startDate.getTime());
    endDate.add(Calendar.DATE, 6);

One bug in Calendar breaks your code, clone , seems to simply give the identical object, hence at the end you have identical dates. (Java 7 at least).

 DateTime sDateTime = new DateTime(startDate); // My calendar start date
 DateTime eDateTime = new DateTime(date); // the date for the week to be determined

 Interval interval = new Interval(sDateTime, sDateTime.plusWeeks(1));
 while(!interval.contains(eDateTime))
 {
    interval = new Interval(interval.getEnd(), interval.getEnd().plusWeeks(1));
 }
 return interval;

Try this (pseudo-code):

// How many days gone after reference date (a known week-start date)
daysGone  = today - referenceDate;

// A new week starts after each 7 days
dayOfWeek = daysGone % 7;

// Now, we know today is which day of the week.
// We can find start & end days of this week with ease
weekStart = today - dayOfWeek;
weekEnd   = weekStart + 6;

Now, we can shorten all of this to two lines:

weekStart = today - ((today - referenceDate) % 7);
weekEnd   = weekStart + 6;

Note that we subtracted date values like integers to show algorithm. You have to write your java code properly.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM