There's no built-in function for that in Java, but it's not so hard to do it with the existing API's. First you need to get the difference from today (in days), and choose the proper string based on moment.js rules:
Last week Last Monday at 2:30 AM
The day before Yesterday at 2:30 AM
The same day Today at 2:30 AM
The next day Tomorrow at 2:30 AM
The next week Sunday at 2:30 AM
Everything else 7/10/2011
In Joda-Time, you can use the org.joda.time.Days
class to get the difference in days. One detail is that I'm considering just the date (day/month/year) and ignoring the time (hour/minute/second) to get the difference (but you can adjust it to your needs). The method will be like this:
public String calendar(DateTime dt) {
StringBuilder sb = new StringBuilder();
// check difference in days from today, considering just the date (ignoring the hours)
int days = Days.daysBetween(new LocalDate(), dt.toLocalDate()).getDays();
if (days == 0) { // today
sb.append("Today ");
} else if (days == 1) { // tomorrow
sb.append("Tomorrow ");
} else if (days == -1) { // yesterday
sb.append("Yesterday ");
} else if (days > 0 && days < 7) { // next week
sb.append(dt.dayOfWeek().getAsText(Locale.ENGLISH)).append(" ");
} else if (days < 0 && days > -7) { // last week
sb.append("Last ").append(dt.dayOfWeek().getAsText(Locale.ENGLISH)).append(" ");
}
if (Math.abs(days) < 7) { // difference is less than a week, append current time
sb.append("at ").append(dt.toString("h:mm a", Locale.ENGLISH));
} else { // more than a week of difference
sb.append(dt.toString("M/d/yyyy"));
}
return sb.toString();
}
I'm using a org.joda.time.LocalDate
to get the difference, so the time will be ignored (if you use a DateTime
instead, the difference is only 1 day if it has passed more than 24 hours, so change the code according to what you need).
I also used Math.abs(days) < 7
to consider if the difference is more than a week, but I'm not sure if moment.js
considers <= 7
.
Anyway, some examples of usage:
DateTime now = new DateTime();
System.out.println(calendar(now.minusDays(10))); // 9/22/2017
System.out.println(calendar(now.minusDays(6))); // Last Tuesday at 9:34 AM
System.out.println(calendar(now.minusDays(3))); // Last Friday at 9:34 AM
System.out.println(calendar(now.minusDays(1))); // Yesterday at 9:34 AM
System.out.println(calendar(now)); // Today at 9:34 AM
System.out.println(calendar(now.plusDays(1))); // Tomorrow at 9:34 AM
System.out.println(calendar(now.plusDays(3))); // Thursday at 9:34 AM
System.out.println(calendar(now.plusDays(10))); // 10/12/2017
The output is (considering that today is October 2nd 2017, and I ran the code at 9:34 AM in my local time):
9/22/2017
Last Tuesday at 9:34 AM
Last Friday at 9:34 AM
Yesterday at 9:34 AM
Today at 9:34 AM
Tomorrow at 9:34 AM
Thursday at 9:34 AM
10/12/2017
You can also modify the method to take a reference date to compare with (instead of using a hardcoded new LocalDate()
inside the method).
Java new Date/Time API
Joda-Time is in maintainance mode and is being replaced by the new APIs, so I don't recommend start a new project with it. Even in joda's website it says: "Note that Joda-Time is considered to be a largely “finished” project. No major enhancements are planned. If using Java SE 8, please migrate to java.time (JSR-310).".
If you're using Java 8, consider using the new java.time API. It's easier, less bugged and less error-prone than the old APIs.
If you're using Java 6 or 7, you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, you'll also need the ThreeTenABP (more on how to use it here).
The code below works for both.
The only difference is the package names (in Java 8 is java.time
and in ThreeTen Backport (or Android's ThreeTenABP) is org.threeten.bp
), but the classes and methods names are the same.
This API is very similar to Joda-Time, so the code has minor differences:
static DateTimeFormatter HOUR_FORMAT = DateTimeFormatter.ofPattern("h:mm a", Locale.ENGLISH);
static DateTimeFormatter MDY_FORMAT = DateTimeFormatter.ofPattern("M/d/yyyy");
public String calendar(ZonedDateTime dt) {
StringBuilder sb = new StringBuilder();
// check difference in days from today, considering just the date (ignoring the hours)
long days = ChronoUnit.DAYS.between(LocalDate.now(), dt.toLocalDate());
if (days == 0) { // today
sb.append("Today ");
} else if (days == 1) { // tomorrow
sb.append("Tomorrow ");
} else if (days == -1) { // yesterday
sb.append("Yesterday ");
} else if (days > 0 && days < 7) { // next week
sb.append(dt.getDayOfWeek().getDisplayName(TextStyle.FULL, Locale.ENGLISH)).append(" ");
} else if (days < 0 && days > -7) { // last week
sb.append("Last ").append(dt.getDayOfWeek().getDisplayName(TextStyle.FULL, Locale.ENGLISH)).append(" ");
}
if (Math.abs(days) < 7) { // difference is less than a week, append current time
sb.append("at ").append(dt.format(HOUR_FORMAT));
} else { // more than a week of difference
sb.append(dt.format(MDY_FORMAT));
}
return sb.toString();
}
I used a ZonedDateTime
, which is an equivalent of Joda's DateTime
(it represents a date and time in a timezone).
When calling DAYS.between
, I converted it to a LocalDate
, so the comparison considers only the date (day/month/year). If I used a ZonedDateTime
instead, the time would also be considered, so the result would be 1 day only if it has passed more than 24 hours (so you can change it according to your needs).
Note that I also had to create 2 instances of DateTimeFormatter
. I created them outside of the method, so they can be reused (no need to create them all the time inside the method). Joda-Time didn't need it because the toString
method in Joda's objects can take a pattern and internally creates a formatter, while in java.time
API you must create the formatter explicity.
Using it is also similar to Joda-Time:
ZonedDateTime now = ZonedDateTime.now();
System.out.println(calendar(now.minusDays(10))); // 9/22/2017
System.out.println(calendar(now.minusDays(6))); // Last Tuesday at 9:34 AM
System.out.println(calendar(now.minusDays(3))); // Last Friday at 9:34 AM
System.out.println(calendar(now.minusDays(1))); // Yesterday at 9:34 AM
System.out.println(calendar(now)); // Today at 9:34 AM
System.out.println(calendar(now.plusDays(1))); // Tomorrow at 9:34 AM
System.out.println(calendar(now.plusDays(3))); // Thursday at 9:34 AM
System.out.println(calendar(now.plusDays(10))); // 10/12/2017
Just a note about timezones. In java.time
API, all the date classes has a no-arg now()
method that gets the current date/time in the JVM's default timezone.
Although it's very conveninent, it also has some drawbacks, because the default timezone can be changed without notice, even at runtime. It's better to specify which timezone you want, if possible.
The API uses IANA timezones names (always in the format Region/City
, like America/New_York
or Europe/Paris
).
Avoid using the 3-letter abbreviations (like CET
or EST
) because they are ambiguous and not standard.
You can get a list of available timezones (and choose the one that fits best your system) by calling ZoneId.getAvailableZoneIds()
.
To get the current date/time in a specific timezone, use the ZoneId
class:
// get the current date/time in a specific timezone
ZonedDateTime.now(ZoneId.of("Europe/Paris"));