Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Joda Time minusweeks() and plusweeks() over year break 2014/2015 breakdown?

Tags:

java

jodatime

I may be missing something here, but I can't seem to find an explanation in Joda Time's documentation or really anywhere. It seems like Joda Time breaks down when calculating weeks when adding and subtracting weeks when you go from one year to the next.

Can anyone explain why this happens and how to properly do this?

I get the following output from my code below:

2015-01-08 - This is the current week
2015-01-01 - Removed one week
2014-12-25 - Removed one week
2014-12-17 - Removed one week //for some reason, program backed 8 days here
2014-12-10 - Removed one week
2014-12-17 - Added one week
2014-12-24 - Added one week
2014-12-31 - Added one week
2014-01-08 - Added one week //for some reason, program forwarded 8 days here, but it did not forward to 2015.

Original Code

import org.joda.time.*;

public class WonkyWeeks {
    int year;
    int week;

    public void backUpOneWeek() {
        LocalDate today = new LocalDate()
                .withDayOfWeek(4)
                .withWeekOfWeekyear(week)
                .withYear(year);
        LocalDate lastWeek = today.minusWeeks(1);

        week = lastWeek.getWeekOfWeekyear();
        year = lastWeek.getYear();
        System.out.println(lastWeek+" - Removed one week");
    }

    public void forwardOneWeek() {
        LocalDate today = new LocalDate()
                .withDayOfWeek(4)
                .withWeekOfWeekyear(week)
                .withYear(year);
        LocalDate nextWeek = today.plusWeeks(1);

        week = nextWeek.getWeekOfWeekyear();
        year = nextWeek.getYear();
        System.out.println(nextWeek+" - Added one week");
    }

    public void thisWeek() {
        LocalDate thisWeek = new LocalDate()
                .withDayOfWeek(4)
                .withWeekOfWeekyear(week)
                .withYear(year);
        System.out.println(thisWeek+" - This is the current week");
    }

    public static void main(String[] args) {
        WonkyWeeks wonky = new WonkyWeeks();
        wonky.week = 2;
        wonky.year = 2015;
        wonky.thisWeek();
        wonky.backUpOneWeek();
        wonky.backUpOneWeek();
        wonky.backUpOneWeek();
        wonky.backUpOneWeek();
        wonky.forwardOneWeek();
        wonky.forwardOneWeek();
        wonky.forwardOneWeek();
        wonky.forwardOneWeek();
    }
}

After further testing, it gets even more confusing. I tried just adding and removing days instead of weeks and for some reason it seems to skip dates.

Output:

2015-01-08 - This is the current week
2015-01-07 - removed one day
2015-01-06 - removed one day
2015-01-05 - removed one day
2015-01-04 - removed one day
2015-01-03 - removed one day
2015-01-02 - removed one day
2015-01-01 - Removed one full week
2014-12-31 - removed one day
2014-12-30 - removed one day
2014-12-29 - removed one day
2014-12-28 - removed one day
2014-12-27 - removed one day
2014-12-26 - removed one day
2014-12-25 - Removed one full week
2014-12-23 - removed one day // For some reason, it skipped 2014-12-24?
2014-12-22 - removed one day
2014-12-21 - removed one day
2014-12-20 - removed one day
2014-12-19 - removed one day
2014-12-18 - removed one day
2014-12-17 - Removed one full week
2014-12-16 - removed one day
2014-12-15 - removed one day
2014-12-14 - removed one day
2014-12-13 - removed one day
2014-12-12 - removed one day
2014-12-11 - removed one day
2014-12-10 - Removed one full week
2014-12-11 - added one day
2014-12-12 - added one day
2014-12-13 - added one day
2014-12-14 - added one day
2014-12-15 - added one day
2014-12-16 - added one day
2014-12-17 - Added one week
2014-12-18 - added one day
2014-12-19 - added one day
2014-12-20 - added one day
2014-12-21 - added one day
2014-12-22 - added one day
2014-12-23 - added one day
2014-12-24 - Added one week
2014-12-25 - added one day
2014-12-26 - added one day
2014-12-27 - added one day
2014-12-28 - added one day
2014-12-29 - added one day
2014-12-30 - added one day
2014-12-31 - Added one week
2014-01-02 - added one day //Skipped 2014-01-01 and did not forward to 2015
2014-01-03 - added one day
2014-01-04 - added one day
2014-01-05 - added one day
2014-01-06 - added one day
2014-01-07 - added one day
2014-01-08 - Added one week

Further testing code

import org.joda.time.*;

public class WonkyWeeks {
    int year;
    int week;

