Question originally posted on Stack Overflow here.
I'm trying to do an elegant round method that only use the JDK methods and leverage the TimeUnit
class of that JDK.
/**
* Get the date rounded to the given unit
* @param date
* @param unit
* @return the rounded value
*/
public static Date round(Date date,TimeUnit unit) {
long dateInMillies = date.getTime();
long tzOffset = TimeZone.getDefault().getOffset(dateInMillies);
long dateInMilliesWithoutOffset = dateInMillies + tzOffset;
long dateInUnit = unit.convert(dateInMilliesWithoutOffset,TimeUnit.MILLISECONDS);
long dateInMilliesRoundedWithoutOffset = unit.toMillis(dateInUnit);
long dateInMilliesRoundedWithOffset = dateInMilliesRoundedWithoutOffset - tzOffset;
return new Date(dateInMilliesRoundedWithOffset);
}
And my unit test is:
@Test
public void testRoundDate() {
Date currentDayDate = new Date();
Date currentDayBegin = DateUtils.getDayBegin(currentDayDate); // returns 00h00:00 and 000 milliseconds
Date currentDayEnd = DateUtils.getDayEnd(currentDayDate,TimeUnit.MILLISECONDS); // returns 23h59:59 and 999 milliseconds
//
final Calendar initialCalBegin = Calendar.getInstance();
initialCalBegin.setTime(currentDayBegin);
final Calendar initialCalEnd = Calendar.getInstance();
initialCalEnd.setTime(currentDayEnd);
//
Calendar testedCalendar = Calendar.getInstance();
//
testedCalendar.setTime( DateUtils.round(currentDayBegin, TimeUnit.DAYS) );
Assert.assertEquals( testedCalendar.get(Calendar.DAY_OF_YEAR), initialCalBegin.get(Calendar.DAY_OF_YEAR) );
Assert.assertEquals( testedCalendar.get(Calendar.HOUR_OF_DAY), 0 );
Assert.assertEquals( testedCalendar.get(Calendar.MINUTE), 0 );
Assert.assertEquals( testedCalendar.get(Calendar.SECOND), 0 );
Assert.assertEquals( testedCalendar.get(Calendar.MILLISECOND), 0 );
testedCalendar.setTime( DateUtils.round(currentDayEnd, TimeUnit.DAYS) );
Assert.assertEquals( testedCalendar.get(Calendar.DAY_OF_YEAR), initialCalEnd.get(Calendar.DAY_OF_YEAR) );
Assert.assertEquals( testedCalendar.get(Calendar.HOUR_OF_DAY), 0 );
Assert.assertEquals( testedCalendar.get(Calendar.MINUTE), 0 );
Assert.assertEquals( testedCalendar.get(Calendar.SECOND), 0 );
Assert.assertEquals( testedCalendar.get(Calendar.MILLISECOND), 0 );
//
testedCalendar.setTime( DateUtils.round(currentDayBegin, TimeUnit.HOURS) );
Assert.assertEquals( testedCalendar.get(Calendar.DAY_OF_YEAR), initialCalBegin.get(Calendar.DAY_OF_YEAR) );
Assert.assertEquals( testedCalendar.get(Calendar.HOUR_OF_DAY), initialCalBegin.get(Calendar.HOUR_OF_DAY) );
Assert.assertEquals( testedCalendar.get(Calendar.MINUTE), 0 );
Assert.assertEquals( testedCalendar.get(Calendar.SECOND), 0 );
Assert.assertEquals( testedCalendar.get(Calendar.MILLISECOND), 0 );
testedCalendar.setTime( DateUtils.round(currentDayEnd, TimeUnit.HOURS) );
Assert.assertEquals( testedCalendar.get(Calendar.DAY_OF_YEAR), initialCalEnd.get(Calendar.DAY_OF_YEAR) );
Assert.assertEquals( testedCalendar.get(Calendar.HOUR_OF_DAY), initialCalEnd.get(Calendar.HOUR_OF_DAY) );
Assert.assertEquals( testedCalendar.get(Calendar.MINUTE), 0 );
Assert.assertEquals( testedCalendar.get(Calendar.SECOND), 0 );
Assert.assertEquals( testedCalendar.get(Calendar.MILLISECOND), 0 );
//
testedCalendar.setTime( DateUtils.round(currentDayBegin, TimeUnit.MINUTES) );
Assert.assertEquals( testedCalendar.get(Calendar.DAY_OF_YEAR), initialCalBegin.get(Calendar.DAY_OF_YEAR) );
Assert.assertEquals( testedCalendar.get(Calendar.HOUR_OF_DAY), initialCalBegin.get(Calendar.HOUR_OF_DAY) );
Assert.assertEquals( testedCalendar.get(Calendar.MINUTE), initialCalBegin.get(Calendar.MINUTE) );
Assert.assertEquals( testedCalendar.get(Calendar.SECOND), 0 );
Assert.assertEquals( testedCalendar.get(Calendar.MILLISECOND), 0 );
testedCalendar.setTime( DateUtils.round(currentDayEnd, TimeUnit.MINUTES) );
Assert.assertEquals( testedCalendar.get(Calendar.DAY_OF_YEAR), initialCalEnd.get(Calendar.DAY_OF_YEAR) );
Assert.assertEquals( testedCalendar.get(Calendar.HOUR_OF_DAY), initialCalEnd.get(Calendar.HOUR_OF_DAY) );
Assert.assertEquals( testedCalendar.get(Calendar.MINUTE), initialCalEnd.get(Calendar.MINUTE) );
Assert.assertEquals( testedCalendar.get(Calendar.SECOND), 0 );
Assert.assertEquals( testedCalendar.get(Calendar.MILLISECOND), 0 );
//
testedCalendar.setTime( DateUtils.round(currentDayBegin, TimeUnit.SECONDS) );
Assert.assertEquals( testedCalendar.get(Calendar.DAY_OF_YEAR), initialCalBegin.get(Calendar.DAY_OF_YEAR) );
Assert.assertEquals( testedCalendar.get(Calendar.HOUR_OF_DAY), initialCalBegin.get(Calendar.HOUR_OF_DAY) );
Assert.assertEquals( testedCalendar.get(Calendar.MINUTE), initialCalBegin.get(Calendar.MINUTE) );
Assert.assertEquals( testedCalendar.get(Calendar.SECOND), initialCalBegin.get(Calendar.SECOND) );
Assert.assertEquals( testedCalendar.get(Calendar.MILLISECOND), 0 );
testedCalendar.setTime( DateUtils.round(currentDayEnd, TimeUnit.SECONDS) );
Assert.assertEquals( testedCalendar.get(Calendar.DAY_OF_YEAR), initialCalEnd.get(Calendar.DAY_OF_YEAR) );
Assert.assertEquals( testedCalendar.get(Calendar.HOUR_OF_DAY), initialCalEnd.get(Calendar.HOUR_OF_DAY) );
Assert.assertEquals( testedCalendar.get(Calendar.MINUTE), initialCalEnd.get(Calendar.MINUTE) );
Assert.assertEquals( testedCalendar.get(Calendar.SECOND), initialCalEnd.get(Calendar.SECOND) );
Assert.assertEquals( testedCalendar.get(Calendar.MILLISECOND), 0 );
//
testedCalendar.setTime( DateUtils.round(currentDayBegin, TimeUnit.MILLISECONDS) );
Assert.assertEquals( testedCalendar.get(Calendar.DAY_OF_YEAR), initialCalBegin.get(Calendar.DAY_OF_YEAR) );
Assert.assertEquals( testedCalendar.get(Calendar.HOUR_OF_DAY), initialCalBegin.get(Calendar.HOUR_OF_DAY) );
Assert.assertEquals( testedCalendar.get(Calendar.MINUTE), initialCalBegin.get(Calendar.MINUTE) );
Assert.assertEquals( testedCalendar.get(Calendar.SECOND), initialCalBegin.get(Calendar.SECOND) );
Assert.assertEquals( testedCalendar.get(Calendar.MILLISECOND), initialCalBegin.get(Calendar.MILLISECOND) );
testedCalendar.setTime( DateUtils.round(currentDayEnd, TimeUnit.MILLISECONDS) );
Assert.assertEquals( testedCalendar.get(Calendar.DAY_OF_YEAR), initialCalEnd.get(Calendar.DAY_OF_YEAR) );
Assert.assertEquals( testedCalendar.get(Calendar.HOUR_OF_DAY), initialCalEnd.get(Calendar.HOUR_OF_DAY) );
Assert.assertEquals( testedCalendar.get(Calendar.MINUTE), initialCalEnd.get(Calendar.MINUTE) );
Assert.assertEquals( testedCalendar.get(Calendar.SECOND), initialCalEnd.get(Calendar.SECOND) );
Assert.assertEquals( testedCalendar.get(Calendar.MILLISECOND), initialCalEnd.get(Calendar.MILLISECOND) );
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// The TimeUnits under the millisecond precision should not procude any change to the date
Assert.assertEquals(DateUtils.round(currentDayBegin, TimeUnit.MICROSECONDS) , currentDayBegin);
Assert.assertEquals(DateUtils.round(currentDayEnd, TimeUnit.MICROSECONDS) , currentDayEnd);
Assert.assertEquals(DateUtils.round(currentDayDate, TimeUnit.MICROSECONDS) , currentDayDate);
//
Assert.assertEquals(DateUtils.round(currentDayBegin, TimeUnit.NANOSECONDS) , currentDayBegin);
Assert.assertEquals(DateUtils.round(currentDayEnd, TimeUnit.NANOSECONDS) , currentDayEnd);
Assert.assertEquals(DateUtils.round(currentDayDate, TimeUnit.NANOSECONDS) , currentDayDate);
}
It seems what I've done works fine and is quite elegant. However, I'm not used to using time zones, and I wonder if this code works fine on very specific cases. For example, do you see something that could happen a given day, for a country that has an offset change in its timezone? Please tell me if you think something is wrong in my code.
Can I safely create a new method that takes a TimeZone
parameter and can thus round a date to a given unit on a given timezone?