Java DateFormat或Calendar.getInstance有时返回随机值
下面的Java DateFormat或Calendar.getInstance有时返回随机值,java,android,multithreading,time,thread-safety,Java,Android,Multithreading,Time,Thread Safety,下面的now()代码对一个对象执行两次。一次表示它的创建,另一次表示它何时写入数据库 在一个受控测试中,我成功地重现了一个日期相差很大的bug。有些日期似乎有正确的日期,但在00:00:00或接近00:00:00 另一些人似乎相对落后几个小时,从1小时到16小时甚至几周不等。我还设定了未来的时间Calendar.getInstance.getTime()应该等同于System.getCurrentTimeMillis(),让后者偶尔返回一个未来时间是令人担忧的 我没有发现任何东西,我将进一步调查
now()
代码对一个对象执行两次。一次表示它的创建,另一次表示它何时写入数据库
在一个受控测试中,我成功地重现了一个日期相差很大的bug。有些日期似乎有正确的日期,但在00:00:00
或接近00:00:00
另一些人似乎相对落后几个小时,从1小时到16小时甚至几周不等。我还设定了未来的时间Calendar.getInstance.getTime()
应该等同于System.getCurrentTimeMillis()
,让后者偶尔返回一个未来时间是令人担忧的
我没有发现任何东西,我将进一步调查这个问题,并在这里报告我的发现
private static DateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
public static String calendarToDb(Calendar cal) {
if (cal == null) {
return null;
}
return simpleDateFormat.format(cal.getTime());
}
public static String now() {
return calendarToDb(Calendar.getInstance());
}
以下是android监视器日志的相关部分。这是一个真实的设备,不是模拟器。M代表MessageManager,P代表ContentProvider
08-30 18:07:17.267 M: main starttime = 2016-01-01T00:07:00+0100\
08-30 18:07:17.306 P: AsyncQueryWorker CreateTime = 2016-08-30T18:07:17+0200\
08-30 18:07:18.326 M: main starttime = 2016-01-01T00:00:00+0200\
08-30 18:07:18.371 P: AsyncQueryWorker CreateTime = 2016-08-30T18:07:18+0200\
08-30 18:07:19.898 M: main starttime = 2016-08-30T18:07:19+0200\
08-30 18:07:19.920 P: AsyncQueryWorker CreateTime = 2016-08-30T00:00:00+0100\
我的数据库中有更多看起来更奇怪的数据,根本不应该发生。问题似乎最有可能在于静态数据
static DateFormat simpleDateFormat
如下文所述:
如果多个线程使用同一实例(由于静态原因,它们会这样做),则会存储中间结果。时间可以在方法执行期间部分重置,这会使当前处理的结果发生偏差
解决方案是每次返回新实例。问题似乎最有可能在于静态
static DateFormat simpleDateFormat
如下文所述:
如果多个线程使用同一实例(由于静态原因,它们会这样做),则会存储中间结果。时间可以在方法执行期间部分重置,这会使当前处理的结果发生偏差
解决方案是每次返回新实例。避免遗留日期时间类
这是正确的,应该被接受。您可能会看到由于旧日期时间类中缺乏线程安全性而导致的错误。这是避免这些麻烦的旧遗留日期时间类的许多原因之一
相反,您应该使用这些类。time类具有线程安全性
ISO 8601
您的输入字符串恰好符合日期时间格式的标准
在解析/生成字符串时,java.time类默认使用ISO 8601格式。因此,您不需要指定格式化模式,而需要针对当前Java8实现中的错误进行指定。当前,UTC偏移量中缺少冒号会导致默认情况下解析失败。应该在Java9中修复。而在Java8中,使用and指定模式
OffsetDateTime
输入字符串表示与UTC的偏移,但不是完整时区。所以我们将其解析为对象
在Java 9及更高版本中,直接解析:
OffsetDateTime odt = OffsetDateTime.parse ( input );
在Java8中,使用上面构建的DateTimeFormatter
对象
OffsetDateTime odt = OffsetDateTime.parse ( input , f );
转储到控制台
System.out.println ( "input: " + input + " | odt: " + odt );
输入:2016-08-30T18:07:17+0200 | odt:2016-08-30T18:07:17+02:00
避免遗留日期时间类
这是正确的,应该被接受。您可能会看到由于旧日期时间类中缺乏线程安全性而导致的错误。这是避免这些麻烦的旧遗留日期时间类的许多原因之一
相反,您应该使用这些类。time类具有线程安全性
ISO 8601
您的输入字符串恰好符合日期时间格式的标准
在解析/生成字符串时,java.time类默认使用ISO 8601格式。因此,您不需要指定格式化模式,而需要针对当前Java8实现中的错误进行指定。当前,UTC偏移量中缺少冒号会导致默认情况下解析失败。应该在Java9中修复。而在Java8中,使用and指定模式
OffsetDateTime
输入字符串表示与UTC的偏移,但不是完整时区。所以我们将其解析为对象
在Java 9及更高版本中,直接解析:
OffsetDateTime odt = OffsetDateTime.parse ( input );
在Java8中,使用上面构建的DateTimeFormatter
对象
OffsetDateTime odt = OffsetDateTime.parse ( input , f );
转储到控制台
System.out.println ( "input: " + input + " | odt: " + odt );
输入:2016-08-30T18:07:17+0200 | odt:2016-08-30T18:07:17+02:00
奇怪的我假设您正在使用多个线程。您是否考虑过使用新的java8日期api,或者使用
JodaTime
?是的,我使用不同的线程(main和AsyncQueryWorkerThread)。我不认为java8适用于较旧的api级别afaik?问题是,在大多数情况下,它工作时,有时返回随机值或重置日期/时间是有意义的。java8中内置的许多功能,后来在项目中被重新移植到Java6和Java7,并在项目中进一步适应Android。奇怪。。。我假设您正在使用多个线程。您是否考虑过使用新的java8日期api,或者使用JodaTime
?是的,我使用不同的线程(main和AsyncQueryWorkerThread)。我不认为java8适用于较旧的api级别afaik?问题是,在大多数情况下,它工作时,有时返回随机值或重置日期/时间是有意义的。java8中内置的许多功能,后来在项目中被移植到Java6和Java7,并在项目中进一步适应Android。