Another disadvantage of starting with complicated stuff is that you may miss some simplifications the way I did above. Concerning correctness, all the divisions and comparisons with 356 and 30 are slightly wrong.
Note also that the task is not exactly defined. Given the dates 2000-12-31T23:23:00
and 2001-01-01T00:11:22
, multiple answers are correct:
- "48 minutes from now"
- "nearly one hour from now"
- "tomorrow"
- "next month"
- "next year"
And maybe also "next century" and "next millennium", but both are disputable (and irrelevant most of the time).
There's no clear solution for this and you should specify the behavior precisely and cover it by tests. The most straightforward solution would be to look at the calendar year first, which would give us the answer "next year". Pretty impractical, but every other solution needs some arbitrary choices.
I'd probably start with
String timeDifferenceString(long currentTimeMillis, long otherMillis) {
final boolean isPast = otherMillis < currentTimeMillis;
final Calendar first = Calendar.getInstance();
first.setTimeInMillis(Math.min(currentTimeMillis, otherMillis));
final Calendar second = Calendar.getInstance();
second.setTimeInMillis(Math.max(currentTimeMillis, otherMillis));
return timeDifferenceString(first, second, isPast);
}
so that no Math.abs
is needed anymore. Obviously, Joda-Time or the corresponding JDK8 classes are a better choice than Calendar
, but let's keep it simple for this answer.
Now you can write
private String timeDifferenceString(Calendar first, Calendar second, boolean isPast) {
final String agoOrFromNow = isPast ? "ago" : "from now";
final String lastOrNext = isPast ? "last " : "next ";
final int years = second.get(Calendar.YEAR) - first.get(Calendar.YEAR);
if (years > 1) {
return years + " years " + agoOrFromNow;
} else if (years > 0) {
return lastOrNext + "year";
}
final int months = second.get(Calendar.MONTH) - first.get(Calendar.MONTH);
if (months > 1) {
return months + " months " + agoOrFromNow;
} else if (years > 0) {
return lastOrNext + "month";
}
...
return "now";
}
This leads to a rather lengthy but trivial method. Now you may want to split it as suggested by h.j.k. This is not easy, as the parts above only conditionally return something. Using a guard condition, this can be solved like this
if (years > 0) {
if (years > 1) {
return years + " years " + agoOrFromNow;
} else if (years > 0) {
return lastOrNext + "year";
} else {
return "this year";
}
}
After extracting the method, you'll see that it's about the same for every time unit, so you may want to generalize it to
private String timeDifferenceString(int count, String unitName, boolean isPast) {
if (count > 1) {
return count + " " + unitName + "s " + agoOrFromNow(isPast);
} else if (count > 0) {
return lastOrNext(isPast) + " " + unitName;
} else {
return "this " + unitName;
}
}
Note that this won't work for other languages for many reasons including pluralization rules.
Now, you have a few trivial methods and one important one looking like
private String timeDifferenceString(Calendar first, Calendar second, boolean isPast) {
final int years = second.get(Calendar.YEAR) - first.get(Calendar.YEAR);
if (years > 0) {
return timeDifferenceString(years, "year", isPast);
}
final int months = second.get(Calendar.MONTH) - first.get(Calendar.MONTH);
if (months > 0) {
return timeDifferenceString(months, "month", isPast);
}
...
return "now";
}
This is the right time for making changes, allowing you to output "48 minutes from now" instead of "next year" in my above example.