Christopher Hackl
Christopher Hackl

Reputation: 193

Issue with Calendar calculation that spans 2 calendar years

I'm using a method that calculates the next Monday from a given date string.

public static String getStartOfNextWeek(String DATE){

String format = "dd.MM.yyyy";SimpleDateFormat df = new SimpleDateFormat(format);

Date date = null;
try {
date = df.parse(DATE);
} catch (ParseException e) {
e.printStackTrace();
}

Calendar cal = Calendar.getInstance();

cal.setTime(date);
int week = cal.get(Calendar.WEEK_OF_YEAR);
int year = cal.get(Calendar.YEAR);

Calendar calendar = new GregorianCalendar();
calendar.clear(); 
calendar.set(Calendar.YEAR, year);
calendar.set(Calendar.WEEK_OF_YEAR, week);

//add 8 days to get next weeks Monday

calendar.add(Calendar.DAY_OF_WEEK, 8);

Date startDate = calendar.getTime();

SimpleDateFormat df2 = new SimpleDateFormat("dd.MM.yyyy");

String start = df2.format(startDate);

return start;

This work perfectly fine over a single calendar year, but when I'm passing a value that spans two calendar years problems arise.

For example:

input: 15.12.2014
output: 22.12.2014 CORRECT
input: 22.12.2014
output: 29.12.2014 CORRECT
input: 29.12.2014
output: 6.1.2014 INCORRECT

I realize where the mistake is located, since it takes WEEK_OF_YEAR as "1", but YEAR as "2014", so the output is technically correct. Just wrong for my purpose.

How would i best tell the calendar object that i want the next monday in week 1, but 2015?

Upvotes: 0

Views: 96

Answers (3)

Basil Bourque
Basil Bourque

Reputation: 339372

UPDATE: The Joda-Time project is now in maintenance mode, with the team advising migration to the java.time classes. This Answer is left intact as history. See my newer Answer.

Joda-Time

The Joda-Time library, version 2.5, gets the correct answer. And gets it more easily.

// Parse input string.
String input = "29.12.2014";
DateTimeFormatter formatter = DateTimeFormat.forPattern( "dd.MM.yyyy" );
LocalDate inputLocalDate = formatter.parseLocalDate( input );

// Find desired Monday.
LocalDate possibleMonday = inputLocalDate.withDayOfWeek( DateTimeConstants.MONDAY ); 

// The possible Monday could be past, present, or future of our input date. Adjust as needed.
LocalDate desiredMonday = null;
if ( possibleMonday.isBefore( inputLocalDate ) || possibleMonday.isEqual( inputLocalDate ) ) {
    desiredMonday = possibleMonday.plusWeeks( 1 ); // If the possible Monday is past or present, add a week to get *next* Monday.
} else {
    desiredMonday = possibleMonday;  // If the possible Monday is future, use it.
}

String output = formatter.print( desiredMonday ); 

Dump to console.

System.out.println( "input : " + input );
System.out.println( "inputLocalDate : " + inputLocalDate );
System.out.println( "desiredMonday : " + desiredMonday );
System.out.println( "output : " + output );

When run.

input : 29.12.2014
inputLocalDate : 2014-12-29
desiredMonday : 2015-01-05
output : 05.01.2015

Upvotes: 1

Basil Bourque
Basil Bourque

Reputation: 339372

tl;dr

LocalDate.parse( 
    "29.12.2014" ,
    DateTimeFormatter.ofPattern( "dd.MM.uuuu" )
).with(
    TemporalAdjusters.next( DayOfWeek.MONDAY )
)

2015-01-05

java.time

The modern approach uses the java.time classes.

DateTimeFormatter f = DateTimeFormatter.ofPattern( "dd.MM.uuuu" )
LocalDate ld = LocalDate.parse( "29.12.2014" , f ) ;

To move to another date, use a TemporalAdjuster implementation found in the TemporalAdjusters class. Specify the desired day-of-week with DayOfWeek enum object. No problem crossing end-of-year/start-of-year.

LocalDate followingMonday = ld.with( TemporalAdjusters.next( DayOfWeek.MONDAY ) ) ;

If you want to use the current date if it is a Monday, use TemporalAdjusters.nextOrSame.

Similar methods provide for previous day-of-week, as well: previous & previousOrSame.


About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

Where to obtain the java.time classes?

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

Upvotes: 0

Martin
Martin

Reputation: 1150

There's something weird when combining Date and Calender, when it comes to parsing dates, using only Calender it works great;

    String[] dt = dateStr.split("\\.");
    GregorianCalendar cal = new GregorianCalendar(Integer.parseInt(dt[2]), (Integer.parseInt(dt[1])-1), Integer.parseInt(dt[0]));
    cal.setFirstDayOfWeek(Calendar.MONDAY);
    cal.clear(Calendar.DAY_OF_WEEK);
    cal.add(Calendar.DATE, 7);
    System.out.println(cal.get(Calendar.YEAR) + "." + (cal.get(Calendar.MONTH)+1) + "." + cal.get(Calendar.DATE));

Edit: You have to subtract 1 from the month, since calendar expects the months to range from 0 to 11. (Calendar.JANUARY == 0) //true

Upvotes: 0

Related Questions