5

I'm using Apache commons-lang3 DateUtils.truncate(Calendar calendar, int field) method to 'cut off' unnecessary fields of a Calendar object. Now when the field parameter gets the value of Calendar.WEEK_OF_MONTH, it throws a

java.lang.IllegalArgumentException: The field 4 is not supported

The truncate() method's documentation says:

/**
 * <p>Truncates a date, leaving the field specified as the most
 * significant field.</p>
 *
 * <p>For example, if you had the date-time of 28 Mar 2002
 * 13:45:01.231, if you passed with HOUR, it would return 28 Mar
 * 2002 13:00:00.000.  If this was passed with MONTH, it would
 * return 1 Mar 2002 0:00:00.000.</p>
 * 
 * @param date  the date to work with, not null
 * @param field  the field from {@code Calendar} or <code>SEMI_MONTH</code>
 * @return the different truncated date, not null
 * @throws IllegalArgumentException if the date is <code>null</code>
 * @throws ArithmeticException if the year is over 280 million
 */

So I assume this should work, but it clearly doesn't. Is there a way to truncate dates to the week's first day using DateUtils?


UPDATE:

I looked up in the source code and found out that the modify() method (tuncate() uses this internally), iterates through a bunch of predefined fields to find the given parameter. Now these fields are:

private static final int[][] fields = {
        {Calendar.MILLISECOND},
        {Calendar.SECOND},
        {Calendar.MINUTE},
        {Calendar.HOUR_OF_DAY, Calendar.HOUR},
        {Calendar.DATE, Calendar.DAY_OF_MONTH, Calendar.AM_PM 
            /* Calendar.DAY_OF_YEAR, Calendar.DAY_OF_WEEK, Calendar.DAY_OF_WEEK_IN_MONTH */
        },
        {Calendar.MONTH, DateUtils.SEMI_MONTH},
        {Calendar.YEAR},
        {Calendar.ERA}};

As one can see, there is nothing related to Calendar's WEEK-ish fields, so I guess I have to do this manually... Any other thoughts/recommendations are welcome!

Sleeper9
  • 1,707
  • 1
  • 19
  • 26
  • 1
    What do you expect the date to become when you truncate the week?! – Uwe Allner Oct 31 '14 at 11:02
  • E.g. if I pass the current date: `Fri Oct 31 12:22:43 CET 2014`, I expect `Mon Oct 27 00:00:00 CET 2014`. Now I managed to get it by truncating to DAY_OF_MONTH field and manually set the DAY_OF_WEEK field to MONDAY (in my country, monday is the first day of week). – Sleeper9 Oct 31 '14 at 11:26

2 Answers2

3

It's not really possible to truncate by a week in a sensible fashion. Consider the following date:

2014-11-01 12:01:55

2014-11-01 12:01:00 // truncate minute
2014-11-01 00:00:00 // truncate day
2014-11-00 00:00:00 // truncate month

The original date is a Saturday. So what does week truncation mean in that case? Should we truncate to the previous Monday? If so, that would be:

2014-10-27 00:00:00 // truncate week?

That doesn't seem right to me. We've changed the month in this case; sometimes even the year would change. If you can think of a sensible way to describe this (and some use cases), please file an issue and we'll take a look at it. But it strikes me as a field that makes no sense for truncation.

You may find some ideas for your original problem here: Retrieve current week's Monday's date

Community
  • 1
  • 1
Duncan Jones
  • 67,400
  • 29
  • 193
  • 254
  • 2
    It's absolutely sensible for me if a 'higher' field rolls its value when truncating for week. I solved it now this way: `calendar = DateUtils.truncate(calendar, Calendar.DAY_OF_MONTH); calendar.set(Calendar.DAY_OF_WEEK, calendar.getFirstDayOfWeek());` So for my use case, the example you provided is almost suitable, except for the `2014-11-00 00:00:00 // truncate month` part, which is impossible. :-) – Sleeper9 Nov 03 '14 at 08:13
  • @Sleeper9 Ah yes, good point (regarding truncate month). If you feel there is a good use case for this, raise an issue in the Commons Jira. – Duncan Jones Nov 06 '14 at 06:41
  • Thanks for the support, I'll think about that. – Sleeper9 Nov 06 '14 at 08:38
3

tl;dr

LocalDate.now( 
    ZoneId.of( "Africa/Tunis" )
).with( 
    TemporalAdjuster.previousOrSame​( DayOfWeek.MONDAY )
)

java.time

No need for an external library like DateUtils. Use the java.time classes built into Java 8 and later.

Sounds like your real question is how to get the first day of the week for a particular date.

Time zone

A time zone is crucial in determining a date. For any given moment, the date varies around the globe by zone. For example, a few minutes after midnight in Paris France is a new day while still “yesterday” in Montréal Québec.

Specify a proper time zone name in the format of continent/region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 3-4 letter pseudo-zones such as EST or IST as they are not true time zones, not standardized, and not even unique(!).

ZoneId z = ZoneId.of( "America/Montreal" ) ;

The LocalDate class represents a date-only value without time-of-day and without time zone.

LocalDate today = LocalDate.now( z );

To get the first day of the week for that date, we must define the first day of the week. That definition varies by culture. In the United States, we usually mean Sunday. Much of the rest of the world means Monday. Which ever it is for your users, specify with a DayOfWeek enum object.

To move from our date to that date, use a TemporalAdjuster implementation found in TemporalAdjusters class.

LocalDate firstOfWeek = today.with( 
    TemporalAdjuster.previousOrSame​( DayOfWeek.MONDAY ) 
) ;
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154