admin管理员组文章数量:1122846
Let's get this over with.
How do you convert a LocalDateTime
to a java.util.Date
without any "magic tricks" pulled by Java (which are becoming less and less amusing to me)?
The year, month, and day are correct (January 1, 1900), but the time is for some reason 0:29:43 (instead of 00:00:00, as I intended).
Your time, I figure, may be different, for example because it may depend on time zone.
import java.util.Date;
import java.time.LocalDateTime;
import java.time.Instant;
import java.time.ZoneId;
public class DateDemo {
public static void main(String[] args) {
Date date = toDate(1900, 1, 1, 0, 0); // must be: January 1, 1900. 00:00:00
System.out.println(ZoneId.systemDefault()); // Europe/Moscow
System.out.println(date); // actual: Mon Jan 01 00:29:43 MSK 1900
}
static Date toDate(int year, int month, int day, int hours, int minutes) {
// LocalDateTime appeared a convenient medium, what with its handy factory methods
LocalDateTime localDateTime = LocalDateTime.of(year, month, day, hours, minutes);
Instant instant = localDateTime.atZone(ZoneId.systemDefault()).toInstant();
return Date.from(instant);
}
}
If for some reason it's impossible, you may change LocalDateTime
to some other class from java.util.time
. The point is I have year, month, day, hours, minutes, seconds and need to make a Date
instance from that concisely and readably (preferably, without using any deprecated API). LocalDateTime
factory methods appeared handy.
This works but has one deprecated call (should be zero), generates two warnings in total and is ugly AF. Thus suboptimal.
new Date(localDateTime.getYear() - 1900, localDateTime.getMonth().getValue(), localDateTime.getDayOfMonth(), localDateTime.getHour(), localDateTime.getMinute(), localDateTime.getSecond())
I have to use java.util.Date
. We got a legacy project. Our APIs expect Date
instances.
I examined these before posting (some of them suggest the same, non-working, solution): 1, 2, 3, 4.
Java 8
Let's get this over with.
How do you convert a LocalDateTime
to a java.util.Date
without any "magic tricks" pulled by Java (which are becoming less and less amusing to me)?
The year, month, and day are correct (January 1, 1900), but the time is for some reason 0:29:43 (instead of 00:00:00, as I intended).
Your time, I figure, may be different, for example because it may depend on time zone.
import java.util.Date;
import java.time.LocalDateTime;
import java.time.Instant;
import java.time.ZoneId;
public class DateDemo {
public static void main(String[] args) {
Date date = toDate(1900, 1, 1, 0, 0); // must be: January 1, 1900. 00:00:00
System.out.println(ZoneId.systemDefault()); // Europe/Moscow
System.out.println(date); // actual: Mon Jan 01 00:29:43 MSK 1900
}
static Date toDate(int year, int month, int day, int hours, int minutes) {
// LocalDateTime appeared a convenient medium, what with its handy factory methods
LocalDateTime localDateTime = LocalDateTime.of(year, month, day, hours, minutes);
Instant instant = localDateTime.atZone(ZoneId.systemDefault()).toInstant();
return Date.from(instant);
}
}
If for some reason it's impossible, you may change LocalDateTime
to some other class from java.util.time
. The point is I have year, month, day, hours, minutes, seconds and need to make a Date
instance from that concisely and readably (preferably, without using any deprecated API). LocalDateTime
factory methods appeared handy.
This works but has one deprecated call (should be zero), generates two warnings in total and is ugly AF. Thus suboptimal.
new Date(localDateTime.getYear() - 1900, localDateTime.getMonth().getValue(), localDateTime.getDayOfMonth(), localDateTime.getHour(), localDateTime.getMinute(), localDateTime.getSecond())
I have to use java.util.Date
. We got a legacy project. Our APIs expect Date
instances.
I examined these before posting (some of them suggest the same, non-working, solution): 1, 2, 3, 4.
Java 8
Share Improve this question edited Nov 27, 2024 at 5:53 Cagepi asked Nov 26, 2024 at 13:33 CagepiCagepi 1951 silver badge7 bronze badges 32 | Show 27 more comments2 Answers
Reset to default 2tl;dr
the time is for some reason 0:29:43
Your result is unreproducible. See your code run at Ideone.com. Result: Mon Jan 01 00:00:00 GMT 1900
. Your JVM’s current default time zone may produce other results, but you neglected to post that time zone. Run: System.out.println( ZoneId.systemDefault() ) ;
.
Read on for a better approach in getting to a java.util.Date
from a LocalDateTime
object. Time zone is crucial.
java.util.Date // Legacy class. Terribly designed; use only if you must.
.from ( // New conversion method added to the old class.
LocalDateTime // Represents a date with time-of-day but lacking the context of a time zone or offset-from-UTC.
.of (
LocalDate.of ( y , m , d ) , // Date-only.
LocalTime.of ( h , m , s ) // Time-only.
) // Returns a `LocalDateTime` object.
.atZone (
ZoneId.of( "America/Edmonton" ) // Returns a time zone, a `ZoneId` object.
) // Returns a `ZonedDateTime` object.
.toInstant() // Returns a `Instant` object.
)
For example, using an offset-from-UTC of zero hours-minutes-seconds:
String output =
java.util.Date
.from (
LocalDateTime
.of (
LocalDate.of ( 1900 , 1 , 1 ) ,
LocalTime.MIDNIGHT
)
.atZone (
ZoneOffset.UTC
)
.toInstant ( )
)
.toString ( ); // This method lies to you! The JVM’s current default time zone is dynamically applied, while the object actually represents a moment as seen with an offset-from-UTC of zero hours-minutes-seconds. Very confusing anti-feature.
System.out.println ( "output = " + output );
When run on a JVM with a default time zone of UTC (offset of zero):
output = Mon Jan 01 00:00:00 UTC 1900
For fun, in that code above change that time zone from ZoneOffset.UTC
to ZoneId.of ( "Asia/Tokyo" )
:
output = Sun Dec 31 15:00:00 UTC 1899
Time Zone
The source of your confusion is time zone, on two counts.
Count # 1 — Determine a moment
The legacy class java.util.Date
represents a moment, a specific point on the timeline. A moment is made up of a date, a time of day, and a time zone.
Moment = Date + Time + Zone
For any given moment, the time and the date both vary around the globe by time zone. Noon on January 23 of 2025 in Tokyo Japan is hours earlier than noon in Toulouse France, which is hours earlier than Toledo Ohio US. In this example, we have three different moments, several hours apart.
If you have a date and a time in hand, and want to determine a moment, you need to know in which time zone is that date and time meant to be seen.
If you do not know the intended time zone, then you have reached an impasse and cannot continue. You absolutely cannot guess which zone, you need to know with certainty. And absolutely should not pick a zone randomly as your code is doing with a call to ZoneId.systemDefault
which accesses the JVM’s current default time zone. That default can be changed at any moment by any code in any app within that JVM.
If you know the intended zone, then proceed. A real time zone is named in format of Continent/Region
, not 2-4 letter codes like CST
or IST
.
ZoneId z = ZoneId.of( "Asia/Tokyo" ) ;
Apply that zone to your LocalDateTime
object to determine a moment, a point on the timeline. The LocalDateTime
object has two parts: date and time. Adding the third part, time zone, makes a moment. In Java we represent this moment as a ZonedDateTime
object.
ZonedDateTime zdt = myLocalDateTime.atZone( z ) ; // Voilà, a moment.
We can adjust that moment to be seen from an offset of zero hours-minutes-seconds from UTC by extracting an Instant
. Same moment, different wall-clock/calendar.
Instant instant = zdt.toInstant() ;
Convert from that modern class to its legacy counterpart, java.util.Date
.
java.util.Date myJavaUtilDate = java.util.Date.from( instant ) ;
Count # 2 — Date#toString
lies
Also very confusing is the fact that java.util.Date#toString
lies to you. In generating its text, the method dynamically applies the JVM‘s current default time zone. The result is not the semantics of what is stored within the Date
object.
The stored value is a moment as seen in UTC, not as seen in the JVM’s current default time zone.
This lie is a well-intentioned feature (anti-feature?) but terribly poor design choice. This is but one of many reasons to avoid the legacy date-time classes. Always do your work in java.time, then use the new conversion methods added to the old legacy classes as needed to interface with the parts of your own code not yet updated to java.time.
Converting java.util.Date
to Instant
To examine a java.util.Date
object, you can switch back to java.time. With the java.time.Instant
class, your can generate text in standard ISO 8601 format.
Instant instant = myJavaUtilDate.toInstant() ;
String output = instant.toString() ; // Generate text in standard ISO 8601 format.
2024-11-26T16:18:22.123456Z
The Z
on the end is a standard abbreviation for an offset of zero, +00:00
.
Note the fractional second. The legacy class java.util.Date
is limited to mere milliseconds resolution. In contrast, the modern java.time.Instant
class resolves to nanoseconds. So calling java.util.Date#from( instant )
may involve data-loss, truncating any microseconds and nanoseconds.
Count from epoch reference
Another way to verify the value of a java.util.Date
is to extract its count-from-epoch. Both java.util.Date
& java.time.Instant
are built on a count from the same epoch reference of first moment of 1970 in UTC, 1970-01-01T00:00Z. The java.util.Date
holds a count of milliseconds, while java.time.Instant
holds a count of whole seconds plus a number of nanoseconds.
long millisecondsOfJavaUtilDate = myJavaUtilDate.getTime() ;
long millisecondsOfInstant = instant.toEpochMilli() ;
Again, be aware that the Instant
object may hold microseconds or nanoseconds. Calling Instant#toEpochMilli()
ignores that info.
A solution (aka hack) is to get the different components of the LocalDateTime and plug them into a Date object. It might work or not depending on your needs.
public static Date convertToDate(LocalDateTime localDateTime) {
int year = localDateTime.getYear();
int month = localDateTime.getMonthValue();
int day = localDateTime.getDayOfMonth();
int hour = localDateTime.getHour();
int minute = localDateTime.getMinute();
int second = localDateTime.getSecond();
@SuppressWarnings("deprecation")
Date date = new Date(year - 1900, month - 1, day, hour, minute, second);
return date;
}
Bear in mind this returns a different point in time compared to the original LocalDateTime, but it sounds from your question that's what you want.
And caveat emptor: please add plenty of tests for whatever you are doing, including when there are daylight savings changes.
本文标签:
版权声明:本文标题:java - Year, month, day, hours, minutes to Date without "magic" - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1736232044a1914735.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
Date
is likeInstant
. The instant represented by theDate
you got is the same as the instant represented bylocalDateTime.atZone(ZoneId.systemDefault()).toInstant()
. It's just that itstoString
method (or most of the other formatting methods in the legacy API) does not format it correctly. – Sweeper Commented Nov 26, 2024 at 13:410:29:43
? And what is the defaultTimeZone
(ZoneId
)? – user85421 Commented Nov 26, 2024 at 17:24Date
’stoString()
method should use that offset. – Holger Commented Nov 28, 2024 at 12:28