Java 中没有内置函数,但使用现有 API 并不难做到。首先你需要得到与今天的差异(以天为单位),并根据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
在 Joda-Time 中,您可以使用 org.joda.time.Days 类来获取天数差异。一个细节是我只考虑日期(日/月/年)并忽略时间(小时/分钟/秒)来获得差异(但您可以根据需要进行调整)。方法是这样的:
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();
}
我使用 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))); // 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
输出是(考虑到今天是 2017 年 10 月 2 日nd,我在当地时间上午 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's website 中,它也说:“请注意,Joda-Time 被认为是一个基本上“完成”的项目。没有计划进行重大改进。如果使用 Java SE 8,请迁移到 java.time (JSR-310 )。”。
如果您使用的是 Java 8,请考虑使用 new java.time API。更简单,less bugged and less error-prone than the old APIs。
如果您使用的是 Java 6 或 7,则可以使用 ThreeTen Backport,它是 Java 8 新日期/时间类的出色向后移植。对于Android,您还需要ThreeTenABP(更多关于如何使用它here)。
下面的代码适用于两者。
唯一的区别是包名(在 Java 8 中是 java.time,在 ThreeTen Backport(或 Android 的 ThreeTenABP)中是 org.threeten.bp),但类和方法 names 是相同的。
这个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();
// 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();
}
我使用了 Joda 的 DateTime 的 ZonedDateTime、which is an equivalent(它代表时区中的日期和时间)。
调用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 timezones names(始终采用Region/City 格式,如America/New_York 或Europe/Paris)。
避免使用三个字母的缩写(如CET 或EST),因为它们是ambiguous and not standard。
您可以致电ZoneId.getAvailableZoneIds() 获取可用时区列表(并选择最适合您系统的时区)。
要获取特定时区的当前日期/时间,请使用 ZoneId 类:
// get the current date/time in a specific timezone
ZonedDateTime.now(ZoneId.of("Europe/Paris"));