I'm parsing dates from a Dataset[Transaction] objects in order to collect a set of dictinct values:
val distinctTransactionDates: Set[Date] = transactions.map(t => t.transaction_date).distinct().collect().toSet
But the dates are parsed incorrectly, for example if a transaction's Date is 2019-03-31, the returned value is 2019-04-01. When I logged to check t.transaction_date.getTime it is 1553990400000 (GMT: Sunday, 31 Mar 2019, 0:00:00). But the gap for some dates vs getTime is more than one day.
The Date here is a java.sql.Date
I can't figure out how to parse dates correctly in this case in order to get distinct values without any corrections. For the above example I'm expecting to get 2019-03-31.
You are trying to extract a date-only value from a date-with-time-of-day source, but failing to account for time zone.
Another problem: You are using the terrible java.sql.Date class that was supplanted years ago by the modern java.time classes. Specifically, LocalDate.
Your date-with-time-of-day source can be referred to as a moment, a specific point on the timeline. For any given moment the time-of-day and the date both vary by time zone around the globe. Noon in Paris is not noon in Montréal. And a new day dawns earlier in the east than in the west. You must get very clear on this to do proper date-time handling. One moment in nature can be viewed in many ways through human-created notions of time zones.
First extract your moment via JDBC as an OffsetDateTime object.
Code shown here is in Java syntax rather than Scala. Also, note that java.time uses immutable objects.
OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;
Adjust into the time zone by which you want to perceive a date.
ZoneId z = ZoneId( "Asia/Tokyo" ) ;
ZonedDateTime zdt = odt.withZoneSameInstant( z ) ;
Extract the date-only portion.
LocalDate ld = zdt.toLocalDate() ;
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));
I am working on a system where each user can specify a setting of their adjustment from UTC in hours.
So when a date/time is input from this user, the value is adjusted by their setting and saved as UTC. Similarly, on the way out, the date/time is retrieved from the database, adjusted per their setting and displayed.
I might be thinking about this too much, but does this mean to show the correct date/time for each person, I have to effectively adjust the hours and tell the instance of my SimpleDateFormat that this is "UTC"? Right now I am in the UK where the current time zone is UTC+1 and if I don't specify to print in UTC then the time is off by one hour!
DateTime dateWithOffset = statusDate.plusMinutes(currentTimezoneOffsetInMinutes);
SimpleDateFormat sdf = new SimpleDateFormat("dd MMM yyyy HH:mm");
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
return sdf.format(dateTime.toDate());
Am I thinking about this correctly? Or is there an easier way to print a date in a format given that I just want an hours adjustment from UTC?
You are working too hard. Never do manual adjustments for offsets and time zones, never be adding or subtracting minutes to a date-time value for that purpose. Let a decent date-time library do that work.
java.time
The Joda-Time team advises us to migrate to the java.time framework built into Java 8 and later.
The ZoneOffset class represents an offset-from-UTC. Keep in mind that in some areas an offset may involve not only a number of hours but also minutes and even seconds.
The OffsetDateTime class represents a a moment in the timeline with an assigned offset.
int hours = 3; // input by user
ZoneOffset offset = ZoneOffset.ofHours( hours );
OffsetDateTime odt = OffsetDateTime.now( offset );
The standard ISO 8601 formats are used by the toString methods in java.time.
String output = odt.toString();
Generally the best practice is to do your business logic and data storage in UTC. Convert to/from an offset or zoned value only for interaction with user.
In java.time a moment on the timeline in UTC is represented by the Instant class. You can extract an Instant object from the OffsetDateTime.
Instant instant = odt.toString();
Both this Instant and this OffsetDateTime represent the same simultaneous moment on the timeline. They present different wall-clock times.
It may be more clear to skip the use of the OffsetDateTime.now convenience method and start with Instant.
Instant instant = Instant.now(); // Always in UTC, by definition.
ZoneOffset offset = ZoneOffset.ofHours( hours );
OffsetDateTime odt = OffsetDateTime.ofInstant( instant , offset ); // Same moment but presenting alternate wall-clock time.
Handling input
If the user is inputting date-time values as strings, we need to parse. In java.time that means the DateTimeFormatter class. The formatting codes are similar to the outmoded java.text.SimpleDateFormat but not exactly identical, so be sure to study the doc.
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "dd MMM uuuu HH:mm";
Since the offset-from-UTC is given separately, we parse this input string as a LocalDateTime devoid of time zone information.
LocalDateTime ldt = LocalDateTime.parse( inputString , formatter );
To view, create a String object formatted in ISO 8601 format by calling ldt.toString().
2016-01-02T12:34:45
Apply the pre-determined ZoneOffset object to yield a OffsetDateTime object.
OffsetDateTime odt = ldt.atOffset( offset );
2016-01-12T12:34:45+03:00
Think in UTC
Handling date-time values is a headache. UTC is your aspirin.
When a programmer arrives at the office, she should take off her “UK citizen / London resident” hat, and put on her “UTC” hat. Forget all about your own local time zone. Learn to think in UTC (and 24-hour clock). Add another clock to your desk or computer, set to UTC (or Reykjavík Iceland), or at least bookmark a page like time.is/UTC. Do all your logging, business logic, data serialization, data-exchange, and debugging in UTC.
Make Instant your first-thought, your go-to class. It's value is always in UTC by definition.
Instant instant = Instant.now();
Look at the Instant extracted from the OffsetDateTime value we saw above whose String representation was 2016-01-12T12:34:45+03:00. Being in UTC means 9 AM rather than noon, same moment but three hours difference in wall-clock time. The Z is short for Zulu and means UTC.
String output = odt.toInstant().toString();
2016-01-12T09:34:45Z
Adjust into an offset or time zone only as needed, when expected by a user or data sink. FYI, a time zone is an offset-from-UTC plus a set of rules for handling anomalies such as Daylight Saving Time (DST). Use a time zone in preference to a mere offset wherever possible.
The Europe/London time zone is the same as UTC in the summer, but in winter uses Daylight Saving Time nonsense, and is one hour ahead of UTC. So using the same Instant seen just above, the London wall-clock time is 10 AM rather than 9 AM in UTC, and different from the noon we saw with an offset of +03:00.
ZoneId zoneId = ZoneId.of( "Europe/London" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );
2016-01-12T10:34:45+01:00[Europe/London]
Always specify the desired/required offset or time zone; never rely on the implicit current default by omitting this optional argument. (Ditto for Locale by the way.) Note how in all the code of this answer the fact that your JVM has a current default time zone (ZoneId.systemDefault) of Europe/London and the fact that my JVM has a current default time zone of America/Los_Angeles is completely irrelevant. The code runs the same, gets the same results, regardless of whatever machine you use to develop, test, and deploy.
Locale
Specify a Locale object when generating a textual representation of a date-time value that involves a name of month or day, commas or periods and so on. The Locale determines (a) the human language to use when translating such names, and (b) the cultural norms to follow in deciding issues such as punctuation marks.
The Locale has nothing to do with time zones and offset-from-UTC. For example, you could use Locale.CANADA_FRENCH with a date-time zoned for Asia/Kolkata if you had a Québécois user in India.
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 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 <= ).