Java SimpleDateFormat 不给出常量结果

Java SimpleDateFormat doesn't Give Constant Result

我需要将 Java 日期对象格式化为类似 yyyyMMdd 的字符串(四舍五入到一天)。例如,20180129。我有以下实现:

SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
sdf.format(roundedDate);

该代码大部分时间都有效,但有时它会生成类似 2018129 的结果,这不是我想要的。所以我的数据库中会有 201801292018129

该应用程序使用来自 MQ 的消息并将消息中的时间戳汇总到 Java 日期对象中。并将日期格式化为上述字符串。

问题是我无法在调试模式下重现该问题。它总是在调试器中产生预期的结果。但是在我 运行 它在服务器上(在 Docker 中)一段时间后,我看到了这样损坏的数据。

我想知道为什么 SimpleDateFormat 在给定有效的 Date 对象的情况下会有这种不确定的行为?任何想法将不胜感激。

SimpleDateFormat 不是线程安全的,请参阅 this 优秀文章。

java.time.format.DateTimeFormatter 是此功能在核心 Java.

中的现代线程安全实现

tl;博士

改用线程安全的java.time类。

具体来说,使用LocalDate and DateTimeFormatter.BASIC_ISO_DATE

LocalDate.parse(
    "2018129" ,
    DateTimeFormatter.BASIC_ISO_DATE
)

2018-01-29

LocalDate.now()
    .format( DateTimeFormatter.BASIC_ISO_DATE )

20180129

线程安全

您没有提供足够的信息来诊断您的问题。我猜是:

  • 您正在跨线程使用那些遗留的日期时间对象,它们并非设计为线程安全的。而是使用 java.time 类 ,其中 are 通过 immutable objects 模式设计为​​线程安全的.
  • 在你提到但忽略解释的这个神秘的“日期舍入”中,无论你在做什么,都会出现问题。

错误的数据类型

timestamp in the message into a Java Date object.

您正在将仅限日期的值放入带有日期时间的日期类型中。方钉圆孔

相反,对仅限日期的值使用仅限日期的类型:LocalDate

ISO 8601

您想要的 YYYYMMDD 格式恰好在 ISO 8601 标准中定义为“基本”变体,其中分隔符的使用最少。

Java提供了一个DateTimeFormatter object for this purpose: DateTimeFormatter.BASIC_ISO_DATE。因此无需定义格式化模式。

String input = "2018129" ;
LocalDate ld = LocalDate.parse( input , DateTimeFormatter.BASIC_ISO_DATE ) ;

要生成这样的字符串,请使用相同的格式化程序。

LocalDate today = LocalDate.now( ZoneId.of( "Africa/Tunis" ) ) ;
String output = today.format( DateTimeFormatter.BASIC_ISO_DATE ) ;

顺便说一下,我建议使用 ISO 8601 格式的完整版本,而不是紧凑的“基本”变体。根据我的经验,节省的几个字节不值得放弃可读性和减少歧义。另外,java.time 类 在 parsing/generating String 对象时默认使用全长 ISO 8601 格式,因此您可以分配完全 DateTimeFormatter 个对象。


关于java.time

java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

Joda-Time project, now in maintenance mode, advises migration to the java.time 类.

要了解更多信息,请参阅 Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310

您可以直接与您的数据库交换 java.time 对象。使用 JDBC driver compliant with JDBC 4.2 或更高版本。不需要字符串,不需要 java.sql.* 类.

在哪里获取java.time类?

ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.