2015-08-31 2 views
2

У меня есть класс, где я вычислить разницу в годах между текущей датой (now) и некоторыми другими датами (birthday):Почему это Joda-Time связано с ошибкой иногда?

import java.util.Date; 

import org.joda.time.LocalDate; 
import org.joda.time.Years; 

public class Logic { 
    private final Date now; 

    public Logic(final Date curDate) { 
     now = curDate; 
    } 

    [...] 

    protected int getAgeInYears(final Date now, final Date birthday) { 
     final LocalDate nowDate = new LocalDate(now.getTime()); 
     final LocalDate birthdayDate = new LocalDate(birthday.getTime()); 
     return Years.yearsBetween(birthdayDate, nowDate).getYears(); 
    } 
} 

Тогда у меня есть юнят тесты, которые проверяют getAgeInYears:

public class SomeTest { 
    @Test 
    public void test() { 
     final LocalDate now = new LocalDate(2015, 8, 28); 
     final Logic objectUnderTest = 
      new Logic(now.toDate()); 
     final LocalDate birthDay1 = new LocalDate(2015 - 7, 8, 28); 
     Assert.assertEquals(6, 
      objectUnderTest.getAgeInYears(now.toDate(), birthDay1.toDate())); // This assertion fails sometimes, but not always 
     final LocalDate birthDay2 = new LocalDate(2015 - 7, 8, 27); 
     Assert.assertEquals(7, 
      objectUnderTest.getAgeInYears(now.toDate(), birthDay2.toDate())); 
     final LocalDate birthDay3 = new LocalDate(1981, 4, 24); 
     Assert.assertEquals(34, 
      objectUnderTest.getAgeInYears(now.toDate(), birthDay3.toDate())); 

    } 
} 

утверждение

Assert.assertEquals(6, 
    objectUnderTest.getAgeInYears(now.toDate(), birthDay1.toDate())); 

не удается иногда.

На моей машине тест проходит без ошибок. На одном из серверов сборки (Java 6) этот тест завершился неудачно. Когда я перестроил код локально с помощью Java 6, тест не сработал.

Что может вызвать это странное поведение?

Обновление 1: Сообщение об ошибке expected:<6> but was:<7>.

Update 2: я переписал код, чтобы избавиться от toDate звонков и получил это:

public class Logic { 
    private final LocalDate now; 

    public Logic(final LocalDate curDate) { 
     now = curDate; 
    } 

    [...] 

    protected int getAgeInYears(final LocalDate now, final LocalDate birthday) { 
     final LocalDate nowDate = new LocalDate(now); 
     final LocalDate birthdayDate = new LocalDate(birthday); 
     return Years.yearsBetween(birthdayDate, nowDate).getYears(); 
    } 
} 

public class LogicTest { 
    @Test 
    public void getAgeInYearsSunnyDay() { 
     final LocalDate now = new LocalDate(2015, 8, 28); 
     final AdditionalServicesLogic objectUnderTest = 
      new AdditionalServicesLogic(now); 
     final LocalDate birthDay1 = new LocalDate(2015 - 7, 8, 28); 
     Assert.assertEquals(7, 
      objectUnderTest.getAgeInYears(now, birthDay1)); 
     final LocalDate birthDay2 = new LocalDate(2015 - 7, 8, 27); 
     Assert.assertEquals(6, 
      objectUnderTest.getAgeInYears(now, birthDay2)); 
     final LocalDate birthDay3 = new LocalDate(1981, 4, 24); 
     Assert.assertEquals(34, 
      objectUnderTest.getAgeInYears(now, birthDay3)); 

    } 
} 

Теперь я постоянно получаю ошибку утверждение на линии

Assert.assertEquals(6, 
    objectUnderTest.getAgeInYears(now, birthDay2)); 

Я ожидаю, чтобы получить 6, но вместо этого получите 7.

+0

Каковы значения (EDIT: _actual_) этих переменных? – Kai

+0

@ user714965 'now = new LocalDate (2015, 8, 28)', 'birthDay1 = new LocalDate (2015 - 7, 8, 28)' –

+1

Вот как вы их инициализируете. Я хотел бы призвать вас, возможно, напечатать фактические значения в тестовом журнале. – Kai

ответ

1

Вы должны удалить java.util.Date со своего класса Logic и использовать только классы йоды. Это сделает ваш код намного понятнее, потому что нет необходимости конвертировать между LocalDate и Date. На самом деле это делает ваш тест бессмысленным, поскольку вы только проверите Years.yearsBetween времени joda, которое я ожидаю, что его уже проверили.

Я подозреваю, что ошибка может быть вокруг преобразования LocalDate/Date, вы должны проверить это, печатая фактические значения now.toDate() и birthDay1.toDate() в журнал испытаний. И есть взгляд на Javadocs из LocalDate.toDate:

Преобразования в Дату JDK полно осложнения, как JDK Дата конструктора не ведут себя, как вы могли бы ожидать около ДСТЫХ переходов. Этот метод работает, беря первое предположение, а затем корректируя дату JDK , пока он не получит самый ранний действительный момент. Это также относится к ситуации , где данные часового пояса JDK отличаются от данных зоны Joda-Time .

+0

Спасибо. Удаление 'toDate' частично разрешило проблему, но не полностью. См. Обновление 2. Предположим, у меня родился ребенок, родившийся 28.08.2008. 27 августа 2015 года ему 6 лет. 28.08.2015 (его или ее день рождения), ребенку становится 7 лет. Соответствующий тест не выполняется (см. Обновление 2). –

+2

Хм, может быть, 'Years.yearsBetween' не пересматривает полную дату? Кажется, что он смотрит только на 2015-2008 гг. = ** 7 ** – Kai

+0

Нет, 'Years.yearsBetween' правильно смотрит на месяц и день месяца. Просто проверьте на 2008-08-27/2015-08-28 (=> 7 лет), 2008-08-28/2015-08-28 (=> 7 лет) и 2008-08-28/2015-08-27 (=> 6 лет) Я думаю, что ОП только что смутился, какой день месяца принадлежит той стороне сравнения. –

1

Joda время является правильным держать свой код в виде:

Вы явно установить birthDay2 в 2008-08-27. А теперь ваша переменная - дата 2015-08-28. Поэтому, если вы добавите 7 лет до birthDay2, вы получите дату 2015-08-27, которая будет на день раньше, чем 2015-08-28.

Результат 7 лет не изменится, если у вас есть = 2015-08-27 (тогда разница составляет ровно 7 лет).

6 лет как ожидалось, только если вы установили birthDay2 в 2008-08-28 и сейчас = 2015-08-27 (потому что здесь сравнение числа дней месяца предотвращает полную 7-летнюю дельта) ,

Небольшая заметка:

Существует один частный случай вокруг високосных дней, необходим уход (здесь я думаю Joda время неправильно) - см также это закрыто issue.

Смежные вопросы