【问题标题】:Generate & parse "Year-Month" values in text from Java从 Java 生成和解析文本中的“年月”值
【发布时间】:2015-12-22 04:54:50
【问题描述】:

我有一个列出各种年月组合的 HTML 弹出菜单。每个菜单项都有一个本地化的字符串表示,例如用于内容显示的Nov 2015,以及一个编码值的属性,例如201511

例如:

<select name="selectPeriod">
    <option value="201511">Nov 2015</option>
    <option value="201510">Oct 2015</option>
    <option value="201509">Sept 2015</option>

我使用 java.util.Calendar 生成编码值字符串。

int month = Calendar.getInstance().get(Calendar.MONTH)+1;
int year = Calendar.getInstance().get(Calendar.YEAR);
String v = String.valueOf( year ) + String.valueOf( month );

这可行,但是当我真正关心的是没有日期或时间的年 + 月时,使用日历(日期加时间)似乎很奇怪。

在现代 Java 中有没有更好的方法来处理一年+一个月?

【问题讨论】:

  • 仅供参考,我将此问题基于 another Question 中使用的示例代码,但重点不同。
  • 哇!我不知道这种在 Stack 上发布问题的方法!这是合法的吗?
  • @Identity1 是的,如果这就是您的意思,answer your own Question 是合法的,甚至受到鼓励。如果您的意思是从另一个问题中提取代码 sn-p,那无疑是不寻常的。实际上,我确实写了下面的答案来发布原始问题,但后来意识到我的答案解决了原作者意图之外的问题。 accepted Answer there 与我在这里的回答无关。所以,我会说,是的,这是合法的。我在这里的目的是明确关注YearMonth 类。
  • 太酷了!我想我可以使用这种方法

标签: java parsing datetime


【解决方案1】:

在现代 Java 中确实有一种更优雅的方式来处理 YearMonth,Java 8 及更高版本中内置了 java.time 框架。

您的示例代码还存在其他一些问题:

  • 获取当前时刻两次。
    重复获取当前时刻两次,这是一种不好的做法。如果这两行代码恰好发生在午夜钟声左右,那么您将使用两个不同的日期,可能还有两个不同的月份甚至年份!
  • 忽略时区问题。
    下面讨论。

java.time

旧的 java.util.Calendar/.Date 类是出了名的麻烦,应该避免。它们已在 Java 8 中被 java.time 框架取代。

新类的灵感来自非常成功的 Joda-Time 框架,该框架旨在作为其继任者,在概念上相似但经过重新架构。由JSR 310 定义。由ThreeTen-Extra 项目扩展。请参阅Tutorial

YearMonth

新类包括一个 YearMonth 类,完全符合您的目的:表示没有任何日期、时间或时区的年+月。

我建议在您的业务逻辑中使用此类的对象,仅在必要时使用字符串,例如生成 HTML。

时区

虽然 YearMonth 中不包含时区,但请注意,时区对于确定当前的 YearMonth 至关重要。当前日期,因此 YearMonth,在世界各地随时变化。新的一天在东方如巴黎比在西方如蒙特利尔更早出现,那里仍然是“昨天”。

如果在您的示例代码中省略了时区,则隐式使用 JVM 当前的默认时区。这种隐式使用意味着您的结果可能因以下几种原因而有所不同:您的代码可能会移动到另一台机器,系统管理员可能会更改主机的默认设置,或者更糟糕的是,在同一 JVM 中运行的任何应用程序的任何线程中的任何代码都可以@ 987654327@。最好指定所需/预期的时区(ZoneId in java.time)。

ZoneId zoneId = ZoneId.of( "America/Montreal" );
YearMonth yearMonth = YearMonth.now( zoneId );

字符串格式

java.time 类中的toString 方法默认使用标准的ISO 8601 格式。您的格式 YYYYMM 类似于标准格式,但在这种特殊情况下(年月),需要使用连字符以避免歧义,YYYY-MM。大多数标准格式都允许“基本”变体省略连字符,但此处不允许。

我强烈建议您尽可能使用 ISO 8601 格式的日期时间值的字符串表示形式。

String output = yearMonth.toString ();

2015-12

我强烈建议在您的 HTML 属性中使用该格式。坚持 ISO 8601 将简化您的生活。像这样(注意连字符):

<option value="2015-11">Nov 2015</option>

但如果您坚持,您可以通过指定 custom formatter pattern 来构建不带连字符的字符串。

DateTimeFormatter formatter = DateTimeFormatter.ofPattern ( "yyyyMM" );
String output = yearMonth.format ( formatter );

201512

本地化字符串

让 java.time 完成本地化显示文本的工作。请注意传递给 DateTimeFormatter 的 Locale,而不是隐式依赖 JVM 当前的默认语言环境。

Locale locale = Locale.CANADA_FRENCH;
DateTimeFormatter formatter = DateTimeFormatter.ofPattern ( "MMM yyyy" , locale );
String output = yearMonth.format ( formatter );

十二月。 2015

日期

如果您需要特定日期,请指定月份中的某一天以与今年和月份相结合。

LocalDate ld = ym.atDay( 1 ) ;  // Get first day of that year-month. 

您可以从那里获取特定时刻,例如一天中的第一时刻。 不要假设一天从 00:00:00 开始。让 java.time 确定第一个时刻。

ZoneId z = ZoneId.of( "Pacific/Auckland" ) ;
ZonedDateTime zdt = ld.atStartOfDay( z ) ;

如果您需要以 UTC 格式查看同一时刻,请提取 Instant

Instant instant = zdt.toInstant() ;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-12-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-30
    • 1970-01-01
    • 1970-01-01
    • 2019-09-06
    相关资源
    最近更新 更多