SimpleDateFormat 在部署一段时间后给出错误的日期和时间
SimpleDateFormat giving wrong date and time after some time of deployment
我的代码中有 2 个文件:
文件 1 内容:
public static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
public static final SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
文件 2 内容:
sdf.format(formatter.parse("2015-02-02")));
问题:文件 2 中的上面一行最初打印“2015-02-02 12:00:00”几个小时,但之后打印“2015-02-01 06:00:00”。
知道这里可能是什么问题。
附加信息:
我的服务器 运行 在位于美国的某台云计算机上。
new java.util.Date( )
始终正确给出 UTC 时区值。
服务器使用命令 java -jar xyz.jar
启动。
还有其他文件正在使用 sdf
和 formatter
变量。
我无法在本地机器上重现这个。
一旦问题开始在服务器上发生,它就会显示错误的日期时间,直到服务器重新启动。
如果你查看 Oracle 官方文档,它说
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.
通过查看您的代码,您似乎在多个线程中重复使用同一个实例。这是不正确的!!!
要么维护一个格式化程序池,要么同步访问(不推荐),要么每次都创建一个新实例。
另一个 api 中的一些代码为 sdf
设置了时区,这导致 issue.Here 是在本地复制问题的示例:
import java.text.SimpleDateFormat;
import java.util.TimeZone;
public class SimpleDateFormatTExample {
private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
private static SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
private static String timeZone = "CST";
public static void main(String[] args) {
//-Duser.timezone=UTC
try {
String dateTimeString1 = sdf.format(formatter.parse("2018-01-01"));
System.out.println("Thread Main->> " + dateTimeString1);
//output : Thread Main->> 2018-01-01 12:00:00
} catch (Exception e) {
e.printStackTrace();
}
new Thread(() -> {
try {
//timezone is changed by another thread
sdf.setTimeZone(TimeZone.getTimeZone(timeZone));
String dateTimeString = sdf.format(formatter.parse("2018-01-01"));
System.out.println("Thread child->> " + dateTimeString);
//output : Thread child->> 2017-12-31 06:00:00
} catch (Exception e) {
e.printStackTrace();
}
}).start();
try {
Thread.sleep(1000);
String dateTimeString1 = sdf.format(formatter.parse("2018-02-15"));
System.out.println("Thread Main:After timezone changes by another thread->> " + dateTimeString1);
//output : Thread Main:After timezone changes by another thread->> 2018-02-14 06:00:00
} catch (Exception e) {
e.printStackTrace();
}
}
}
Nathan Hughes 和我自己的评论足以合并为一个答案:使用 java.time、现代日期时间 API,特别是 DateTimeFormatter
。
public static final DateTimeFormatter printFormatter
= DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss");
现在您的格式可能如下所示:
String stringToPrint = LocalDate.parse("2015-02-02")
.atStartOfDay(ZoneOffset.UTC)
.format(printFormatter);
System.out.println(stringToPrint);
这会打印:
2015-02-02 00:00:00
在格式转换代码中,我利用了这样一个事实,即您的原始字符串 2015-02-02
是日期的标准 ISO 8601 格式。 LocalDate
将此格式解析为默认格式,即没有任何显式格式化程序。
你的代码出了什么问题?
从您的问题来看,您观察到的行为似乎有两种可能的解释:
- 服务器上使用这两个格式化程序的另一个类程序之一,设置其中一个的时区,例如America/Chicago。
- 两个或多个线程同时使用这些格式,这会导致其中一个线程的行为不正确。
观察到的行为,一个6小时的错误,出现后一直持续到服务器重启,似乎更符合第一个解释,你在自己的回答中也证实了这一点,谢谢你这样做那。
与 SimpleDateFormat
相反,现代 DateTimeFormatter
是线程安全的,可以防止任何线程问题,并且是不可变的,可以防止其他 类 修改格式化程序。所以它在这两种情况下都解决了你的问题。
顺便说一句,我想您知道您在格式模式字符串中错误地使用了小写字母 hh
。 hh
是上午或下午从 01 到 12 的小时,而从 00 到 23 的一天中的小时需要大写 HH
(这适用于 SimpleDateFormat
和 DateTimeFormatter
).
Link: Oracle tutorial: Date Time 解释如何使用 java.time
.
我的代码中有 2 个文件:
文件 1 内容:
public static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
public static final SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
文件 2 内容:
sdf.format(formatter.parse("2015-02-02")));
问题:文件 2 中的上面一行最初打印“2015-02-02 12:00:00”几个小时,但之后打印“2015-02-01 06:00:00”。 知道这里可能是什么问题。
附加信息:
我的服务器 运行 在位于美国的某台云计算机上。
new java.util.Date( )
始终正确给出 UTC 时区值。
服务器使用命令 java -jar xyz.jar
启动。
还有其他文件正在使用 sdf
和 formatter
变量。
我无法在本地机器上重现这个。
一旦问题开始在服务器上发生,它就会显示错误的日期时间,直到服务器重新启动。
如果你查看 Oracle 官方文档,它说
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.
通过查看您的代码,您似乎在多个线程中重复使用同一个实例。这是不正确的!!!
要么维护一个格式化程序池,要么同步访问(不推荐),要么每次都创建一个新实例。
另一个 api 中的一些代码为 sdf
设置了时区,这导致 issue.Here 是在本地复制问题的示例:
import java.text.SimpleDateFormat;
import java.util.TimeZone;
public class SimpleDateFormatTExample {
private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
private static SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
private static String timeZone = "CST";
public static void main(String[] args) {
//-Duser.timezone=UTC
try {
String dateTimeString1 = sdf.format(formatter.parse("2018-01-01"));
System.out.println("Thread Main->> " + dateTimeString1);
//output : Thread Main->> 2018-01-01 12:00:00
} catch (Exception e) {
e.printStackTrace();
}
new Thread(() -> {
try {
//timezone is changed by another thread
sdf.setTimeZone(TimeZone.getTimeZone(timeZone));
String dateTimeString = sdf.format(formatter.parse("2018-01-01"));
System.out.println("Thread child->> " + dateTimeString);
//output : Thread child->> 2017-12-31 06:00:00
} catch (Exception e) {
e.printStackTrace();
}
}).start();
try {
Thread.sleep(1000);
String dateTimeString1 = sdf.format(formatter.parse("2018-02-15"));
System.out.println("Thread Main:After timezone changes by another thread->> " + dateTimeString1);
//output : Thread Main:After timezone changes by another thread->> 2018-02-14 06:00:00
} catch (Exception e) {
e.printStackTrace();
}
}
}
Nathan Hughes 和我自己的评论足以合并为一个答案:使用 java.time、现代日期时间 API,特别是 DateTimeFormatter
。
public static final DateTimeFormatter printFormatter
= DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss");
现在您的格式可能如下所示:
String stringToPrint = LocalDate.parse("2015-02-02")
.atStartOfDay(ZoneOffset.UTC)
.format(printFormatter);
System.out.println(stringToPrint);
这会打印:
2015-02-02 00:00:00
在格式转换代码中,我利用了这样一个事实,即您的原始字符串 2015-02-02
是日期的标准 ISO 8601 格式。 LocalDate
将此格式解析为默认格式,即没有任何显式格式化程序。
你的代码出了什么问题?
从您的问题来看,您观察到的行为似乎有两种可能的解释:
- 服务器上使用这两个格式化程序的另一个类程序之一,设置其中一个的时区,例如America/Chicago。
- 两个或多个线程同时使用这些格式,这会导致其中一个线程的行为不正确。
观察到的行为,一个6小时的错误,出现后一直持续到服务器重启,似乎更符合第一个解释,你在自己的回答中也证实了这一点,谢谢你这样做那。
与 SimpleDateFormat
相反,现代 DateTimeFormatter
是线程安全的,可以防止任何线程问题,并且是不可变的,可以防止其他 类 修改格式化程序。所以它在这两种情况下都解决了你的问题。
顺便说一句,我想您知道您在格式模式字符串中错误地使用了小写字母 hh
。 hh
是上午或下午从 01 到 12 的小时,而从 00 到 23 的一天中的小时需要大写 HH
(这适用于 SimpleDateFormat
和 DateTimeFormatter
).
Link: Oracle tutorial: Date Time 解释如何使用 java.time
.