【问题标题】:Momentjs calendar() equivalent for JavaMomentjs calendar() 等效于 Java
【发布时间】:2017-10-01 06:16:37
【问题描述】:

Momentjs 具有 calendar() 函数可以漂亮地打印如下所示的时间跨度。

例如:

“上周一下午 1:14”

“09/21/2017”(如果日期是不久前)

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

【问题讨论】:

  • 仅供参考,Joda-Time 项目处于维护模式。团队建议迁移到 java.time 类。

标签: java momentjs jodatime date-difference


【解决方案1】:

java.time

现代方法使用行业领先的 java.time 类。

ZoneId z = ZoneId.now( “America/Montreal”  ) ;
LocalDate today = LocalDate.now( z ) ;

使用加号和减号方法进行数学运算。

LocalDate tomorrow = today.plusDays( 1 ) ;

搜索堆栈溢出。几乎每个基本的日期时间问题都已被问及答案。

字符串

java.time 类不会生成诸如“tomorrow”和“Last Monday”之类的字符串。因此,没有直接等同于您引用的库。你将不得不自己做农民工作。

搜索DateTimeFormatter 类和DateTimeFormatterBuilder 类。

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

很漂亮

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

乔达时间

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


关于java.time

java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.DateCalendarSimpleDateFormat

Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。

要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310

从哪里获得 java.time 类?

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

【讨论】:

  • 这不是我问的。我的问题是:我想自动漂亮地打印日期,例如“上周一下午 1:14”、“明天下午 1:14”、“10/11/2017”(如果很久以前)
  • @PerrierCitror 啊,我没有意识到那些注释字符串是目标。查看我的编辑。
  • 嗯,好像没有直接的等价物……我讨厌Java中的时间格式……真是个痛苦的人
【解决方案2】:

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) &lt; 7来考虑是否相差一周以上,但我不确定moment.js是否考虑&lt;= 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 的 DateTimeZonedDateTimewhich 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_YorkEurope/Paris)。 避免使用三个字母的缩写(如CETEST),因为它们是ambiguous and not standard

您可以致电ZoneId.getAvailableZoneIds() 获取可用时区列表(并选择最适合您系统的时区)。

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-11-24
    • 2011-06-03
    • 2010-11-30
    • 2010-11-10
    • 2014-07-13
    • 2011-03-23
    • 2019-01-31
    • 1970-01-01
    相关资源
    最近更新 更多