从Java生成并解析文本中的“年 – 月”值

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

例如:

  Nov 2015 Oct 2015 Sept 2015 

我使用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中有一些更好的方法可以处理一年+一个月吗?

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

此外,您的示例代码还有其他一些问题:

  • 获得当前时刻两次。
    以冗余方式获取当前时刻是一种不好的做法。 如果这两行代码恰好发生在午夜的中风之间,那么您将使用两个不同的日期,可能还有两个不同的月份甚至几年!
  • 忽略时区问题。
    讨论如下。

java.time

旧的java.util.Calendar / .Date类是众所周知的麻烦,应该避免。 它们已经在Java 8及更高版本的java.time框架中被取代。

新课程的灵感来自非常成功的Joda-Time框架,旨在作为其inheritance者,在概念上类似但重新设计。 由JSR 310定义。 由ThreeTen-Extra项目扩展。 请参阅教程 。

YearMonth

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

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

时区

虽然YearMonth中没有包含时区,但请注意时区对于确定当前YearMonth至关重要。 当前日期以及因此YearMonth随时都在世界各地变化。 新的一天在巴黎东部如巴黎早于蒙特利尔,比如蒙特利尔仍然是“昨天”。

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

 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将简化您的生活。 像这样(注意连字符):

  

但是如果你坚持,你可以通过指定自定义格式化程序模式来构建没有连字符的字符串。

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

201512

本地化字符串

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

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

DEC。 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() ;