2017-02-17 740 views
1

使用下面的代码,我注意到有时日期格式不正确。为了让它变得更奇怪,有时timeStamp会有正确的日期,timeStampCopy会有错误的日期,反之亦然。为什么我的Java Calendar.setTime()偶尔设置错误时间?

public static Timestamp method(String date, DateFormat dateFormat) throws Exception {   

     // date is always "2017-02-17"  

     // original 
     GregorianCalendar gCal = new GregorianCalendar(); 
     gCal.setTime(dateFormat.parse(date)); 
     Timestamp timeStamp = new Timestamp(gCal.getTimeInMillis()); 

     // copy 
     GregorianCalendar gCalCopy= new GregorianCalendar(); 
     gCalCopy.setTime(dateFormat.parse(date)); 
     Timestamp timeStampCopy = new Timestamp(gCalCopy.getTimeInMillis()); 

     if (!timeStamp.toString().contains("2017-02-17")) 
      System.out.println(timeStamp.toString()); 
     if (!timeStampCopy.toString().contains("2017-02-17")) 
      System.out.println(timeStampCopy.toString()); 

     return timeStamp; 

    } 

我不知道这可能是导致它,但我这个使用Date对象和我有同样的问题尝试。我认为这可能是一个解析问题,但由于它做了两次相同的事情,我不确定。

下面是一些我得到的值:

timeStamp is:  2017-02-17 00:00:00.0 
timeStampCopy is: 1700-02-17 00:00:00.0 
+0

什么是你的功能输入 –

+4

你是否在线程之间共享'DateFormat'实例? –

+0

@AndyTurner是的!我刚刚补充说,由于某种原因,当我向该方法添加“synchronized”时,它似乎突然开始正常工作。不知道为什么。 – Overclock

回答

3

你说你共享线程之间的DateFormat实例。

按照Javadoc

日期格式不同步。建议为每个线程创建单独的格式实例。如果多个线程同时访问一个格式,它必须在外部同步。

注意,这指的是访问DateFormat实例的外部同步,的方法。如果DateFormat实例没有其他用途,则仅使用方法​​将修复此问题。

您可以:

  • 显式同步,周围用DateFormat实例的所有代码(这是值得添加@GuardedBy注释的变量,以记录您所期望的锁被使用前举行);
  • 将变量类型更改为ThreadLocal<DateFormat>(并适当地初始化共享变量),以确保每个线程都拥有自己的DateFormat副本。

后一种方法争用较少,因为每个线程都可以独立于其他线程进行。这也意味着您不会意外省略同步。

但是,还有更好的库处理日期和时间,这是设计与DateFormat缺乏线程安全问题的后见之明。在Java 8中,有java.time API;对于早期版本的Java,有Jodatime。

+0

所有的都不错,但是最后一句话。 Joda-Time项目现在处于维护模式,并建议迁移到java.time类。对于早期版本的Java(6和7),请使用[ThreeTen-Backport](http://www.threeten.org/threetenbp/)项目。大部分java.time功能都在那里被移植。在[ThreeTenABP](https://github.com/JakeWharton/ThreeTenABP)项目中进一步适用于Android。 –

1

Answer by Turner是正确的,应该被接受。

java.time是线程安全的

的java.time类使用不可变对象,使他们本质上就是线程安全的解决这个问题。

LocalDate ld = LocalDate.of("2017-02-17"); 
ZoneId z = ZoneId.of("America/Montreal"); 
ZonedDateTime zdt = ld.atStartOfDay(z); 

通过调用toString生成标准ISO 8601格式的字符串。对于其他格式,请使用DateTimeFormatter类。搜索堆栈溢出了许多示例和讨论。不用担心线程,所有线程安全。

对于UTC值,请提取Instant

Instant instant = zdt.toInstant() ; 

不需要使用java.sql.Timestamp。现代JDBC驱动程序可以通过toObject和setObject方法处理java.time类型。对于较旧的驱动程序,使用添加到旧类的新方法进行转换。


关于java.time

java.time框架是建立在Java 8和更高版本。这些类取代了日期时间类legacy,如java.util.Date,Calendar,& SimpleDateFormat

Joda-Time项目现在位于maintenance mode,建议迁移到java.time类。请参阅Oracle Tutorial。并搜索堆栈溢出了很多例子和解释。规格是JSR 310

从何处获取java.time类?

ThreeTen-Extra项目与其他类扩展java.time。这个项目是未来可能增加java.time的一个试验场。您可以在这里找到一些有用的类,如Interval,YearWeek,YearQuartermore

相关问题