Given the following code
public static void main(String[] args) {
org.joda.time.format.DateTimeFormatter _timestampFomatNYCJoda = org.joda.time.format.DateTimeFormat.forPattern("yyyyMMdd HHmmss.SSS").withZone(DateTimeZone.forID("America/New_York"));
DateTimeFormatter _timestampFomatNYC = DateTimeFormatter.ofPattern("yyyyMMdd HHmmss.SSS").withZone(ZoneId.of("America/New_York"));
LocalDateTime localDateTime = LocalDateTime.now();
org.joda.time.LocalDateTime jodaLocalDateTime = new org.joda.time.LocalDateTime();
System.out.println("System Time " + new Date());
System.out.println("Java Version " + localDateTime.format(_timestampFomatNYC));
System.out.println("Joda Version " + _timestampFomatNYCJoda.print(jodaLocalDateTime.toDateTime(DateTimeZone.UTC)));
}
Why does the Java Version and Joda Version dont match ? I am running this on IST clock.
Below is the output
System Time Fri Mar 27 17:01:33 IST 2020
Java Version 20200327 170133.933
Joda Version 20200327 130133.938
I can reproduce your results. I can also explain them. Joda-Time and java.time have been designed to behave differently in this case. Let’s look at them in turn.
Joda-Time
In Joda-Time DateTimeFormatter.withZone() gives you a formatter with an override zone, that is, a zone that will always be used for formatting dates and times. In other words, any date and time will be converted to this zone for printing. The documentation says:
When printing, this zone will be used in preference to the zone from
the datetime that would otherwise be used.
When you do new org.joda.time.LocalDateTime(), you are getting a LocalDateTime representing the current date and time in your default time zone. The Local in some class names means without time zone or offset from UTC. I figure that you must have got a value equal to 2020-03-27T17:01:33.938.
Apparently what happens when you format a LocalDateTime with a formatter with an override zone, is that the formatter assumes that your LocalDateTime is in UTC (which yours isn’t) and converts it from there, in your case to America/New_York time zone. Since summer time (DST) is in effect in New York, the offset is -04:00, so 17:01 becomes 13:01.
This is the wrong result. When the time is 17:01 in your time zone, it is not 17:01 UTC, so the conversion is based on a false premise. It is also not 13:01 in New York, so the converted result is telling a lie.
java.time
With java.time setting an override zone on a formatter works similarly for formatting, but with a difference that matters here: the override zone is only used when printing a date-time object that identifies an instant (a point in time). From the docs:
When formatting, if the temporal object contains an instant, then it
will be converted to a zoned date-time using the override zone.
Whether the temporal is an instant is determined by querying the
INSTANT_SECONDS field. If the input has a chronology then it will be
retained unless overridden. If the input does not have a chronology,
such as Instant, then the ISO chronology will be used.
… In all other cases, the override zone is added to the temporal,
replacing any previous zone, but without changing the date/time.
Again LocalDateTime.now() gives you the current date and time of day (a few milliseconds earlier than the query through Joda-Time), 2020-03-27T17:01:33.933. Local still means without offset or time zone.
Because your LocalDateTIme hasn’t got offset or time zone, it cannot identify an unambigous point in time, an instant. Therefore when formatting it neither the date nor the time of day is changed. And since your format pattern contains no time zone or offset, none is printed. So you just get the date and time in your time zone (not in New York), 20200327 170133.933.
To get the date and time in New York time zone
DateTimeFormatter timestampFormat
= DateTimeFormatter.ofPattern("yyyyMMdd HHmmss.SSS");
ZonedDateTime timeInNy = ZonedDateTime.now(ZoneId.of("America/New_York"));
System.out.println(timeInNy.format(timestampFormat));
When I ran this code just now, the output was:
20200327 122359.683
Documentation links
Joda-Time DateTimeFormatter.withZone()
java.time DateTimeFormatter.withZone()
How to format java.util.Date with DateTimeFormatter portable?
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.
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
I wrote the following program, proving, that Date DOES NOT only contain correct "instant":
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".
The number 10799000 is 3*60*60*1000-1000 - the timezone offset of my local time.
This means, that Date class is dual. It's millisecond part may be shifted relatively to hh mm ss part by timezone offset.
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.
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.
UPDATE 3
Event funnier:
Date date = new Date(70, 0, 1, 0, 0, 1);
assertEquals(1000, date.getTime());
this test fails.
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!
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.
If you 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:
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.
Date doesn't have any kind of "dual contract" that lets you view it as a time-zone-less representation. It is just 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.)
Implicitly using the system default time zone is not the 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:
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.
The problem is that 2017-03-26T01:30 didn't exist in the Europe/London time zone due to DST - at 1am, the clock skipped forward to 2am.
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.
If you absolutely have to 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 is just an instant in time like Instant, but at least you won't lose data.
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...
tl;dr
How to format java.util.Date with DateTimeFormatter portable?
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 Skeet is correct. Here is my own take, with some specific points.
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.time classes.
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.
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.
LocalDateTime != moment
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 sense in nearly any practical scenario.
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 “any locality” or “no locality”, not any one particular locality.
Lacking zone/offset means a LocalDateTime does not represent a moment. It is not a point on the timeline. It is a vague idea about potential moments, 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.
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.
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
ZonedDateTime = moment
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.
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]
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.
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.
ZonedDateTime zdtMontreal = zdtKiribati.withZoneSameInstant( ZoneId.of( "America/Montreal") );
zdtMontreal.toString(): 2018-12-24T05:00-05:00[America/Montreal]
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.
Epoch
Regarding your experiments with the epoch reference of 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.
Per the documentation for 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.
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”.
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.
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.
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
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.
Instant instant = Instant.EPOCH.plus( Duration.ofSeconds( 1 ) ) ;
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 = 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.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 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
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.time classes instead. Stop banging your head against a brick wall, and then your headache will go away.
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.
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.
Using a JDBC driver compliant with JDBC 4.2 or later, you may exchange java.time objects directly with your database. No need for strings nor java.sql.* classes.
Where to obtain the java.time classes?
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 6 and 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 ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
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.
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));
Following is the code that I have used to get the 00 hour of the current day (in long format).
I am running the below code in android.
The method returns the value properly most of the time. But once in a while it returns the value of System.currentTimeMillis().
import java.text.SimpleDateFormat;
import java.util.Date;
public static final SimpleDateFormat SD_FORMAT_DAY_MONTH_YEAR = new SimpleDateFormat("dd/MM/yyyy");
public static long getLongForCurrent00hr() {
Date date = new Date();
String time = SD_FORMAT_DAY_MONTH_YEAR.format(date);
long value;
try {
Date date2 = SD_FORMAT_DAY_MONTH_YEAR .parse(time);
value = date2.getTime();
} catch (ParseException e) {
value = 0;
}
return value;
}
Why is it returning the System.currentTimeMillis()?
How can I solve the issue?
I am more interested in knowing WHY..
As I was ruuning this code today, I checked it by putting Logs:
Most of the time it returns: 1462386600000
And few times System.currentTimeMillis() like 1462430867302.
Your example code works
I see no problem with your code, as you frame it (read below for criticism).
Nearly your exact code is shown here. Two changes:
I made your format constant a local variable. (simply to make this demo easier, one block of code that can be copy-pasted)
I added a couple calls to get an Instant, the current moment in UTC. Similar to java.util.Date, but Instant::toString creates a string showing UTC rather than confusingly applying the JVM’s current time zone. So you can more clearly see that you are indeed getting the first moment of the day of your JVM’s current default time zone. In my case when running this code, my JVM’s current default time zone is America/Los_Angeles, currently in Daylight Saving Time (DST) for an offset-from-UTC for -07:00 (seven hours behind UTC).
Example code.
Date date1 = new Date ();
SimpleDateFormat SD_FORMAT_DAY_MONTH_YEAR = new SimpleDateFormat ( "dd/MM/yyyy" );
String time = SD_FORMAT_DAY_MONTH_YEAR.format ( date1 );
Date date2 = null;
long value;
try {
date2 = SD_FORMAT_DAY_MONTH_YEAR.parse ( time );
value = date2.getTime ();
} catch ( ParseException e ) {
value = 0;
}
System.out.println ( "date1: " + date1 + " date2: " + date2 + " value: " + value + " | instant 1: " + date1.toInstant () + " | instant 2: " + date2.toInstant () );
When run.
date1: Thu May 05 16:55:40 PDT 2016 date2: Thu May 05 00:00:00 PDT 2016 value: 1462431600000 | instant 1: 2016-05-05T23:55:40.907Z | instant 2: 2016-05-05T07:00:00Z
Working too hard
Your Question is confusing, but it seems that you are trying to capture the first moment of the day. You are going about it the wrong way, and are working too hard.
Time Zone
Your code appears to be working with the java.util.Date class. That class represents a moment on the timeline in UTC.
But you are not getting the first moment of the day in UTC. When you parse that date-only string to generate a new java.util.Date (a date plus time-of-day value, despite the misleading name), your JVM’s current default time zone is applied implicitly. Very confusing to have time zones invisibly injected into the process.
Instead you should consciously consider time zones, and always make the time zone explicit is your coding (as seen below).
java.time
The old java.util.Date/.Calendar classes have proven to be poorly designed, confusing, and troublesome. They are now legacy, supplanted by the java.time framework built into Java 8 and later. Much of the java.time functionality is back-ported to Java 6 & 7 and further adapted for Android.
For a date-only value without time-of-day and without time zone, use LocalDate class. While not storing a time zone, determining a date such as “today” requires a time zone. If omitted, your JVM’s current default time zone is applied (beware, that default can change at any moment during runtime).
ZoneId zoneId = ZoneId.of( "America/Montreal" );
LocalDate today = LocalDate.now( zoneId );
You seem to want the first moment of the day. Do not assume the time of that moment is 00:00:00.0. While often true, in some time zones an anomaly such as Daylight Saving Time may shift to another time. Let java.time determine the correct time. Calling [atStartOfDay][2] generates a ZonedDateTime for the first moment appropriate to the specified time zone.
ZonedDateTime zdt = today.atStartOfDay( zoneId );
I strongly recommend against using handling date-time values as a count-from-epoch. That is like using an array of ints of Unicode code points rather than using the String-related classes for handling text. But if you insist, you can convert. But beware data loss as the java.time classes have a finer resolution of nanoseconds whereas you are asking for milliseconds (one of many reasons to avoid handling date-time as a count-from-epoch). First extract an Instant, a moment on the timeline in UTC with a resolution of nanoseconds.
Instant instant = zdt.toInstant();
long millisecondsFromEpoch = instant.toEpochMilli(); // WARNING: Possible data loss (going from nanoseconds to milliseconds).
UTC
If you did want the first moment of the day in UTC, that too is easy.
You could specify UTC as the time zone, using the constant ZoneOffset.UTC. (That constant happens to be in ZoneOffset, a subclass of ZoneId.)
ZonedDateTime zdt = today.atStartOfDay( ZoneOffset.UTC );
But that may not be the most appropriate route. A time zone is an offset-from-UTC plus a set of rules for anomalies such as Daylight Saving Time (DST). UTC has no such anomalies by definition. So more appropriate would be the OffsetDateTime rather than ZonedDateTime.
OffsetTime ot = OffsetTime.of( 0 , 0 , 0 , 0 , ZoneOffset.UTC );
OffsetDateTime odt = today.atTime( ot );
I have a Date field in my class that can has two types of values: with and without time. Something like this: 2015-01-01 and 2015-01-01 12:00:00. I want to make formatted string from my date. I know I can use SimpleDateFormat class for doing this, but I don't know the format. In fact, If my date has the time part, I must use yyyy-MM-dd HH:mm:ss format and if my date does not have the time part, I must use yyyy-MM-dd format. My question is, Is there anyway to check a date has time section before formatting it?
Here is my code:
private SimpleDateFormat dateTimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.ENGLISH);
private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH);
.....
private String formatDate(Date date){
//I need to something like this:
if(/* `date` has time part */){
return dateTimeFormat.format(date);
}
else{
return dateFormat.format(date);
}
}
You cannot reliably do that, because once you create a Date object, it is represented as a number in milliseconds, which includes the specific time. For this reason you cannot possibly know how the object was built and if the specific time was set.
A workaround would be to check if the hours, minutes and seconds are set to zero. Keep in mind that there is a small probability that the date was parsed as "yyyy-MM-dd HH:mm:ss", but all these values were set to 0, simply because the time was indeed 00:00:00. However, this probability is equal to 1 / (24 * 60 * 60) = 0.00001157407, so I assume that you can live with that.
A Big Mess
As shown in the other answers, the old date-time classes bundled with Java such as java.util.Date and java.sql.Date are a mess. They were rushed to market, with bad design decisions. Specifically a java.util.Date represents both a date and a time-of-day, while its subclass java.sql.Date pretends not to have a time-of-day but actually does. The doc explains that you are supposed to ignore this inheritance relationship to help maintain the illusion. Not good.
java.time
This whole mess has been supplanted by the java.time framework built into Java 8 and later. The new classes are inspired by the highly successful Joda-Time framework, intended as its successor, similar in concept but re-architected. Defined by JSR 310. Extended by the ThreeTen-Extra project. See the Tutorial.
Date-Only
Among the new classes is LocalDate. This is the first class bundled with Java for representing a date only, without time-of-day nor time zone. To determine a date such as "today", you need a time zone (a ZoneId). For example, a new day dawns earlier in Paris than in Montréal. When you need a date-only value, I suggest you add a member to your class of this type.
LocalDate today = LocalDate.now( ZoneId.of( "America/Montreal" ) );
Date-Time
If you want a date-time, first consider the Instant class. This marks a moment on the timeline in UTC. Almost always best to do your business logic and data storage in UTC. When you need a specific moment in time rather than a vague date, add a member of this type to your class.
Instant now = Instant.now();
For presentation to the user in their desired/expected time zone, apply a time zone to an Instant to get a ZonedDateTime.
ZonedDateTime zdt = ZonedDateTime.ofInstant( now , ZoneId.of( "America/Montreal" ) );
First Moment Of The Day
I do not recommend this strategy, but to directly answer your Question about detecting if the time-of-day in a date-time value happens to be the first moment of the day…
First you need to think about time zone. All of these date-time classes mentioned above track time by a count-from-epoch in UTC. The old classes count in milliseconds, the new in nanoseconds. The epoch for both old and new is the first moment of 1970 in UTC. So there is no such thing as a date-time without a time, as you pose it in the Question. The closest thing to that is a date-time whose time-of-day happens to be the first moment of the day. Seems to be your situation (though my discussion above strongly recommends you change your situation).
How to determine if a date-time has a time-of-day that is the first moment of the day? First you must consider time zone. Either you want UTC or you want a particular time zone such as America/Montreal. Depends on your business rules.
If starting with a java.util.Date, first convert to java.time.
Instant instant = myJUDate.toInstant();
Be aware that a date does not always start at the time 00:00:00.0. Because of Daylight Saving Time (DST), and possibly other anomalies, in some places the first moment of the date is a different wall-clock time. The java.time framework can determine this first moment of the day by using the LocalDate class and its atStartOfDay methods.
So after determining the time zone we care about, we adjust our Instant into a ZonedTimeZone.
Instant instant = Instant.now ();
ZoneId zoneId = ZoneId.of ( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant ( instant , zoneId );
Next we need to see if that is first moment of the day. So we convert to a LocalDate, then back to another ZonedDateTime by calling atStartOfDay. Comparing the first ZonedDateTime to the second tells us if the original was indeed at the start of the day. To sum it up: We are converting from ZonedDateTime → LocalDate → ZonedDateTime.
// Convert to LocalDate, to get start of day, to compare to above.
LocalDate localDate = zdt.toLocalDate ();
ZonedDateTime startOfDay = localDate.atStartOfDay ( zoneId );
Boolean isStartOfDay = ( zdt.isEqual ( startOfDay ) );
Dump to console.
System.out.println ( "instant: " + instant + " for zoneId: " + zoneId + " is zdt: " + zdt + " if compared to startOfDay: " + startOfDay + " is T/F: " + isStartOfDay );
instant: 2015-12-12T23:20:23.560Z for zoneId: America/Montreal is zdt: 2015-12-12T18:20:23.560-05:00[America/Montreal] if compared to startOfDay: 2015-12-12T00:00-05:00[America/Montreal] is T/F: false
If you want UTC rather than a particular time zone, in the code above use the constant ZoneOffset.UTC as your ZoneId object. ZoneOffset is a subclass of ZoneId.
Assuming you're using java.sql.Date which derives from java.util.Date there is no possibility of a Date object not having a time value.
Note the documentation:
http://docs.oracle.com/javase/7/docs/api/java/sql/Date.html
A Date object instance holds a miliseconds value, to be precise the difference, measured in milliseconds, between the current time and midnight, January 1, 1970 UTC.
Use a Calendar object. The calendar can give you structured access to all fields of a Date value, i.e. year, month, day, hours, minutes, seconds, etc. This would allow you to check whether the time fields are non-zero. As JB Nizet stated, the time part can happen to be zero, in which case wou would misinterpret it as a date only value.
I have the following date value 1995-12-31T23:59:59
but in order to parse this for a solr query I need it in the below format
1995-12-31T23:59:59Z
How can I parse this to get the added "Z" on the end in java 1.6 ?
The type must be java.util.date after the conversion - fyi
When I toString the date now and attempt to parse it with the SimpleDateFormat object it looks like this
"Mon Jan 01 00:00:00 CST 2001" - what is this format to convert it?
Use SimpleDateFormat:
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
Date d = df.parse("1995-12-31T23:59:59Z");
System.out.println(d);
Put the 'Z' in single quotes to escape
"Z" is the time zone abbreviation for Zulu time zone i.e. UTC. If solr API accepts the date object, then you can just parse the date in the following way by setting preferred timezone:
SimpleDateFormat dateParser = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
dateParser.setTimeZone(TimeZone.getTimeZone("Z"));
Date date = df.parse("1995-12-31T23:59:59");
If you need to convert it back to string then use the method provided by nsfyn55:
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
System.out.println(dateFormatter.format());
Avoid Old Date-Time Classes
You are using the old java.util.Date/.Calendar and SimpleDateFormat classes. Avoid them.
The Date class has the poor design choice of its toString applying a default time zone when generating a String. So it seems like it has a time zone when in fact it does not (except one buried underneath that is ignored for regular use). Confusing, yes. Avoid it.
java.time
Instead use java.time built into Java 8 and later.
First parse as a LocalDateTime without any time zone or offset.
LocalDateTime ldt = LocalDateTime.parse( "1995-12-31T23:59:59Z" );
Apply a time zone or offset-from-UTC to give this LocalDateTime meaning, to make it an actual moment on the timeline. You have to know, or ask, what time zone or offset was intended by this string as no indication was embedded. For this example, I will arbitrarily assume Québec.
ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ldt.atZone( zoneId );
Your desired output has a Z on the end, for Zulu which means UTC.
In java.time an Instant represents a moment on the timeline in UTC. You can extract an Instant from the ZonedDateTime.
Instant instant = zdt.toInstant();
The Instant class’ toString method generates a string in your desired format. That format is one of the standard ISO 8601 formats.
String output = instant.toString();
Half-Open
I happened to notice that your example value was trying to get the end of 1995. There is a better way to do such search or comparison criteria.
In date-time work, the best practice is called Half-Open where the beginning of a span of time is inclusive while the ending is exclusive. So a week starts on Monday and runs up to, but not including, the next Monday.
Defining a year means starting at the first moment of the first day of 1995 and running up to but not including the first moment of the first day of the following year, 1996. Searching for any values within that range is done not with a BETWEEN but as: ( someEvent >= firstMomentOf1995 AND someEvent < firstMomentOf1996 ) ( not <= ).