    public void backUpOneWeek() {
        LocalDate today = new LocalDate()
                .withDayOfWeek(4)
                .withWeekOfWeekyear(week)
                .withYear(year);
        LocalDate adayago = today.minusDays(1);
        System.out.println(adayago+" - removed one day");
        LocalDate twodaysago = adayago.minusDays(1);
        System.out.println(twodaysago+" - removed one day");
        LocalDate threedaysago = twodaysago.minusDays(1);
        System.out.println(threedaysago+" - removed one day");
        LocalDate fourdaysago = threedaysago.minusDays(1);
        System.out.println(fourdaysago+" - removed one day");
        LocalDate fivedaysago = fourdaysago.minusDays(1);
        System.out.println(fivedaysago+" - removed one day");
        LocalDate sixdaysago = fivedaysago.minusDays(1);
        System.out.println(sixdaysago+" - removed one day");
        LocalDate lastWeek = sixdaysago.minusDays(1);

        week = lastWeek.getWeekOfWeekyear();
        year = lastWeek.getYear();
        System.out.println(lastWeek+" - Removed one full week");
    }
    public void forwardOneWeek() {
        LocalDate today = new LocalDate()
                .withDayOfWeek(4)
                .withWeekOfWeekyear(week)
                .withYear(year);
        LocalDate tomorrow = today.plusDays(1);
        System.out.println(tomorrow+" - added one day");
        LocalDate dayAfterTomorrow = tomorrow.plusDays(1);
        System.out.println(dayAfterTomorrow+" - added one day");
        LocalDate threeDaysFromNow = dayAfterTomorrow.plusDays(1);
        System.out.println(threeDaysFromNow+" - added one day");
        LocalDate fourDaysFromNow = threeDaysFromNow.plusDays(1);
        System.out.println(fourDaysFromNow+" - added one day");
        LocalDate fiveDaysFromNow = fourDaysFromNow.plusDays(1);
        System.out.println(fiveDaysFromNow+" - added one day");
        LocalDate sixDaysFromNow = fiveDaysFromNow.plusDays(1);
        System.out.println(sixDaysFromNow+" - added one day");
        LocalDate nextWeek = sixDaysFromNow.plusDays(1);

        week = nextWeek.getWeekOfWeekyear();
        year = nextWeek.getYear();
        System.out.println(nextWeek+" - Added one week");
    }
    public void thisWeek() {
        LocalDate thisWeek = new LocalDate()
                .withDayOfWeek(4)
                .withWeekOfWeekyear(week)
                .withYear(year);
        System.out.println(thisWeek+" - This is the current week");
    }
    public static void main(String[] args) {
        WonkyWeeks wonky = new WonkyWeeks();
        wonky.week = 2;
        wonky.year = 2015;
        wonky.thisWeek();
        wonky.backUpOneWeek();
        wonky.backUpOneWeek();
        wonky.backUpOneWeek();
        wonky.backUpOneWeek();
        wonky.forwardOneWeek();
        wonky.forwardOneWeek();
        wonky.forwardOneWeek();
        wonky.forwardOneWeek();
    }
}
like image 917
Jerri Kangasniemi Avatar asked Jun 23 '15 23:06

Jerri Kangasniemi


1 Answers

Joda-Time is correct but not your logic. You have to carefully make a difference between the "calendar year" (starting on first of January) and the year of the week-date (as defined in ISO-8601, also called the "week-based-year" or simply "week-year").

For example you use two members in your class which are not strictly correlated to each other to store intermediate results:

week = nextWeek.getWeekOfWeekyear();
year = nextWeek.getYear();

The problem with these lines is that the week is related to the week-based-year, not to the calendar year as the second line indicates. Keep in mind that the week-based-year can be one year less than the calendar year on first of January. For example [2014-12-31] is the same date as [2015-W01-3]. Keep also in mind that Joda-Time offers another method called getWeekyear().

Then you are going to use these two values to manipulate a date this way:

LocalDate today = new LocalDate()
        .withDayOfWeek(4)
        .withWeekOfWeekyear(week)
        .withYear(year);

Again the same terminology problem. And the method withWeekOfWeekyear(week) can already change the calendar year and shift the day-of-month to another day when trying to preserve the day-of-week within the current week-year 2015, not the calendar year 2014 producing an unexpected date shift! The whole code produces results which are not really predictable and will surprise everyone. Another big problem is the order of method calls which matters because the week-manipulation refers to the current week-year (which one?!). Following code would look much healthier:

LocalDate today = new LocalDate()
        .withWeekyear(year)
        .withWeekOfWeekyear(week)
        .withDayOfWeek(4);

Solution: You should better refer consequently to the week-year, not to the calendar year in your code. Or even better: If you just want to add or remove weeks then I suggest you to store the date (as object of type LocalDate) instead and to apply date.plusWeeks(1) or similar. You can always query the date for day-of-week, week-of-weekbased-year, week-year, calendar year etc. Much better than to save week-of-year and calendar year.

Update after testing:

I have now changed the year to the week-year and also changed the order of method calls when setting the date (first the week-year, then the week and finally the day-of-week). After these changes your code will work fine according to my own tests (although I still advise you to simplify your class state and logic). Here my full changed and corrected code:

import org.joda.time.LocalDate;

public class WonkyWeeks {
    int year;
    int week;

