Java中与Momentjs calendar()函数等效的函数是什么?

3

Momentjs有calendar()函数,可用于漂亮地打印时间跨度,如下所示。

例如:

"上周一 下午1:14"

"2017年09月21日"(如果日期很久远)

在Java中(如果可能的话,使用Joda-Time),是否有相应的函数?

moment().subtract(10, 'days').calendar(); // 09/21/2017
moment().subtract(6, 'days').calendar();  // Last Monday at 1:14 PM
moment().subtract(3, 'days').calendar();  // Last Thursday at 1:14 PM
moment().subtract(1, 'days').calendar();  // Yesterday at 1:14 PM
moment().calendar();                      // Today at 1:14 PM
moment().add(1, 'days').calendar();       // Tomorrow at 1:14 PM
moment().add(3, 'days').calendar();       // Wednesday at 1:14 PM
moment().add(10, 'days').calendar();      // 10/11/2017

3
FYI,Joda-Time项目正在维护模式中。团队建议迁移到java.time类。 - Basil Bourque
2个回答

5

java.time

现代方法使用行业领先的java.time类。
ZoneId z = ZoneId.now( “America/Montreal”  ) ;
LocalDate today = LocalDate.now( z ) ;

使用加号和减号方法进行数学运算。
LocalDate tomorrow = today.plusDays( 1 ) ;

搜索 Stack Overflow。几乎每个基本的日期时间问题都已经被问答过了。

字符串

java.time 类不生成诸如“明天”和“上周一”之类的字符串。因此,没有你引用的库的直接等价物。您将不得不自己完成这项工作。

搜索 DateTimeFormatter 类和 DateTimeFormatterBuilder 类。

此外,DayOfWeek 枚举及其自动本地化的 getDisplayName 方法可能会有用。

prettytime

prettytime 库可能会对你有所帮助,尽管我没有使用过。

Joda-Time

Joda-Time 项目处于维护模式。团队建议迁移到 java.time 类。


关于 java.time

java.time 框架是内置于 Java 8 及以上版本中的。这些类取代了老旧的 遗留 日期时间类,如 java.util.DateCalendarSimpleDateFormat

Joda-Time 项目现在处于 维护模式,建议迁移到 java.time 类。

要了解更多,请查看Oracle教程。并在Stack Overflow上搜索许多示例和解释。规范是JSR 310

如何获取java.time类?

ThreeTen-Extra项目扩展了java.time的附加类。该项目是java.time可能未来添加的试验场。您可能会在这里找到一些有用的类,例如Interval, YearWeek, YearQuarter以及更多


1
这不是我问的问题。我的问题是:我想要自动地漂亮地打印日期,例如“上周一下午1:14”,“明天下午1:14”,“2017年10月11日”(如果很久以前)。 - user2686744
@PerrierCitror 啊,我没有意识到那些被注释的字符串是目标。请看我的修改。 - Basil Bourque
2
嗯,看起来没有直接的等价物...我讨厌Java中的时间格式...真是太痛苦了。 - user2686744

1
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();

    // 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日,而且我在当地时间上午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();

    // 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();
}

我使用了一个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_YorkEurope/Paris)。 避免使用三个字母的缩写(如CETEST),因为它们是 含糊不清和不标准的

你可以通过调用 ZoneId.getAvailableZoneIds() 来获取可用时区列表(并选择最适合你系统的时区)。

要在特定时区中获取当前日期/时间,请使用 ZoneId 类:

// get the current date/time in a specific timezone
ZonedDateTime.now(ZoneId.of("Europe/Paris"));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接