Java中没有内置的函数来实现这个功能,但使用现有的API并不难。首先,您需要获取今天与目标日期之间的差值(以天为单位),然后根据
moment.js规则选择适当的字符串。
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
在Joda-Time中,您可以使用
org.joda.time.Days
类来获取天数差异。一个细节是我只考虑日期(日/月/年),而忽略时间(时/分/秒)以获取差异(但您可以根据需要进行调整)。该方法将如下所示:
public String calendar(DateTime dt) {
StringBuilder sb = new StringBuilder();
int days = Days.daysBetween(new LocalDate(), dt.toLocalDate()).getDays();
if (days == 0) {
sb.append("Today ");
} else if (days == 1) {
sb.append("Tomorrow ");
} else if (days == -1) {
sb.append("Yesterday ");
} else if (days > 0 && days < 7) {
sb.append(dt.dayOfWeek().getAsText(Locale.ENGLISH)).append(" ");
} else if (days < 0 && days > -7) {
sb.append("Last ").append(dt.dayOfWeek().getAsText(Locale.ENGLISH)).append(" ");
}
if (Math.abs(days) < 7) {
sb.append("at ").append(dt.toString("h:mm a", Locale.ENGLISH));
} else {
sb.append(dt.toString("M/d/yyyy"));
}
return sb.toString();
}
我正在使用org.joda.time.LocalDate
来获取差异,因此时间将被忽略(如果您使用DateTime
,则如果已经过了24小时,则差异仅为1天,因此根据您的需要更改代码)。
我还使用了Math.abs(days) < 7
来考虑差异是否超过一周,但我不确定moment.js
是否考虑<= 7
。
无论如何,以下是一些用法示例:
DateTime now = new DateTime();
System.out.println(calendar(now.minusDays(10)));
System.out.println(calendar(now.minusDays(6)));
System.out.println(calendar(now.minusDays(3)));
System.out.println(calendar(now.minusDays(1)));
System.out.println(calendar(now));
System.out.println(calendar(now.plusDays(1)));
System.out.println(calendar(now.plusDays(3)));
System.out.println(calendar(now.plusDays(10)));
输出结果如下(假设今天是2017年10月2日,而且我在当地时间上午9:34运行了代码):
2017年9月22日
上周二上午9:34
上周五上午9:34
昨天上午9:34
今天上午9:34
明天上午9:34
下周四上午9:34
2017年10月12日
你也可以修改方法,以使用参考日期进行比较(而不是在方法内部使用硬编码的new LocalDate()
)。
Java新的日期/时间API
Joda-Time正在维护模式中,并将被新API取代,因此我不建议使用它开始新项目。即使在joda的网站上也说:“请注意,Joda-Time被认为是一个基本“完成”的项目。没有计划进行重大增强。如果使用Java SE 8,请迁移到java.time(JSR-310)。”。
如果您使用Java 8,请考虑使用新的java.time API。它比旧API更简单,更少出错和错误机率更小。
如果您正在使用Java 6或7,您可以使用
ThreeTen Backport,这是Java 8新日期/时间类的一个很好的后移版本。而对于Android,您还需要
ThreeTenABP(更多关于如何使用它的信息
在此处)。
下面的代码适用于两者。唯一的区别是包名称(在Java 8中为
java.time
,在ThreeTen Backport(或Android的ThreeTenABP)中为
org.threeten.bp
),但类和方法
名称相同。
此API与Joda-Time非常相似,因此代码有轻微差异:
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();
long days = ChronoUnit.DAYS.between(LocalDate.now(), dt.toLocalDate());
if (days == 0) {
sb.append("Today ");
} else if (days == 1) {
sb.append("Tomorrow ");
} else if (days == -1) {
sb.append("Yesterday ");
} else if (days > 0 && days < 7) {
sb.append(dt.getDayOfWeek().getDisplayName(TextStyle.FULL, Locale.ENGLISH)).append(" ");
} else if (days < 0 && days > -7) {
sb.append("Last ").append(dt.getDayOfWeek().getDisplayName(TextStyle.FULL, Locale.ENGLISH)).append(" ");
}
if (Math.abs(days) < 7) {
sb.append("at ").append(dt.format(HOUR_FORMAT));
} else {
sb.append(dt.format(MDY_FORMAT));
}
return sb.toString();
}
我使用了一个ZonedDateTime
,它是Joda的DateTime
的等价物(它表示时区中的日期和时间)。
在调用DAYS.between
时,我将其转换为LocalDate
,因此比较仅考虑日期(日/月/年)。如果我使用ZonedDateTime
,则还会考虑时间,因此只有超过24小时才会得出1天的结果(因此您可以根据需要进行更改)。
请注意,我还必须创建2个DateTimeFormatter
实例。我在方法外创建它们,以便可以重复使用它们(无需在方法内部一直创建它们)。 Joda-Time不需要它,因为Joda对象中的toString
方法可以采用模式并在内部创建格式化程序,而在java.time
API中,您必须显式创建格式化程序。
使用它也类似于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
关于时区的一个注意事项。在 java.time
API 中,所有的日期类都有一个无参的 now()
方法,它可以获取 JVM 默认时区的当前日期/时间。
虽然这非常方便,但它也有一些缺点,因为默认时区可能在运行时随时改变而不受通知。如果可能的话,最好指定你想要的时区。
该 API 使用 IANA 时区名称(总是以Region/City
格式,如America/New_York
或Europe/Paris
)。
避免使用三个字母的缩写(如CET
或EST
),因为它们是 含糊不清和不标准的。
你可以通过调用 ZoneId.getAvailableZoneIds()
来获取可用时区列表(并选择最适合你系统的时区)。
要在特定时区中获取当前日期/时间,请使用 ZoneId
类:
// get the current date/time in a specific timezone
ZonedDateTime.now(ZoneId.of("Europe/Paris"));