有时 java.util.Date before 失败

Sometimes java.util.Date before fails

我支持完成日期比较的 Java 1.6 代码。

static final SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy");

static void process(Message message) {
 String now =formatter.format(new Date());
 String end = formatter.format("01-12-2017");

 Date endDay = formatter.parse(end);
 Date currentDay = formatter.parse(now);

 if(endDay.before(currentDay)){
    System.out.println("Send a notification indicating the end day has been reached, so the message is not processed.");
 }else {
    System.out.println("Process the message and applies some business rules");
 }
}

我知道代码不是最好的,但是在 160,000 笔交易中有 3 笔交易失败并且执行了 if 块中的代码。我打算使用日历,但这里可能发生了什么?

你看 Javas Date 库并不是处理日期的最佳库。特别是当我们谈论 java-1.6.

我的建议是与这个 Joda Time 取得联系。 Here 您可以找到图书馆所需的一切。根据我个人的经验,它工作起来更稳定,也更容易理解。

tl;博士

使用 thread-safe java.time classes 代替线程-不安全 legacy SimpleDateFormat class .

DateTimeFormatter f = DateTimeFormatter.ofPattern( "MM-dd-uuuu" ) ;
ZoneId z = ZoneId.of( "America/Montreal" ) ;
…
LocalDate.parse( "01-12-2017" , f )
         .isBefore( LocalDate.now( z ) )

旧版 date-time class 不是 thread-safe

我注意到您正在使用 singleton for the formatter, a single static instance of SimpleDateFormat. That legacy class is not thread-safe,如 class Java文档中所述:

Synchronization

Date formats are not synchronized. It is recommended to create separate format instances for each thread. If multiple threads access a format concurrently, it must be synchronized externally.

你报告的偶然错误似乎没有充分的理由,这向我表明你一次从多个线程使用该对象。名为 process 的静态方法,以及您在服务器评论中的提及,让我更加怀疑。在运行时,您偶尔会遇到线程之间的冲突。

避免遗留 date-time classes.

您正在使用麻烦的旧 date-time classes 现在 legacy, supplanted by the java.time classes. Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.

java.time class 使用 immutable objects, and are designed to be thread-safe. So says the package documentation.

LocalDate

您正在使用 date-time 个对象作为 date-only 值。 java.time classes 包含一种表示 date-only 的方法。 LocalDate class 表示没有 time-of-day 和时区的 date-only 值。

时区对于确定日期至关重要。对于任何给定时刻,日期在全球范围内因地区而异。例如,Paris France is a new day while still “yesterday” in Montréal Québec.

午夜后几分钟

指定 proper time zone name in the format of continent/region, such as America/Montreal, Africa/CasablancaPacific/Auckland。切勿使用 ESTIST 等 3-4 字母缩写,因为它们 不是 真正的时区,未标准化,甚至不是唯一的(!)。

ZoneId class 是 thread-safe。因此,您可以保留一个实例以供跨线程重复使用。

ZoneId z = ZoneId.of( "America/Montreal" );
LocalDate today = LocalDate.now( z );

将日期表示为字符串时,我建议使用标准 ISO 8601 格式。对于 date-only,那将是 YYYY-MM-DD,例如 2017-01-23。当 parsing/generating 字符串时,java.time classes 默认使用这些标准格式。

LocalDate target = LocalDate.parse( "2017-01-23" );

如果您无法控制格式,请使用 DateTimeFormatter,如 Stack Overflow 上的许多其他问答中所示。这个classthread-safe,所以可以保留一个实例,跨线程重复使用。

DateTimeFormatter f = DateTimeFormatter.ofPattern( "MM-dd-uuuu" );
LocalDate target = LocalDate.parse( "01-12-2017" , f );

isEqualisBeforeisAfter等方法比较。

if( target.isBefore( today ) ){
    System.out.println( "Send a notification indicating the end day has been reached, so the message is not processed." );
} else {
    System.out.println( "Process the message and applies some business rules" );
}

关于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 classes.

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

从哪里获得java.time classes?

  • Java SE 8 and SE 9 及更高版本
    • Built-in。
    • 标准 Java API 的一部分,带有捆绑实施。
    • Java 9 添加了一些小功能和修复。
  • Java SE 6 and SE 7
    • java.time 的大部分功能是 back-ported 到 Java ThreeTen-Backport 中的 6 和 7。
  • Android
    • ThreeTenABP项目专门为Android改编ThreeTen-Backport(上面提到的)。
    • 参见