如何使用 DateTimeFormatter 便携式格式化 java.util.Date?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/43527477/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to format java.util.Date with DateTimeFormatter portable?
提问by Dims
How to format java.util.Date
with DateTimeFormatter
portable?
如何java.util.Date
用DateTimeFormatter
便携式格式化?
I can't use
我不能用
Date in = readMyDateFrom3rdPartySource();
LocalDateTime ldt = LocalDateTime.ofInstant(in.toInstant(), ZoneId.systemDefault());
ldt.format(dateTimeFormatter);
because I afraid that usage of ZoneId.systemDefault()
can introduce some changes.
因为我担心 的用法ZoneId.systemDefault()
会带来一些变化。
I need to format exactly that object I have.
我需要完全格式化我拥有的那个对象。
UPDATE
更新
Note: time is time. Not space. Timezone is very rough measure of longitude, i.e. space. I don't need it. Only time (and date).
注意:时间就是时间。不是空间。时区是非常粗略的经度度量,即空间。我不需要它。只有时间(和日期)。
UPDATE 2
更新 2
I wrote the following program, proving, that Date
DOES NOT only contain correct "instant":
我编写了以下程序,证明它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());
}
}
The output is follows:
输出如下:
1 second = -10799000
While it should be
虽然应该是
1 second = 1000
if Date
was "Instant".
如果Date
是“即时”。
The number 10799000
is 3*60*60*1000-1000
- the timezone offset of my local time.
数字10799000
是3*60*60*1000-1000
- 我本地时间的时区偏移量。
This means, that Date
class is dual. It's millisecond part may be shifted relatively to hh mm ss
part by timezone offset.
这意味着,Date
该类是双重的。它的毫秒部分可以hh mm ss
通过时区偏移量相对于部分移动。
This means, that if any utility returns Date
object in terms of it's parts (hh mm ss
) then it implicitly converted to local time. And getTime()
means DIFFERENT time simultaneously. I mean on different machines if this program run at the same time, getTime()
will be the same, while time parts will be different.
这意味着,如果任何实用程序Date
根据它的部分 ( hh mm ss
)返回对象,那么它会隐式转换为本地时间。并且getTime()
同时表示不同的时间。我的意思是在不同的机器上,如果这个程序同时运行,getTime()
将是相同的,而时间部分会有所不同。
So, the code example in the beginning is correct: it takes "instant" part of Date
, and supplies system timezone part, which was implicitly used inside Date
. I.e. it converts dual Date
object into explicit LocalDateTime
object with the same parts. And hence, formatting after that, is correct.
因此,开头的代码示例是正确的:它采用 的“即时”部分Date
,并提供系统时区部分,在Date
. 即它将双重Date
对象转换为LocalDateTime
具有相同部分的显式对象。因此,之后的格式是正确的。
UPDATE 3
更新 3
Event funnier:
活动更有趣:
Date date = new Date(70, 0, 1, 0, 0, 1);
assertEquals(1000, date.getTime());
this test fails.
这个测试失败了。
UDPATE 4
UDPATE 4
New code. Dedicated to all believers.
新代码。献给所有信徒。
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!");
}
}
Output:
输出:
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!
回答by Jon Skeet
TL;DR: You're right to be concerned about the use of the system local time zone, but you should have been concerned earlier in the process, when you used the system local time zone to construct a Date
in the first place.
TL;DR:您关注系统本地时区的使用是正确的,但您应该在过程的早期关注,当您首先使用系统本地时区构建 aDate
时。
Ifyou just want the formatted string to have the same components that Date.getDate()
, Date.getMonth()
, Date.getYear()
etc return then your original code is appropriate:
如果你只是想格式化字符串具有相同的组件Date.getDate()
,Date.getMonth()
,Date.getYear()
等再回到你原来的代码是合适的:
LocalDateTime ldt = LocalDateTime.ofInstant(in.toInstant(), ZoneId.systemDefault());
You say you're "afraid that usage of ZoneId.systemDefault()
can introduce some changes" - but that's precisely what Date.getDate()
etc use.
你说你“害怕 的使用ZoneId.systemDefault()
会带来一些变化” - 但这正是Date.getDate()
etc 使用的。
Date
doesn't have any kind of "dual contract" that lets you view it as a time-zone-less representation. It isjust an instant in time. Almost every single method that lets you construct or deconstruct it into components is clearly documented to use the system default time zone, just like your use of ZoneId.systemDefault()
. (One notable exception is the UTC
method.)
Date
没有任何类型的“双重合同”可以让您将其视为无时区的表示。这是刚刚的时间瞬间。几乎每一种允许您将其构造或解构为组件的方法都清楚地记录为使用系统默认时区,就像您使用ZoneId.systemDefault()
. (一个值得注意的例外是UTC
方法。)
Implicitly using the system default time zone is notthe same as Date
being a valid time-zone-less representation, and it's easy to demonstrate why: it can lose data, very easily. Consider the time-zone-free date and time of "March 26th 2017, 1:30am". You may well want to be able to take a text representation of that, parse it, and then later reformat it. If you do that in the Europe/London time zone, you'll have problems, as demonstrated below:
隐式使用系统默认的时区不一样的Date
是一个有效的时区少表示,这很容易说明为什么:它可能会丢失数据,很容易。考虑“2017 年 3 月 26 日,凌晨 1:30”的无时区日期和时间。您可能希望能够获取它的文本表示,解析它,然后重新格式化它。如果您在欧洲/伦敦时区执行此操作,则会遇到问题,如下所示:
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
}
}
The output is 2017-03-26T02:30. It's not that there's an off-by-one error in the code - if you change it to display 9:30am, that will work just fine.
输出为 2017-03-26T02:30。并不是说代码中存在逐一错误 - 如果您将其更改为显示上午 9:30,那将正常工作。
The problem is that 2017-03-26T01:30 didn't exist in the Europe/London time zonedue to DST - at 1am, the clock skipped forward to 2am.
问题是由于夏令时,欧洲/伦敦时区不存在2017-03-26T01:30 - 在凌晨 1 点,时钟跳到凌晨 2 点。
So if you're happy with that sort of brokenness, then sure, use Date
and the system local time zone. Otherwise, don't try to use Date
for this purpose.
因此,如果您对这种损坏感到满意,那么当然可以使用Date
系统本地时区。否则,请勿尝试Date
用于此目的。
If you absolutely haveto use Date
in this broken way, using methods that have been deprecated for about 20 years because they're misleading, but you're able to change the system time zone, then change it to something that doesn't have - and never has had - DST. UTC is the obvious choice here. At that point, you can convert between a local date/time and Date
without losing data. It's still a bad use of Date
, which isjust an instant in time like Instant
, but at least you won't lose data.
如果您绝对必须以Date
这种损坏的方式使用,使用已经被弃用了大约 20 年的方法,因为它们具有误导性,但是您可以更改系统时区,然后将其更改为没有的东西 -从未有过 - 夏令时。UTC 是这里的明显选择。此时,您可以在本地日期/时间之间进行转换而Date
不会丢失数据。它仍然是一个不好用的Date
,这是刚刚在这样一个时刻Instant
,但至少你不会丢失数据。
Or you could make sure that whenever you construct a Date
from a local date/time, you use UTC to do the conversion, of course, instead of the system local time zone... whether that's via the Date.UTC
method, or by parsing text using a SimpleDateFormat
that's in UTC, or whatever it is. Unfortunately you haven't told us anything about where your Date
value is coming from to start with...
或者您可以确保每当您Date
从本地日期/时间构造 a时,您都使用 UTC 进行转换,当然,而不是系统本地时区......无论是通过Date.UTC
方法,还是通过使用解析文本SimpleDateFormat
那是UTC,或者其他什么。不幸的是,您没有告诉我们您的Date
价值从何而来...
回答by Basil Bourque
tl;dr
tl;博士
How to format java.util.Date with DateTimeFormatter portable?
如何使用 DateTimeFormatter 便携式格式化 java.util.Date?
Instant instant = myJavaUtilDate.toInstant() ; // When encountering a `Date`, immediately convert from troublesome legacy class to modern *java.time* class. Then forget all about that `Date` object!
ZoneId z = ZoneId.systemDefault() ; // Or ZoneId.of( "America/Montreal" ) or ZoneId.of( "Africa/Tunis" ) etc.
ZonedDateTime zdt = instant.atZone( z ) ;
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.FULL ).withLocale( Locale.CANADA_FRENCH ) ;
String output = zdt.format( f ) ;
Or, a one-liner… (not that I recommend such a complicated one-liner)
或者,单线……(不是我推荐这么复杂的单线)
myJavaUtilDate.toInstant().atZone( ZoneId.systemDefault() ).format( DateTimeFormatter.ofLocalizedDateTime( FormatStyle.FULL ).withLocale( Locale.CANADA_FRENCH ) )
Details
细节
The Answer by Jon Skeetis correct. Here is my own take, with some specific points.
Jon Skeet的答案是正确的。这是我自己的看法,有一些具体的观点。
Avoid legacy date-time classes.
避免遗留的日期时间类。
Do not use java.util.Date
, java.util.Calendar
, SimpleDateFormat
, java.sql.Date/Time/Timestamp
and other related classes dating back to the earliest versions of Java. While a well-intentioned early attempt at sophisticated handling of date-time values, they fell short of the mark. Now supplanted by the java.timeclasses.
不要使用java.util.Date
,java.util.Calendar
,SimpleDateFormat
,java.sql.Date/Time/Timestamp
和其他相关类追溯到Java的最早版本。虽然早期尝试对日期时间值进行复杂的处理,但他们没有达到目标。现在被java.time类取代。
If you must inter-operate with the legacy classes in old code not yet updated for java.time, convert. Call new methods on the old classes.
如果必须与尚未针对java.time更新的旧代码中的遗留类进行互操作,请转换。在旧类上调用新方法。
Instant instant = myJavaUtilDate.toInstant() ;
You did this in your Question, but then went on to ponder more about Date
. Forget about java.util.Date
. Pretend it never existed. Both Date
and Instant
represent the same thing: A moment in UTC, a point on the timeline. The only difference is concept is that the modern Instant
has a finer resolution of nanoseconds rather than milliseconds in Date
.
你在你的问题中这样做了,但随后继续思考更多关于Date
. 忘记了java.util.Date
。假装它从未存在过。双方Date
并Instant
表示同样的事情:在UTC了一下,在时间轴上的一个点。唯一的区别是概念是现代Instant
具有更精细的纳秒分辨率而不是毫秒Date
。
LocalDateTime
!= moment
LocalDateTime
!= 时刻
You then converted from an Instant
to a LocalDateTime
. You moved from a specific point on the timeline, to a vague range of possible moments. This makes no sensein nearly any practical scenario.
然后,您从 an 转换Instant
为 a LocalDateTime
。你从时间线上的一个特定点移动到一个模糊的可能时刻范围。这在几乎任何实际场景中都没有意义。
A LocalDateTime
lacks any concept of time zone or offset-from-UTC. Having no such concept is its very purpose. Ditto for LocalDate
& LocalTime
: no concept of zone/offset. Think of the “Local” part as meaning “anylocality” or “nolocality”, not any one particular locality.
ALocalDateTime
缺乏任何时区或UTC 偏移量的概念。没有这样的概念就是它的目的。LocalDate
&同上LocalTime
:没有区域/偏移的概念。将“本地”部分视为“任何地点”或“无地点”,而不是任何特定地点。
Lacking zone/offset means a LocalDateTime
does notrepresent a moment. It is nota point on the timeline. It is a vague idea about potentialmoments, along a range of about 26-27 hours. Until you place a LocalDateTime
in a context of a particular zone or offset, it has no real meaning.
缺乏区/补偿装置一个LocalDateTime
不会不表示一个时刻。这不是时间线上的一个点。这是一个关于潜在时刻的模糊概念,大约在 26-27 小时的范围内。除非您将 aLocalDateTime
放在特定区域或偏移的上下文中,否则它没有真正的意义。
Use LocalDateTime
for use such as “Christmas this year starts at first moment of December 25th, 2018”. Such a statement implies anywhere, or nowhere specifically.
用途LocalDateTime
使用,如“今年圣诞节开始于2018年12月25日的第一时刻”。这样的声明暗示了任何地方,或者没有具体的地方。
LocalDate ld = LocalDate.of(2018, Month.DECEMBER , 25);
LocalTime lt = LocalTime.MIN ; // 00:00
LocalDateTime xmasStartsAnywhere = LocalDateTime.of( ld , lt ) ;
xmasStartsAnywhere.toString(): 2018-12-25T00:00
xmasStartsAnywhere.toString(): 2018-12-25T00:00
ZonedDateTime
= moment
ZonedDateTime
= 时刻
Now add in the context of a time zone. The first kids getting their delivery from Santa will be asleep in their beds on Kiritimati(“Christmas Island”) in the first hour of the 25th as seen on the wall-clocks of their homes.
现在在时区的上下文中添加。25 日的第一个小时,第一批收到圣诞老人送来的孩子将在Kiritimati(“圣诞岛”)的床上睡着,就像他们家的挂钟一样。
ZoneId z = ZoneId.of("Pacific/Kiritimati");
LocalDate ld = LocalDate.of(2018, Month.DECEMBER , 25);
ZonedDateTime zdtKiritimati = ZonedDateTime.of( ld , LocalTime.MIN , z );
zdtKiritimati.toString(): 2018-12-25T00:00+14:00[Pacific/Kiritimati]
zdtKiritimati.toString(): 2018-12-25T00:00+14:00[Pacific/Kiritimati]
By the way, we could have assigned that time zone (ZoneId
) directly to to our LocalDateTime
to get a ZonedDateTime
rather than start from scratch.
顺便说一句,我们可以将时区 ( ZoneId
) 直接分配给我们的LocalDateTime
,ZonedDateTime
而不是从头开始。
ZonedDateTime zdtKiritimati = xmasStartsAnywhere.atZone( z ) ; // Move from the vague idea of the beginning of Christmas to the specific moment Christmas starts for actual people in an actual location.
Meanwhile, at the very same moment Santa is laying out presents in Kiribati, the kids on the farms in Québec are just rising at 5 AM the day before (Christmas Eve) to milk the cows and tap the maple sap.
与此同时,就在圣诞老人在基里巴斯布置礼物的同一时刻,魁北克农场的孩子们在前一天(圣诞节前夕)凌晨 5 点起床,为奶牛挤奶并榨取枫树汁。
ZonedDateTime zdtMontreal = zdtKiribati.withZoneSameInstant( ZoneId.of( "America/Montreal") );
zdtMontreal.toString(): 2018-12-24T05:00-05:00[America/Montreal]
zdtMontreal.toString(): 2018-12-24T05:00-05:00[美国/蒙特利尔]
So, after finishing in Kiribati, the elves route Santa westward, moving through a succession of new midnight hours, starting in the far east Asia & New Zealand, then India, then the Middle East, then Africa & Europe, and eventually the Americas. The offsets currently range from 14 hours ahead of UTC to 12 hours behind. So Santa has just over 26 hours to get the job done.
因此,在基里巴斯结束后,精灵们将圣诞老人向西行进,经过一系列新的午夜时间,从远东亚洲和新西兰开始,然后是印度,然后是中东,然后是非洲和欧洲,最后是美洲。目前的偏移范围从 UTC 前 14 小时到后 12 小时不等。所以圣诞老人只有超过 26 小时的时间来完成工作。
Epoch
时代
Regarding your experiments with the epoch referenceof first moment of 1970 in UTC, you were inadvertently injecting your own JVM's current default time zone. Your input string 1970-01-01 00:00:01
is faulty in that it lacks any indicator of a time zone or offset-from-UTC. In other words, that input string is the equivalent of a LocalDateTime
object. When parsing that string as a Date
(having UTC), the Date
class silently implicitly applied your JVM's current default time zone while interpreting that input string, in a desperate attempt to create meaning, to determine a specific moment. Once again you are inappropriately mixing a date-time lacking any concept of zone/offset with a date-time having a zone/offset.
关于您对 UTC 中 1970 年第一时刻的纪元参考进行的实验,您无意中注入了自己的 JVM 当前默认时区。您的输入字符串1970-01-01 00:00:01
有问题,因为它缺少时区或UTC 偏移量的任何指示符。换句话说,该输入字符串相当于一个LocalDateTime
对象。当将该字符串解析为Date
(具有 UTC)时,Date
该类在解释该输入字符串的同时,默默地隐式地应用了您的 JVM 的当前默认时区,拼命地试图创造意义,以确定特定时刻。再一次,您不恰当地将缺乏任何区域/偏移量概念的日期时间与具有区域/偏移量的日期时间混合在一起。
Per the documentation for Date.parse
:
根据以下文档Date.parse
:
If a time zone or time-zone offset has been recognized, then the year, month, day of month, hour, minute, and second are interpreted in UTC and then the time-zone offset is applied. Otherwise, the year, month, day of month, hour, minute, and second are interpreted in the local time zone.
如果已识别时区或时区偏移,则以 UTC 解释年、月、日、小时、分钟和秒,然后应用时区偏移。否则,以本地时区解释年、月、月中的日、小时、分钟和秒。
That “local” in the last sentence was a poor choice of words. Should have been written “interpreted by applying your JVM's current default time zone”.
最后一句话中的“本地”是一个糟糕的词选择。应该写成“通过应用 JVM 的当前默认时区来解释”。
The key here is that you failed to specify a zone/offset, and the Date
class filled in the missing information. A well-intentioned feature, but confusing and counter-productive.
这里的关键是您没有指定区域/偏移量,并且Date
该类填充了缺少的信息。一个善意的功能,但令人困惑和适得其反。
Moral of the story: If you intend a specific moment (a point on the timeline), always specify your desired/intended time zone explicitly.
故事寓意:如果您打算某个特定时刻(时间轴上的某个点),请始终明确指定您想要/想要的时区。
If you mean UTC, say UTC. In this next line, we include a Z
on the end, short for Zulu
and means UTC. This part about specifying UTC is where you went wrong by omission.
如果您指的是 UTC,请说 UTC。在下一行中,我们Z
在末尾添加了 a ,它Zulu
是 UTC 的缩写,表示 UTC。关于指定 UTC 的这部分是您因遗漏而出错的地方。
Instant instant = Instant.parse( "1970-01-01T00:00:01Z" ) ; // One second after the first moment of 1970 **in UTC**.
instant.toString(): 1970-01-01T00:00:01Z
Instant.toString(): 1970-01-01T00:00:01Z
By the way, another way of writing that code is to use a constant defined for the epoch reference 1970-01-01T00:00:00Z, and the Duration
class for representing a span of time unattached to the timeline.
顺便说一句,编写该代码的另一种方法是使用为纪元参考 1970-01-01T00:00:00Z 定义的常量,以及Duration
用于表示未附加到时间线的时间跨度的类。
Instant instant = Instant.EPOCH.plus( Duration.ofSeconds( 1 ) ) ;
instant.toString(): 1970-01-01T00:00:01Z
Instant.toString(): 1970-01-01T00:00:01Z
Your next experiment has the same story. You failed to specify a zone/offset, so Date
applied one while interpreting your zone-less input. A bad idea in my opinion, but that is the documented behavior.
你的下一个实验也有同样的故事。您未能指定区域/偏移量,因此Date
在解释您的无区域输入时应用了一个。我认为这是一个坏主意,但这是记录在案的行为。
Date date = new Date(70, 0, 1, 0, 0, 1);
assertEquals(1000, date.getTime()); // fails
You can see from the Date
object's generated string that it represents a date-time of one second after 1970 starts in another time zone rather than in UTC. Here is output from my JVM with default time zone of America/Los_Angeles
.
您可以从Date
对象生成的字符串中看到,它表示 1970 年以另一个时区而不是 UTC 开始后一秒的日期时间。这是我的 JVM 的输出,默认时区为America/Los_Angeles
.
date.toString(): Thu Jan 01 00:00:01 PST 1970
date.toString(): Thu Jan 01 00:00:01 PST 1970
Let's convert to Instant
for clarity. Notice how the hour-of-day is 8 AM in UTC. On that first day of 1970, people in zone America/Los_Angeles
used a wall-clock time eight hours behind UTC. So one second after midnight, 00:00:01
, on much of the west coast of North America is simultaneously 8 AM in UTC. Nothing “funny” going on here at all.
Instant
为了清楚起见,让我们转换为。请注意 UTC 中一天中的一小时是 8 点。在 1970 年的第一天, zone 中的人们America/Los_Angeles
使用了比 UTC 晚八小时的挂钟时间。因此00:00:01
,在北美西海岸的大部分地区,午夜后一秒同时是 UTC 时间的上午 8 点。这里根本没有什么“有趣”的事情发生。
Instant instant = date.toInstant() ; // 00:00:01 in `America/Los_Angeles` = 8 AM UTC (specifically, 08:00:01 UTC).
instant.toString(): 1970-01-01T08:00:01Z
Instant.toString(): 1970-01-01T08:00:01Z
Two important pieces are in play here:
这里有两个重要的部分在起作用:
- You must learn and understand that a moment, a point on the timeline, has different wall-clock time used by different different people in different places around the globe. In other words, the wall-clock time for any given moment varies around the globe by time zone.
- The poor design choices of the legacy date-time classes such as
java.util.Date
unfortunately complicate the situation. The ill-advised behavior brings confusion rather than clarity to the already confusing topic of date-time handling. Avoid the legacy classes.Use only java.timeclasses instead. Stop banging your head against a brick wall, and then your headache will go away.
- 您必须学习并理解,时间轴上的某个时刻具有不同的挂钟时间,供全球不同地方的不同人使用。换句话说,任何给定时刻的挂钟时间在全球各地因时区而异。
java.util.Date
不幸的是,遗留日期时间类的糟糕设计选择使情况复杂化。不明智的行为给已经令人困惑的日期时间处理主题带来了混乱而不是清晰。避免遗留类。只使用java.time类。停止用头撞砖墙,然后你的头痛就会消失。
Tips:
尖端:
- Learn to think, work, debug, log, and exchange data in UTC. Think of UTC as The One True Time?. Avoid translating back-and-forth between your own parochial time zone and UTC. Instead forget about your own zone and focus on UTC while at work programming/administrating. Keep a UTC clock on your desktop.
- Apply a time zone only when required by business logic or by expectation of user in presentation.
- Always specify your desired/expected time zone explicitly as optional argument. Even if you intend to use the current default value, explicitly call for the default, to make your code self-documenting about your intention. By the way… Ditto for
Locale
: always specify explicitly, never rely implicitly on default.
- 学习在 UTC 中思考、工作、调试、记录和交换数据。将 UTC 视为唯一的真实时间?. 避免在您自己的狭隘时区和 UTC 之间来回转换。相反,在工作编程/管理时忘记您自己的区域并专注于 UTC。在您的桌面上保留一个 UTC 时钟。
- 仅在业务逻辑需要或用户在演示中期望时应用时区。
- 始终将您想要/期望的时区明确指定为可选参数。即使您打算使用当前的默认值,也要明确调用默认值,以使您的代码自记录您的意图。顺便说一句......同上
Locale
:总是明确指定,从不隐式依赖默认值。
About java.time
关于java.time
The java.timeframework is built into Java 8 and later. These classes supplant the troublesome old legacydate-time classes such as java.util.Date
, Calendar
, & SimpleDateFormat
.
该java.time框架是建立在Java 8和更高版本。这些类取代了麻烦的旧的遗留日期时间类,例如java.util.Date
, Calendar
, & SimpleDateFormat
。
The Joda-Timeproject, now in maintenance mode, advises migration to the java.timeclasses.
现在处于维护模式的Joda-Time项目建议迁移到java.time类。
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
要了解更多信息,请参阅Oracle 教程。并在 Stack Overflow 上搜索许多示例和解释。规范是JSR 310。
Using a JDBC drivercompliant with JDBC 4.2or later, you may exchange java.timeobjects directly with your database. No need for strings nor java.sql.* classes.
使用符合JDBC 4.2或更高版本的JDBC 驱动程序,您可以直接与您的数据库交换java.time对象。不需要字符串或 java.sql.* 类。
Where to obtain the java.time classes?
从哪里获得 java.time 类?
- Java SE 8, Java SE 9, and later
- Built-in.
- Part of the standard Java API with a bundled implementation.
- Java 9 adds some minor features and fixes.
- Java SE 6and Java SE 7
- Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
- Android
- Later versions of Android bundle implementations of the java.time classes.
- For earlier Android, the ThreeTenABPproject adapts ThreeTen-Backport(mentioned above). See How to use ThreeTenABP….
- Java SE 8、Java SE 9及更高版本
- 内置。
- 具有捆绑实现的标准 Java API 的一部分。
- Java 9 添加了一些小功能和修复。
- Java SE 6和Java SE 7
- 多的java.time功能后移植到Java 6和7在ThreeTen-反向移植。
- 安卓
- 更高版本的 Android 捆绑实现 java.time 类。
- 对于早期的 Android,ThreeTenABP项目采用了ThreeTen-Backport(上面提到过)。请参阅如何使用ThreeTenABP ...。
The ThreeTen-Extraproject extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval
, YearWeek
, YearQuarter
, and more.
该ThreeTen-额外项目与其他类扩展java.time。该项目是未来可能添加到 java.time 的试验场。你可能在这里找到一些有用的类,比如Interval
,YearWeek
,YearQuarter
,和更多。
回答by Anshul Sharma
you can use as per your requirment.
您可以根据您的要求使用。
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));