生成&;解析";“年-月”;Java文本中的值

生成&;解析";“年-月”;Java文本中的值,java,parsing,datetime,Java,Parsing,Datetime,我有一个HTML弹出菜单,列出了各种年-月组合。每个菜单项都有一个本地化的字符串表示形式,如内容显示的2015年11月,以及一个编码值属性,如201511 例如: 2015年11月 2015年10月 2015年9月 我使用java.util.Calendar生成编码值字符串 int month = Calendar.getInstance().get(Calendar.MONTH)+1; int year = Calendar.getInstance().get(Calendar.YEAR)

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

例如:


2015年11月
2015年10月
2015年9月
我使用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中有更优雅的方法来处理年+月,Java 8和更高版本中内置了Java.time框架

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

  • 两次获取当前时刻。
    重复获取当前时刻两次是不好的做法。如果这两行代码恰好发生在午夜钟声前后,您将使用两个不同的日期,可能是两个不同的月份甚至年份
  • 忽略时区问题。下面讨论
java.time 旧的java.util.Calendar/.Date类非常麻烦,应该避免使用。在Java8和更高版本中,它们已被框架所取代

新类的灵感来源于一个非常成功的框架,该框架旨在作为其继承者,在概念上类似,但经过了重新设计。定义为。由项目扩展。看

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

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

时区 虽然一个年月内不包括时区,但请注意,时区对于确定当前的年月至关重要。目前的日期,也就是年月,在世界各地随时都在变化。东方的巴黎比西方的蒙特勒尔(Montréal)更早地迎来了新的一天,那里仍然是“昨天”

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

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

我强烈建议尽可能将日期时间值的字符串表示形式设置为ISO8601格式

String output = yearMonth.toString ();
2015-12

我强烈建议在HTML属性中使用这种格式。坚持ISO 8601将简化您的生活。如下所示(请注意连字符):

201512

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

Locale locale = Locale.CANADA_FRENCH;
DateTimeFormatter formatter = DateTimeFormatter.ofPattern ( "MMM yyyy" , locale );
String output = yearMonth.format ( formatter );
déc。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 = zdt.toInstant() ;

仅供参考,我以中使用的示例代码为基础,重点不同。哇!我不知道这种在堆栈上发布问题的方法!这是合法的权利吗?@Identity1是的,如果这是你的意思,这是合法的,甚至是鼓励的。如果您的意思是从另一个问题中提取代码片段,那么这是不寻常的。事实上,我确实在下面写下了关于原始问题的答案,但后来意识到我的答案所涉及的问题超出了原始作者的意图。这个问题与我的回答无关。所以,我想说,是的,这是合法的。我在这里的目的是明确地关注
YearMonth
类。这很酷!我想我可以用这种方法
LocalDate ld = ym.atDay( 1 ) ;  // Get first day of that year-month. 
ZoneId z = ZoneId.of( "Pacific/Auckland" ) ;
ZonedDateTime zdt = ld.atStartOfDay( z ) ;
Instant instant = zdt.toInstant() ;