如何使用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
部分。
这意味着,如果任何实用程序根据它的部分(Date
)返回hh mm ss
个对象,则它会隐式转换为本地时间。 getTime()
同时意味着不同的时间。我的意思是在不同的机器上,如果这个程序同时运行,getTime()
将是相同的,而时间部分将是不同的。
所以,开头的代码示例是正确的:它需要" instant" Date
的一部分,并提供系统时区部分,该部分隐含在Date
中使用。即它将双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!
答案 0 :(得分:33)
TL; DR:您关注系统本地时区的使用是正确的,但是当您使用系统本地时区构建{{{ 1}}首先。
如果您只希望格式化的字符串具有Date
,Date.getDate()
,Date.getMonth()
等返回的相同组件,那么原始代码是合适的:< / p>
Date.getYear()
你说你害怕使用LocalDateTime ldt = LocalDateTime.ofInstant(in.toInstant(), ZoneId.systemDefault());
会引入一些变化&#34; - 但这恰恰是ZoneId.systemDefault()
等使用的。
Date.getDate()
没有任何&#34;双重合同&#34;这使您可以将其视为无时区表示。 只是一个瞬间。几乎所有允许您构建或解构为组件的方法都可以清楚地记录下来,使用系统默认时区,就像使用Date
一样。 (一个值得注意的例外是ZoneId.systemDefault()
方法。)
隐式使用系统默认时区不与UTC
是一个有效的无时区表示相同,并且很容易证明原因:它可以很容易丢失数据。考虑&#34; 2017年3月26日,凌晨1:30和#34;的无时区日期和时间。您可能希望能够对其进行文本表示,解析它,然后再重新格式化。如果你在欧洲/伦敦时区这样做,你将遇到问题,如下所示:
Date
输出结果为2017-03-26T02:30。并不是代码中出现了一个错误的错误 - 如果你将其更改为显示在上午9:30,那就可以正常工作了。
问题是2017-03-26T01:30 由于DST而在欧洲/伦敦时区不存在 - 凌晨1点,时钟跳到凌晨2点。
因此,如果您对这种破碎感到满意,那么请确保使用import java.util.*;
import java.time.*;
import java.time.format.*;
public class Test {
public static void main(String[] args) {
TimeZone.setDefault(TimeZone.getTimeZone("Europe/London"));
Date date = new Date(2017 - 1900, 3 - 1, 26, 1, 30);
Instant instant = date.toInstant();
ZoneId zone = ZoneId.systemDefault();
LocalDateTime ldt = LocalDateTime.ofInstant(instant, zone);
System.out.println(ldt); // Use ISO-8601 by default
}
}
和系统本地时区。否则,请不要为此目的使用Date
。
如果你绝对 以这种破碎的方式使用Date
,那么使用已弃用了大约20年的方法,因为它们会产生误导,但是你是能够改变系统时区,然后将其更改为没有 - 而且从未有过 - DST。 UTC是这里显而易见的选择。此时,您可以在本地日期/时间和Date
之间进行转换,而不会丢失数据。它仍然是Date
的错误用法, 就像Date
一样,但至少你不会丢失数据。< / p>
或者您可以确保无论何时从本地日期/时间构建Instant
,您都可以使用UTC进行转换,而不是系统本地时区...是否&#39 ; s通过Date
方法,或使用UTC中的Date.UTC
或其他任何内容解析文本。不幸的是,您还没有告诉我们您的SimpleDateFormat
价值来自何处...
答案 1 :(得分:2)
您可以根据您的要求使用。
java.util.Date
DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
Date date = new Date();
System.out.println(dateFormat.format(date));
java.util.Calendar
DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
Calendar cal = Calendar.getInstance();
System.out.println(dateFormat.format(cal.getTime()));
java.time.LocalDateTime
DateTimeFormatter dateTimeFormat = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
LocalDateTime localDateTime = LocalDateTime.now();
System.out.println(dateTimeFormat.format(localDateTime));