    public void backUpOneWeek() {
        LocalDate today =
            new LocalDate().withWeekyear(year).withWeekOfWeekyear(week).withDayOfWeek(4);
        LocalDate adayago = today.minusDays(1);
        System.out.println(adayago + " - removed one day");
        LocalDate twodaysago = adayago.minusDays(1);
        System.out.println(twodaysago + " - removed one day");
        LocalDate threedaysago = twodaysago.minusDays(1);
        System.out.println(threedaysago + " - removed one day");
        LocalDate fourdaysago = threedaysago.minusDays(1);
        System.out.println(fourdaysago + " - removed one day");
        LocalDate fivedaysago = fourdaysago.minusDays(1);
        System.out.println(fivedaysago + " - removed one day");
        LocalDate sixdaysago = fivedaysago.minusDays(1);
        System.out.println(sixdaysago + " - removed one day");
        LocalDate lastWeek = sixdaysago.minusDays(1);

        week = lastWeek.getWeekOfWeekyear();
        year = lastWeek.getWeekyear();
        System.out.println(lastWeek + " - Removed one full week");
    }

    public void forwardOneWeek() {
        LocalDate today =
            new LocalDate().withWeekyear(year).withWeekOfWeekyear(week).withDayOfWeek(4);
        LocalDate tomorrow = today.plusDays(1);
        System.out.println(tomorrow + " - added one day");
        LocalDate dayAfterTomorrow = tomorrow.plusDays(1);
        System.out.println(dayAfterTomorrow + " - added one day");
        LocalDate threeDaysFromNow = dayAfterTomorrow.plusDays(1);
        System.out.println(threeDaysFromNow + " - added one day");
        LocalDate fourDaysFromNow = threeDaysFromNow.plusDays(1);
        System.out.println(fourDaysFromNow + " - added one day");
        LocalDate fiveDaysFromNow = fourDaysFromNow.plusDays(1);
        System.out.println(fiveDaysFromNow + " - added one day");
        LocalDate sixDaysFromNow = fiveDaysFromNow.plusDays(1);
        System.out.println(sixDaysFromNow + " - added one day");
        LocalDate nextWeek = sixDaysFromNow.plusDays(1);

        week = nextWeek.getWeekOfWeekyear();
        year = nextWeek.getWeekyear();
        System.out.println(nextWeek + " - Added one week");
    }

    public void thisWeek() {
        LocalDate thisWeek =
            new LocalDate().withWeekyear(year).withWeekOfWeekyear(week).withDayOfWeek(4);
        System.out.println(thisWeek + " - This is the current week");
    }

    public static void main(String[] args) {
        WonkyWeeks wonky = new WonkyWeeks();
        wonky.week = 2;
        wonky.year = 2015;
        wonky.thisWeek();
        wonky.backUpOneWeek();
        wonky.backUpOneWeek();
        wonky.backUpOneWeek();
        wonky.backUpOneWeek();
        wonky.forwardOneWeek();
        wonky.forwardOneWeek();
        wonky.forwardOneWeek();
        wonky.forwardOneWeek();
    }
}

Output of the changed code:

2015-01-08 - This is the current week
2015-01-07 - removed one day
2015-01-06 - removed one day
2015-01-05 - removed one day
2015-01-04 - removed one day
2015-01-03 - removed one day
2015-01-02 - removed one day
2015-01-01 - Removed one full week
2014-12-31 - removed one day
2014-12-30 - removed one day
2014-12-29 - removed one day
2014-12-28 - removed one day
2014-12-27 - removed one day
2014-12-26 - removed one day
2014-12-25 - Removed one full week
2014-12-24 - removed one day
2014-12-23 - removed one day
2014-12-22 - removed one day
2014-12-21 - removed one day
2014-12-20 - removed one day
2014-12-19 - removed one day
2014-12-18 - Removed one full week
2014-12-17 - removed one day
2014-12-16 - removed one day
2014-12-15 - removed one day
2014-12-14 - removed one day
2014-12-13 - removed one day
2014-12-12 - removed one day
2014-12-11 - Removed one full week
2014-12-12 - added one day
2014-12-13 - added one day
2014-12-14 - added one day
2014-12-15 - added one day
2014-12-16 - added one day
2014-12-17 - added one day
2014-12-18 - Added one week
2014-12-19 - added one day
2014-12-20 - added one day
2014-12-21 - added one day
2014-12-22 - added one day
2014-12-23 - added one day
2014-12-24 - added one day
2014-12-25 - Added one week
2014-12-26 - added one day
2014-12-27 - added one day
2014-12-28 - added one day
2014-12-29 - added one day
2014-12-30 - added one day
2014-12-31 - added one day
2015-01-01 - Added one week
2015-01-02 - added one day
2015-01-03 - added one day
2015-01-04 - added one day
2015-01-05 - added one day
2015-01-06 - added one day
2015-01-07 - added one day
2015-01-08 - Added one week
like image 51
Meno Hochschild Avatar answered Nov 01 '22 00:11

Meno Hochschild