I have a problem using java.util.Calendar and commons-lang DateUtil
The problem is that my test works correctly on local machine and fails on CloudBees. Seems like there are problems with locales, but I’m not sure.
Here is the code:
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
//bla-bla-bla
public static final String TEST_DATE_AS_STRING = "13 10 2012 20:50:44";
public static final int MILLIS_IN_HOUR = 3600000;
private static final String LEAP_WEEK_DATE_AS_STRING = "31 10 2012 20:50:44";
private final SimpleDateFormat sdf = new SimpleDateFormat("dd MM yyyy HH:mm:ss");
@Test
public void getWeekDatePair() throws ParseException{
Date date = sdf.parse(TEST_DATE_AS_STRING);
DatePair dp = Util.DateTime.getWeekDatePair(date);
Assert.assertEquals(sdf.format(new Date(dp.getStart())), "08 10 2012 00:00:00");
//java.lang.AssertionError: expected [14 10 2012 00:00:00] but found [07 10 2012 00:00:00]
Assert.assertEquals(sdf.format(new Date(dp.getEnd())), "14 10 2012 00:00:00");
}
@Test
public void getLeapWeekDatePair() throws ParseException {
Date leapDate = sdf.parse(LEAP_WEEK_DATE_AS_STRING);
DatePair dp = Util.DateTime.getWeekDatePair(leapDate);
Assert.assertEquals(sdf.format(new Date(dp.getStart())), "29 10 2012 00:00:00");
//java.lang.AssertionError: expected [04 11 2012 00:00:00] but found [28 10 2012 00:00:00]
Assert.assertEquals(sdf.format(new Date(dp.getEnd())), "04 11 2012 00:00:00");
}
Here is failed test output:
java.lang.AssertionError: expected [04 11 2012 00:00:00] but found [28 10 2012 00:00:00]
at org.testng.Assert.fail(Assert.java:94)
at org.testng.Assert.failNotEquals(Assert.java:494)
at org.testng.Assert.assertEquals(Assert.java:123)
at org.testng.Assert.assertEquals(Assert.java:176)
at org.testng.Assert.assertEquals(Assert.java:186)
at ru.rating.utils.UtilDateTimeTest.getLeapWeekDatePair(UtilDateTimeTest.java:77)
expected [14 10 2012 00:00:00] but found [07 10 2012 00:00:00]
Stacktrace
java.lang.AssertionError: expected [14 10 2012 00:00:00] but found [07 10 2012 00:00:00]
at org.testng.Assert.fail(Assert.java:94)
at org.testng.Assert.failNotEquals(Assert.java:494)
at org.testng.Assert.assertEquals(Assert.java:123)
at org.testng.Assert.assertEquals(Assert.java:176)
at org.testng.Assert.assertEquals(Assert.java:186)
at ru.rating.utils.UtilDateTimeTest.getWeekDatePair(UtilDateTimeTest.java:69)
Here is implementation:
public static DatePair getWeekDatePair(){
return getWeekDatePair(new Date());
}
/**
* This is test method
* */
static DatePair getWeekDatePair( Date date){
Date truncDay = truncate(date.getTime(), Calendar.DAY_OF_MONTH);
Calendar calStart = getCalendarInstance(date, Calendar.DAY_OF_MONTH);
calStart.setTime(truncDay);
calStart.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
Calendar calEnd = Calendar.getInstance();
calEnd.setTime(calStart.getTime());
calEnd.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
return new DatePair(calStart.getTime(), calEnd.getTime());
}
public static Date truncate(long date, int calField) {
Calendar cal = getCalendarInstance(new Date(date), calField);
cal = DateUtils.truncate(cal, calField);
return cal.getTime();
}
static Calendar getCalendarInstance(Date date, int calendarField){
//Calendar cal = Calendar.getInstance();
Calendar cal = new GregorianCalendar(Locale.ENGLISH);
cal.setTime(date);
if(calendarField!=Calendar.HOUR){
cal.set(Calendar.HOUR_OF_DAY, 0);
}
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
return cal;
}
Although we are missing key piece of information here (how is
Util.DateTime.getWeekDatePair(java.util.Date)implemented), I suspect that what you do there is instantiatingjava.util.Calendarusing defaultLocaleand then search for first day of the week.My suspicion came from the fact, that you don’t pass the Locale instance to the
getWeekDatePair()method.Now, what is the problem here? Well, the first day of the week depends on the Locale. Therefore when you instantiate the
Calendarlike this:Calendar.getInstance(), what you in fact do is:Calendar.getInstance(Locale.getDefault(Locale.Category.FORMAT). And of course the first day of the week may differ on two different machines, because the Locales may differ.For example, first day of the week is Sunday in US, but Monday in Poland (I believe it is like that in Russia, isn’t it?) Therefore if you do this test on two different machines, fist of which has Locale set to en-US and second to ru-RU, you may expect different results.
If it is only the problem of tests, you may just as well set default Locale and everything should be working just fine. However, please keep in mind that if you are testing web application, using default Locale is a bad thing, as it will return server Locale rather than the one that comes from web browser (or some user profile if you have one). Should this Locales differ, you might use something that is confusing for end user.
Edit
It is quite obvious why it happens from the implementation, and I gave you the hints previously. Consider this code:
This prints (correctly):
Now, let’s change the Calendar instantiation to:
Voila, now you’ll see:
To see why this is the correct behavior, let’s test this code as well:
For English Locale, it will return 1 (Sunday) as oppose to 2 (Monday) which is the result for Russian Locales. This code behaves correctly, as it returns Monday and Sunday from given week. The only “problem” is the fact, that week means something else all over the world.
As you can see, it has nothing to do with DateUtils, it is merely related to Calendar’s behavior. And because of this code:
Calendar cal = new GregorianCalendar(Locale.ENGLISH);the behavior should in fact be consistent, so you should always get an error no matter what machine you are testing the code on. It it is not, I really can’t understand why.Depending on what you are trying to achieve, it may make sense to add Locale as a parameter to your method (and instantiate Calendar accordingly), then write some tests covering few Locales (some Arabic Locale may also be interesting as nobody said first day of the week has to be either Sunday or Monday) or merely modifying test dates so that they match the correct Calendar’s behavior.