【发布时间】:2017-09-17 13:22:21
【问题描述】:
如何格式化java.util.Date 和DateTimeFormatter 便携?
我不能用
Date in = readMyDateFrom3rdPartySource();
LocalDateTime ldt = LocalDateTime.ofInstant(in.toInstant(), ZoneId.systemDefault());
ldt.format(dateTimeFormatter);
因为我担心ZoneId.systemDefault() 的使用会带来一些变化。
我需要完全格式化我拥有的那个对象。
更新
注意:时间就是时间。不是空间。时区是非常粗略的经度度量,即空间。我不需要它。只有时间(和日期)。
更新 2
我编写了以下程序,证明Date 不仅包含正确的“即时”:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DataNature2 {
public static void main(String[] args) throws ParseException {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateTimeString = "1970-01-01 00:00:01";
Date date = simpleDateFormat.parse(dateTimeString);
System.out.println("1 second = " + date.getTime());
}
}
输出如下:
1 second = -10799000
应该是这样的
1 second = 1000
如果Date 是“即时”。
数字10799000 是3*60*60*1000-1000 - 我当地时间的时区偏移量。
这意味着,Date 类是双重的。它的毫秒部分可能会相对于hh mm ss 部分偏移时区偏移量。
这意味着,如果任何实用程序根据其部件 (hh mm ss) 返回 Date 对象,则它会隐式转换为本地时间。 getTime() 同时表示不同的时间。我的意思是如果这个程序在不同的机器上同时运行,getTime() 会是一样的,而时间部分会不同。
因此,开头的代码示例是正确的:它采用Date 的“即时”部分,并提供系统时区部分,该部分隐含在Date 中使用。 IE。它将双 Date 对象转换为具有相同部分的显式 LocalDateTime 对象。因此,之后的格式是正确的。
更新 3
活动更有趣:
Date date = new Date(70, 0, 1, 0, 0, 1);
assertEquals(1000, date.getTime());
此测试失败。
UDPATE 4
新代码。献给所有信徒。
public class DataNature3 {
public static class TZ extends java.util.TimeZone {
private int offsetMillis;
public TZ(int offsetHours) {
this.offsetMillis = offsetHours * 60 * 60 * 1000;
}
@Override
public int getOffset(int era, int year, int month, int day, int dayOfWeek, int milliseconds) {
throw new UnsupportedOperationException();
}
@Override
public void setRawOffset(int offsetMillis) {
this.offsetMillis = offsetMillis;
}
@Override
public int getRawOffset() {
return offsetMillis;
}
@Override
public boolean useDaylightTime() {
return false;
}
@Override
public boolean inDaylightTime(Date date) {
return false;
}
}
public static void main(String[] args) {
Date date = new Date(0);
for(int i=0; i<10; ++i) {
TimeZone.setDefault(new TZ(i));
if( i<5 ) {
System.out.println("I am date, I am an instant, I am immutable, my hours property is " + date.getHours() + ", Amen!");
}
else {
System.out.println("WTF!? My hours property is now " + date.getHours() + " and changing! But I AM AN INSTANT! I AM IMMUTABLE!");
}
}
System.out.println("Oh, please, don't do that, this is deprecated!");
}
}
输出:
I am date, I am an instant, I am immutable, my hours property is 0, Amen!
I am date, I am an instant, I am immutable, my hours property is 1, Amen!
I am date, I am an instant, I am immutable, my hours property is 2, Amen!
I am date, I am an instant, I am immutable, my hours property is 3, Amen!
I am date, I am an instant, I am immutable, my hours property is 4, Amen!
WTF!? My hours property is now 5 and changing! But I AM AN INSTANT! I AM IMMUTABLE!
WTF!? My hours property is now 6 and changing! But I AM AN INSTANT! I AM IMMUTABLE!
WTF!? My hours property is now 7 and changing! But I AM AN INSTANT! I AM IMMUTABLE!
WTF!? My hours property is now 8 and changing! But I AM AN INSTANT! I AM IMMUTABLE!
WTF!? My hours property is now 9 and changing! But I AM AN INSTANT! I AM IMMUTABLE!
Oh, please, don't do that, this is deprecated!
【问题讨论】:
-
那么
Date不是你想要的,因为它代表一个瞬间时间,它只有一个当地的年/月/日/小时/分钟/秒当您应用时区时。同一个瞬间在不同的时区有不同的值,没有时区就没有意义。这就像在不指定编码的情况下要求字符串的二进制编码...... -
@Dims:不,它真的不是
Date是一个瞬间——自 Unix 时代以来的毫秒数。这不是因为某个任意时间点 - 它有一个明确指定的起源。你对抗这个问题的时间越长,你遇到问题的时间就越长。 请接受。如果您不相信我,您应该阅读java.util.Date文档,该文档开头是“Date 类表示特定的时间瞬间,精度为毫秒。” -
如果你只有一个绝对时间点(相对于 UNIX 时间戳),没有时区就无法格式化它,因为在不同时区会显示为不同的时间。你的价值来自哪里,以什么形式出现?
-
您的测试并未显示您认为它显示的内容。您在系统默认时区解析它,并在系统默认时区构建 1970-01-01T00:00:01。但是,我在这里完成了。您忽略了文档中的内容以及在日期/时间工作方面具有丰富经验的人所说的内容。当然欢迎你这样做,但你不会取得任何进展。您应该遵循 SLaks 的建议并使用
LocalTime,因为这就是您收到的价值所代表的含义。它并不代表一个明确的瞬间。 -
Dims,我相信您问题的真正答案是:远离
java.util.Date。它让你感到困惑,你没有理由想要它。改用java.time中的新类,就像@JonSkeet 提到的那样。