javadatetimecalendarjava.util.calendar

Gregorian Calendar gets the wrong "start of the week"


This is August 2025:

          August 2025       
    Mon Tue Wed Thu Fri Sat Sun  
                      1   2   3  
      4   5   6   7   8   9  10  
 --> 11  12  13  14  15 (16) 17  
     18  19  20  21  22  23  24  
     25  26  27  28  29  30  31  

Given a date (e.g. 2025-08-16) I want to get the beginning of the week. The first day of the week is Monday, so the beginning of the week is 2025-08-11.

I used this code:

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.TimeZone;

public class Sample {

    public static void main(String[] args) {
        Calendar dt = Calendar.getInstance(TimeZone.getTimeZone("Europe/Rome"));
        dt.setFirstDayOfWeek(Calendar.MONDAY);
        
        // set to 2025-08-16
        dt.set(Calendar.YEAR, 2025);
        dt.set(Calendar.MONTH, Calendar.AUGUST);
        dt.set(Calendar.DATE, 16);

        //dt.getTimeInMillis(); // <-- UNCOMMENT THIS AFTER FIRST RUN
        
        dt.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
        
        // SHOULD BE 2025-08-11 
        System.out.println(new SimpleDateFormat("yyyy-MM-dd").format(dt.getTime()));

    }
}

Running it, it returns 2025-08-04, which is completely wrong. But uncommenting the line marked with "UNCOMMENT THIS", which is a get operation, the actual result becomes the expected one (2025-08-11).

Is it a JDK bug? How can I explain it?


Solution

  • tl;dr

    Use only java.time classes for date-time work. Never use Calendar.

    LocalDate
    .of( 2025 , Month.AUGUST , 16 )
    .with( 
        TemporalAdjusters.previousOrSame ( DayOfWeek.MONDAY ) 
    )
    

    Avoid legacy date-time classes

    Never use the terribly flawed legacy date time classes such as Date, Calendar, and SimpleDateFormat.

    Use only the modern java.time classes.

    java.time

    Represent a date with java.time.LocalDate.

    LocalDate ld = LocalDate.of( 2025 , Month.AUGUST , 16 ) ;
    

    Use a TemporalAdjuster (class name is singular) to get the previous Monday, or get the same date if it is a Monday. The utility class TemporalAdjusters (class name is plural) provides the adjuster you need.

    TemporalAdjuster adjuster = TemporalAdjusters.previousOrSame ( DayOfWeek.MONDAY ) ;
    

    Apply the adjuster to produce a LocalDate object.

    LocalDate previousOrSameMonday = ld.with( adjuster ) ;