tl;dr
- Use modern java.time classes. Built into Java.
- The day may not start at 00:00:00 on some dates in some places.
- Let java.time determine first moment of the day.
- Avoid the ambiguous term “midnight”.
- Think of “first moment of the day”.
- Search Stack Overflow to learn about Half-Open spans of time.
First moment of the day:
ZoneId z = ZoneId.of( "Pacific/Auckland" ) ; // Specify the region whose wall-clock time you want to perceive “today”. Specify time zone with `Continent/Region`, never the 3-4 letter pseudo-zones such as `EST` or `IST`.
LocalDate today = LocalDate.now( z ) ; // Capture the current date as seen in a specific time zone.
ZonedDateTime zdt = today.atStartOfDay( z ) ; // Let java.time determine first moment of the day.
Instant instant = zdt.toInstant() ; // Adjust from time zone to UTC. Same moment, same point on the timeline, different wall-clock time.
Details
Many problems at play here.
Adjusting zones
if UTC time is 30 seconds from epoch. And if that can be represented/converted to Thu Jan 1 00:00:30 EST 1970,
No, wrong. You cannot convert. Those two date-time values do not represent the same moment. If by EST
you meant a time zone such as America/New_York
or America/Montreal
, then those two values are several hours apart.
Instant thirtySecondsFromEpoch = Instant.EPOCH.plusSeconds( 30 ) ;
thirtySecondsFromEpoch.toString(): 1970-01-01T00:00:30Z
See that same moment in much of the east coast of North America.
ZoneId z = ZoneId.of( "America/New_York" ) ;
ZonedDateTime zdtAdjustedFromThirtySecondsFromEpoch = thirtySecondsFromEpoch.atZone( z ) ;
zdtAdjustedFromThirtySecondsFromEpoch.toString(): 1969-12-31T19:00:30-05:00[America/New_York]
Because most of the people on the east coast of North America use a wall-clock time that is five hours behind UTC, that same moment of 30 seconds past epoch is seen as half a minute past 7 PM the day before, the last day of 1969.
Try the same time-of-day, 30 seconds into the first day of 1970, but as seen in America/New_York
time zone rather than in UTC. Now we are talking about a different moment entirely. Half a minute into a new day happens five hours later than a half a minute into new day in UTC.
ZonedDateTime zdt = ZonedDateTime.of( 1970 , 1 , 1 , 0 , 0 , 30 , 0 , z ) ;
zdt.toString(): 1970-01-01T00:00:30-05:00[America/New_York]
zdt.toInstant().toString(): 1970-01-01T05:00:30Z
Time zone
You ignore the crucial issue of time zone.
A time zone is crucial in determining a date. For any given moment, the date varies around the globe by zone. For example, a few minutes after midnight in Paris France is a new day while still “yesterday” in Montréal Québec.
Specify a proper time zone name in the format of continent/region
, such as America/Montreal
, Africa/Casablanca
, or Pacific/Auckland
. Never use the 3-4 letter abbreviation such as EST
or IST
as they are not true time zones, not standardized, and not even unique(!).
java.util.Date::toString
The toString
method on Date
has a well-intentioned but troublesome behavior of implicitly applying the JVM’s current default time zone while generating a string representing the Date
object’s UTC value. Creates the false impression that this zone is present in the Date
.
This is one of many reasons to avoid this awful class. Use java.time classes instead.
java.time
The modern approach uses the java.time classes that supplant the troublesome old legacy date-time classes such as Date
.
If you have a Date
in hand, convert to/from java.time.Instant
via new methods added to the old class.
Instant instant = myJavaUtilDate.toInstant() ;
Avoid talking about “midnight” as that is an amorphous topic. Instead, focus on the first moment of the day, the start of the day.
Do not assume a day starts at 00:00:00. Anomalies such as Daylight Saving Time (DST) mean the day may start at another time-of-day such as 01:00:00. Let java.time determine the start of the day.
Determining the start of the day means determining the date. And determining the date requires a time zone, as mentioned above. So we need to adjust from your Instant
in UTC to a ZonedDateTime
in a specific time zone (ZoneId
) .
ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
Extract from that ZonedDateTime
just the date-only portion.
LocalDate ld = zdt.toLocalDate() ;
Now ask for the first moment of the day in a specified time zone.
ZonedDateTime zdtStartOfDay = ld.atStartOfDay( z ) ;
Truncating
You can truncate various java.time objects. Look for a truncatedTo
method where you pass a TemporalUnit
object (likely a ChronoUnit
) to specify the granularity you want in your result. No need for the Apache DateUtils for this purpose.
If truncating Instant
you are always using UTC for the time zone logic.
Instant instantTrucToDay = Instant.now().truncatedTo( ChronoUnit.DAYS ) ;
More likely, you will want to truncate within the context a some time zone other than UTC as discussed above.
ZonedDateTime zdtTruncToDay = zdt.truncatedTo( ChronoUnit.DAYS ) ;
By the way, if your ultimate goal is to work with an entire date, a date-only without any time-of-day or zone, just stick to using LocalDate
.
if UTC time is 30 seconds from epoch. And if that can be represented/converted to Thu Jan 1 00:00:30 EST 1970
Not sure what you meant here. But keep in mind that given 30 seconds after the epoch of 1970-01-01T00:00:00Z (1970-01-01T00:00:30Z), the same moment in the east coast of North America is several hours earlier. That means around 7 PM the previous day.
Instant instantThirtySecsAfterEpoch = Instant.EPOCH.plusSeconds( 30 ) ;
instantThirtySecsAfterEpoch.toString(): 1970-01-01T00:00:30Z
ZoneId z = ZoneId.of( "America/New_York" ) ;
ZonedDateTime zdt = instantThirtySecsAfterEpoch.atZone( z ) ;
zdt.toString(): 1969-12-31T19:00:30-05:00[America/New_York]
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date
, Calendar
, & SimpleDateFormat
.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
With a JDBC driver complying with JDBC 4.2 or later, you may exchange java.time objects directly with your database. No need for strings or java.sql.* classes.
Where to obtain the java.time classes?
The ThreeTen-Extra project 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.