I was trying to get milliseconds from epoch until 2020.01.01. I used old method with Date and I also wanted to use new sexy LocalDate but two results I got are different:
long millisecondsInTheDay = 24 * 60 * 60 * 1000;
long millis1 = LocalDate.of(2020, Month.JANUARY, 1).toEpochDay() * millisecondsInTheDay; // 1577836800000
long millis2 = new Date(2020 - 1900, 0, 1).toInstant().toEpochMilli(); // 1577833200000
Difference is exactly one hour (3600_000 milliseconds). Why I get different result?
I don't want to comment on why you get a difference because I think that both of the original approaches are problematic. You need to pay close attention to things like time zones; and you really should avoid doing any sort of arithmetic on numerical values representing dates.
You need to pay special care to specify the points you are measuring between: if you want a number of milliseconds, presumably you really want to specify those points as instants in time. "1970" isn't an instant, it's a year-long period; "2020-01-01" isn't an instant either, but a period whose meaning shifts depending on time zone - there's roughly 48h-worth of instants where somewhere on the planet it is considered to be that date.
The correct way to do this (assuming you want milliseconds between epoch and the start of the day in your preferred timezone) is:
Duration between =
Duration.between(
Instant.EPOCH,
LocalDate.of(2020, Month.JANUARY, 1).atStartOfDay(zoneId));
long betweenMillis = between.toMillis(); // If you must - better to keep the type information that this is a Duration.
Note that you need to specify the zoneId, e.g. ZoneId.of("Europe/Warsaw"), because that affects when the start of the day is, and hence how many milliseconds.
Different time zones
Why I get different result?
Joachim Sauer said it already: This is because of different time zones. millis1 is the count of milliseconds until January 1, 2020 at 00:00 in UTC. millis2 counts until January 1, 2020 at 00:00 in your local time zone, presumably Europe/Warsaw. In winter Poland is at offset +01:00 from UTC, which explains the difference of 1 hour between the two. Everything agrees nicely. The epoch is one point in time and independent of time zone. It’s usually defined as January 1, 1970 at 00:00 in UTC.
That said I agree with Andy Turner that both ways to calculate are problematic.
A good calculation with java.time
Here’s my go, of course using java.time, the modern Java date and time API:
ZoneId targetZone = ZoneOffset.UTC;
long millis = LocalDate.of(2020, Month.JANUARY, 1).atStartOfDay(targetZone)
.toInstant()
.toEpochMilli();
System.out.println(millis);
Output:
1577836800000
If you did want your own time zone, just change the first line:
ZoneId targetZone = ZoneId.of("Europe/Warsaw");
1577833200000
The key is to use the same timezone (e.g. UTC) for both, the legacy and the modern API.
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.Month;
import java.time.ZoneOffset;
import java.util.Locale;
import java.util.TimeZone;
public class Main {
public static void main(String[] args) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd", Locale.ENGLISH);
sdf.setTimeZone(TimeZone.getTimeZone("Etc/UTC"));
long millisUsingJavaUtilDate = sdf.parse("2020.01.01")
.getTime();
long millisUsingJavaTime = LocalDate.of(2020, Month.JANUARY, 1)
.atStartOfDay(ZoneOffset.UTC)
.toInstant()
.toEpochMilli();
System.out.println(millisUsingJavaUtilDate);
System.out.println(millisUsingJavaTime);
}
}
Output:
1577836800000
1577836800000
Let's try with another timezone, America/New_York:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.Month;
import java.time.ZoneId;
import java.util.Locale;
import java.util.TimeZone;
public class Main {
public static void main(String[] args) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd", Locale.ENGLISH);
sdf.setTimeZone(TimeZone.getTimeZone("America/New_York"));
long millisUsingJavaUtilDate = sdf.parse("2020.01.01")
.getTime();
long millisUsingJavaTime = LocalDate.of(2020, Month.JANUARY, 1)
.atStartOfDay(ZoneId.of("America/New_York"))
.toInstant()
.toEpochMilli();
System.out.println(millisUsingJavaUtilDate);
System.out.println(millisUsingJavaTime);
}
}
Output:
1577854800000
1577854800000
Learn more about the modern date-time API from Trail: Date Time.
Note that the legacy date-time API (java.util date-time types and their formatting API, SimpleDateFormat) are outdated and error-prone. It is recommended to stop using them completely and switch to java.time, the modern date-time API* .
* For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.
new Date( y , m , d ) uses default time zone
Some of the other Answers are correct and very useful. But I want to make very plain and simple where your code went wrong:
➥ The deprecated constructor of java.util.Date for year-month-day arguments implicitly applies your JVM’s current default time zone.
Take the first part of the key line in your code:
new Date(2020 - 1900, 0, 1).toInstant()
… where an Instant is always in UTC (an offset of zero hours-minutes-seconds), by definition. On my machine the current default time zone in my JVM is America/Los_Angeles. On your date and time, this zone was eight hours behind UTC.
So let's try these three lines of code code:
System.out.println(
ZoneId.systemDefault()
);
System.out.println(
new Date(2020 - 1900, 0, 1)
);
System.out.println(
new Date(2020 - 1900, 0, 1).toInstant()
);
When run, we see indeed that the moment represented by new Date is the first moment of that day as seen in the time zone America/Los_Angeles, colloquially known as PST. That zone on that date is eight hours behind UTC. We can see this fact in the third line, when calling toInstant has adjusted to UTC where the time-of-day is 8 AM.
America/Los_Angeles
Wed Jan 01 00:00:00 PST 2020
2020-01-01T08:00:00Z
Avoid Date
In the bigger picture, stop using Date class!
There are no benefits to be had by studying the behavior of java.util.Date. That class is absolutely terrible, written by people who did not understand date-time handling. Along with Calendar, java.sql.Date, and SimpleDateFormat, these classes should never be used.
These legacy classes were supplanted years ago by the java.time classes, defined in JSR 310. Sun, Oracle, and the JCP community unanimously gave up on these classes. So should you.
Your problem is here:
long millis1 = LocalDate.of(2020, Month.JANUARY, 1).toEpochDay() * millisecondsInTheDay; // 1577836800000
You use the LocalDate class, which gets for you the local time (in your timezone) while time in Java (in millisec) is the amount of time elapsed between 01.01.1970 UTC (Universal Coordinated Time) this is (at the date you requested, 01.01.2020 00:00:00 UTC):
1577836800000
The difference you get is due to the time offset observed at your local time (one hour, probably you are in central european time --CET--)
Edit:
By the way, I've seen in the answers (and in your code) that you use:
new Date(2020 - 1900, 0, 1);
This is very bad code. You are assuming that the above is equivalent to the difference in milliseconds that will be between the dates 2020.1.1 and 1900.1.1 and indeed, it represents the timestamp at date 120.1.1 this is the timestamp at the first of january of year one hundred and twenty (a.C) There's no distributive property between dates in new Date() operator. And if the years were all the same duration, this could be true... but they are not. A good way would be to use:
long millis = new Date(2020, 0, 1).getTime() - new Date(1900, 0, 1).getTime();
but the later is not equivalent to what is written above.
Related
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));
This question already has answers here:
How to check if a date Object equals yesterday?
(9 answers)
Closed 8 years ago.
I tried to convert the new today's date object to UTC. But no luck since new date object is always in local time zone. I have been trying to verify two dates using 'before' method. date1 is in UTC format. date2 is today.
The below code always prints the today's date object in local time zone.
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
public class test {
/**
* #param args
*/
public static void main(String[] args) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
String todayStr = sdf.format(new Date());// This string is in UTC. But I need date object in UTC.
try {
System.out.println(sdf.parse(todayStr));// This is still printing in local time zone.
} catch (ParseException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
A Date object isn't in any time zone - it's just a wrapper around the number of milliseconds since the Unix epoch. The Unix epoch is typically described in terms of UTC (namely midnight at the start of January 1st 1970) but it's really just a point in time.
To get the point in time 48 hours before "now", you can use something like:
Date earlier = new Date(System.currentTimeMillis() - TimeUnit.HOURS.toMillis(48));
You can compare that with any Date object to see which occurred earlier in time - again, with no reference to any particular time zone.
(This isn't an easily testable solution, admittedly - I prefer to abstract System.currentTimeMillis() behind a Clock interface, so that I can test with a fake clock set to whatever time I want.)
Unless you need a string representation, ignore it - as soon as you want a string representation, you need to think about both which calendar system and time zone you're interested in.
Note that you talk about "today's date" in the question - if you mean the start of a particular day, then the time zone is relevant, and you'll need to do more work.
Finally, I'd suggest that if you possibly can, you use either java.time.* from Java 8, or Joda Time instead of java.util.Date etc. The first two are both much better APIs than the last.
The below code always prints the today's date object in local time zone.
Yes, that's because Date.toString() uses the local time zone. That's just what it (unfortunately) does, and it doesn't mean that the Date object is "in" the local time zone.
See the correct answer by Jon Skeet for details.
Yesterday and Day Before
Your requirement is unclear, but it seems you want to test if the target date-time string represents a date-time that lies within the 48-hour period before the beginning of today as defined by UTC time zone. In other words, yesterday or day before.
Avoid java.util.Date
Avoid using the java.util.Date and .Calendar classes bundled with Java. They are notoriously troublesome. Instead use either Joda-Time or the new java.time in Java 8 (inspired by Joda-Time). Both libraries have a date-time class that knows its own assigned time zone, unlike java.util.Date.
Half-Open Span Of Time
And both libraries have classes to represent a span of time. Comparisons are done by the "Half-Open" approach where the beginning is inclusive and the ending exclusive. So your two-day period is defined as the first moment of day-before-yesterday (inclusive) and running up to, but not including, the first moment of today (exclusive).
ISO 8601
Your string format is defined by the ISO 8601 standard. Both Joda-Time and java.time use ISO 8601 for defaults in parsing and generating strings. So they have a built-in formatter to parse your string. Merely pass the string.
Immutable Objects
Joda-Time uses immutable objects. Rather than modify ("mutate") an existing object, a new object is instantiated with values based on the original. The reason is for thread-safety.
Joda-Time
Here is some example code in Joda-Time 2.4.
DateTime target = new DateTime( "2014-01-02T03:04:05.789Z", DateTimeZone.UTC );
DateTime nowUtc = DateTime.now( DateTimeZone.UTC );
DateTime today = nowUtc.withTimeAtStartOfDay();
DateTime dayBeforeYesterday = today.minusDays( 2 ).withTimeAtStartOfDay();
Interval interval = new Interval( dayBeforeYesterday, today ); // Half-Open.
boolean hit = interval.contains( target );
I am able to convert date to days using the below code.
SimpleDateFormat sfd = new SimpleDateFormat("yyyy-MM-dd");
String s1 = sfd.format(dateObj);
String a1 [] = s1.split("-");
int year = Integer.parseInt(a1[0].toString());
int month = Integer.parseInt(a1[1])-1;
int day = Integer.parseInt((a1[2]));
Calendar c1 = Calendar.getInstance();
c1.set(year,month,day);
days = c1.getTime().getTime()/(24*60*60*1000);
The above code works accurately in my system which is windows with timezone GMT +5.30.
However the same code in EST or Pacific timezone adds a day by 1 to final result when the time is 20.00 in the system.
What could be the issue ?
Do we need to set Timezone explicitly in the code ?
input dates does not hold any time stamp ..
is it correct to store in java.util.Date instead of java.sql.Date?
EDIT: As per Alex's comment, it's possible that the problems with the start of your code have blinded me to your real aim.
A Date represents an instant in time. That can fall on different dates depending on the time zone, but how do you want that to affect things? Do you want the number of days since the Unix epoch (which is always UTC) or the number of days since the 1st January 1970 in a particular time zone? Why do you want this "number of days" instead of a representation of a date such as LocalDate? What's the use case here?
EDIT: If you just want to know the number of days since the Unix epoch, you can skip most of this:
days = dateObj.getTime() / (24 * 60 * 60 * 1000);
You shouldn't be going through formatting at all just to get the year / month / day. Just create a Calendar, set the relevant time zone, call setTime with the dateObj you've already got, and then clear the hour/minute/second part of the calendar.
However, you should explicitly specify which time zone you want to consider - a Date represents an instant in time, which will mean different dates in different time zones.
You should also consider using Joda Time which makes all of this simpler and has a specific type for dates (LocalDate). That would also make it easy to find the number of days between the Unix epoch and a particular date without performing the division yourself.
java.time
The java.util Date-Time API and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern Date-Time API*.
Also, quoted below is a notice from the home page of Joda-Time:
Note that from Java SE 8 onwards, users are asked to migrate to java.time (JSR-310) - a core part of the JDK which replaces this project.
Solution using java.time, the modern Date-Time API:
You can convert the object of java.util.Date to Instant using Date#toInstant and then you can find the number of days from now until this date using ChronoUnit#between.
Demo:
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.GregorianCalendar;
public class Main {
public static void main(String[] args) {
// A sample java.util.Date
Date dateObj = GregorianCalendar.from(ZonedDateTime.of(2021, 10, 2, 22, 25, 0, 0, ZoneOffset.UTC)).getTime();
Instant instant = dateObj.toInstant();
// Difference between now and the given java.util.Date
System.out.println(ChronoUnit.DAYS.between(Instant.now(), instant));
}
}
Output:
99
ONLINE DEMO
Note that the above code calculates the number of days between two moments/instants represented in UTC. If you have date-time values local to a particular timezone, you need to specify the corresponding ZoneId.
Demo:
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.GregorianCalendar;
public class Main {
public static void main(String[] args) {
ZoneId tz = ZoneId.of("Australia/Brisbane");
// A sample java.util.Date representing the local date and time values in Australia/Brisbane
Date dateObj = GregorianCalendar.from(ZonedDateTime.of(2021, 10, 2, 22, 25, 0, 0, tz)).getTime();
// Difference between now in Australia/Brisbane and the given java.util.Date
System.out.println(ChronoUnit.DAYS.between(Instant.now().atZone(tz), dateObj.toInstant().atZone(tz)));
}
}
Output:
98
ONLINE DEMO
Learn more about the modern Date-Time API from Trail: Date Time.
* For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.
What time is the start of a day, say 01/01/2010?
Is it 00:00:00:000 ? or is that midnight?
[edit]
It might be a stupid question but I'm confused because I used Calendar.set(Calendar.HOUR, 0) but this gives me a time of 12:00:00.
and now I've realised I should be using HOUR_OF_DAY
The start of the day isn't always midnight. It can depend on the time zone and date. (If the clock moves forward an hour at the start of the day, it will start at 1am.)
That's why Joda-Time has things like LocalDate.toDateTimeAtStartOfDay - and they're well worth using.
But yes, normally it's at 00:00:00 which is midnight. (This can also be formatted as "12am" depending on your locale etc.)
java.time
Normally, the start of the date is 00:00 hours but it may vary because of DST. Therefore, instead of assuming it to be 00:00 hours, the safest option is to use LocalDate#atStartOfDay(ZoneId zone).
Demo:
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class Main {
public static void main(String[] args) {
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("M/d/u", Locale.ENGLISH);
LocalDate date = LocalDate.parse("01/01/2010", dtf);
// In JVM's timezone
ZonedDateTime startOfDay = date.atStartOfDay(ZoneId.systemDefault());
System.out.println(startOfDay);
// In custom timezone
startOfDay = date.atStartOfDay(ZoneId.of("Africa/Johannesburg"));
System.out.println(startOfDay);
}
}
Output:
2010-01-01T00:00Z[Europe/London]
2010-01-01T00:00+02:00[Africa/Johannesburg]
Learn more about the the modern date-time API* from Trail: Date Time.
* For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.
ZonedDateTime from java.time
Like Arvind Kumar Avinash already does in a good answer, I recommend that you use java.time, the modern Java date and time API, for your date and time work.
If you had got a LocalDate or a string holding a date without time of day, that answer shows you how to get the start of the day (the first moment of the day). If you had already got a ZonedDateTime, you may simply use its truncatedTo method. Let’s take one of those interesting examples where the clocks are turned forward at 00:00 so the first moment of the day is 01:00:
ZonedDateTime zdt = ZonedDateTime.of(
2000, 9, 17, 15, 45, 56, 789000000, ZoneId.of("Asia/Dili"));
System.out.println("We got date and time: " + zdt);
ZonedDateTime startOfDay = zdt.truncatedTo(ChronoUnit.DAYS);
System.out.println("Start of day is: " + startOfDay);
Output:
We got date and time: 2000-09-17T15:45:56.789+09:00[Asia/Dili]
Start of day is: 2000-09-17T01:00+09:00[Asia/Dili]
What went wrong in your code?
You’ve already said it in an edit to the question, but it deserves to be mentioned in an answer too: Calendar.HOUR refers to, from the documentation:
Field number for get and set indicating the hour of the morning or
afternoon. HOUR is used for the 12-hour clock (0 - 11). …
So if your Calendar was already holding a time in the afternoon (12 noon or later), setting HOUR to 0 gives you 12 noon (12:00 on a 24 hour clock), not 12 midnight (00:00 on a 24 hour clock). Except that the time of the hour may still be non-zero, so you may also get, for example, 12:34:45.567. The Calendar class was cumbersome to work with.
In any case the Calendar class was poorly designed and is long outdated, so you shouldn’t need to worry; just don’t use that class anymore.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Documentation of Calendar.HOUR.
I need today's date - and zero anything else (" 05/06/08 00:00:00 ")
I've tried
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.HOUR, 0);
Date date1 = calendar.getTime();
System.out.println(date1);
Run: (This is seriously messed up)
If the hour on the computer is < 12:00 at noon : Sun Mar 08 00:44:39 IST 2009
If the hour on the computer is > 12:00 at noon : Sun Mar 08 12:46:53 IST 2009
So I gave this up.
All the Date's setters are deprecated (except the epoch time) - so I don't want to use them either
The only thing I could think of is
Calendar calendar = Calendar.getInstance();
SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
String sDate = dateFormat.format(calendar.getTime());
Date today = dateFormat.parse(sDate);
But this is such a lame code I can't bring myself to write it.
Any other option?
Thanks!
I use this:
public static Date startOfDay(Date date) {
Calendar dCal = Calendar.getInstance();
dCal.setTime(date);
dCal.set(Calendar.HOUR_OF_DAY, 0);
dCal.set(Calendar.MINUTE, 0);
dCal.set(Calendar.SECOND, 0);
dCal.set(Calendar.MILLISECOND, 0);
return dCal.getTime();
}
My standard advice for Java date/time questions: don't use java.util.{Calendar,Date}. Use Joda Time. That way you can represent a date as a date (with no associated time zone), instead of a date/time. Or you could use a DateMidnight if that's what you want to represent. (Be careful of combinations of time zone and date where there is no midnight though...)
What do you need to use the Date with? If you can get away with changing to use Joda throughout, that's great. Otherwise, you can use Joda to do what you want and then convert to milliseconds (and then to java.util.Date) when you really need to.
(Michael's solution when using Date/Calendar is fine if you really want to stick within a broken API... but I can't overstate how much better Joda is...)
You should use HOUR_OF_DAY instead of HOUR and combine it with MINUTE and SECOND also.
import java.util.Calendar;
import static java.util.Calendar.HOUR_OF_DAY;
import static java.util.Calendar.MINUTE;
import static java.util.Calendar.SECOND;
import static java.util.Calendar.MILLISECOND;
public class Today {
public static void main( String [] args ) {
Calendar cal = Calendar.getInstance();
cal.set( HOUR_OF_DAY, 0 );
cal.set( MINUTE, 0 );
cal.set( SECOND, 0 );
cal.set( MILLISECOND, 0 );
System.out.println( cal.getTime() );
}
}
The results you are getting are due to HOUR is used to AM/PM while HOUR_OF_DAY is 24 hrs.
HOUR_OF_DAY:
Field number for get and set indicating the hour of the day. HOUR_OF_DAY is used for the 24-hour clock. E.g., at 10:04:15.250 PM the HOUR_OF_DAY is 22.
HOUR:
Field number for get and set indicating the hour of the morning or afternoon. HOUR is used for the 12-hour clock (0 - 11). Noon and midnight are represented by 0, not by 12. E.g., at 10:04:15.250 PM the HOUR is 10.
The time component is not just hours (and Calendar.HOUR is, as you have noticed, AM/PM).
You need to set all of the time fields to 0: HOUR_OF_DAY, MINUTE, SECOND, MILLISECOND.
See Apache's commons-lang DateUtils.truncate()
java.time
The java.util Date-Time API and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern Date-Time API*.
Also, quoted below is a notice from the home page of Joda-Time:
Note that from Java SE 8 onwards, users are asked to migrate to java.time (JSR-310) - a core part of the JDK which replaces this project.
Solution using java.time, the modern Date-Time API:
The modern Date-Time API has many types which truly represent a date or time or date-time in a specific timezone. You can choose from the following options as per your specific requirement:
If you are looking for a type that represents a date without a timezone, you can use LocalDate.now. The good news is that its variant, LocalDate#now(ZoneId) returns the current date from the system clock in the specified time-zone.
If you are looking for an object that represents a date without a timezone, and with time units set to zero, you can call LocalDate#atStartOfDay on the object obtained with Option#1.
If you are looking for an Instant representing the Date-Time object obtained with Option#2, you can attach this object with ZoneId.of("Etc/UTC") using LocalDateTime#atZone to obtain a ZonedDateTime and convert the same into an Instant using ZonedDateTime#toInstant.
Demo:
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
public class Main {
public static void main(String[] args) {
LocalDate todayInSystemTz = LocalDate.now();
System.out.println(todayInSystemTz);
LocalDate todayInIndia = LocalDate.now(ZoneId.of("Asia/Kolkata"));
System.out.println(todayInIndia);
LocalDateTime todayInSystemTzWithZeroTimeUnits = todayInSystemTz.atStartOfDay();
System.out.println(todayInSystemTzWithZeroTimeUnits);
ZonedDateTime todayInUtcWithZeroTimeUnits = todayInSystemTzWithZeroTimeUnits.atZone(ZoneId.of("Etc/UTC"));
System.out.println(todayInUtcWithZeroTimeUnits);
Instant instant = todayInUtcWithZeroTimeUnits.toInstant();
System.out.println(instant);
// Can I represent the obtained Instant in India?
System.out.println(instant.atZone(ZoneId.of("Asia/Kolkata")));
// Can I represent the obtained Instant in New York?
System.out.println(instant.atZone(ZoneId.of("America/New_York")));
}
}
Output:
2021-06-20
2021-06-20
2021-06-20T00:00
2021-06-20T00:00Z[Etc/UTC]
2021-06-20T00:00:00Z
2021-06-20T05:30+05:30[Asia/Kolkata]
2021-06-19T20:00-04:00[America/New_York]
ONLINE DEMO
The Z in the output is the timezone designator for zero-timezone offset. It stands for Zulu and specifies the Etc/UTC timezone (which has the timezone offset of +00:00 hours).
For any reason, if you need to convert this object of Instant to an object of java.util.Date**, you can do so as follows:
Date date = Date.from(instant);
Learn more about the modern Date-Time API from Trail: Date Time.
* For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.
**
A java.util.Date object simply represents the number of milliseconds since the standard base time known as "the epoch", namely January 1, 1970, 00:00:00 GMT (or UTC). Since it does not hold any timezone information, its toString function applies the JVM's timezone to return a String in the format, EEE MMM dd HH:mm:ss zzz yyyy, derived from this milliseconds value. To get the String representation of the java.util.Date object in a different format and timezone, you need to use SimpleDateFormat with the desired format and the applicable timezone e.g.
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX", Locale.ENGLISH);
sdf.setTimeZone(TimeZone.getTimeZone("America/New_York"));
String strDateNewYork = sdf.format(date);
sdf.setTimeZone(TimeZone.getTimeZone("Etc/UTC"));
String strDateUtc = sdf.format(date);
As mentioned above you should use
Calendar.HOUR_OF_DAY
As opposed to
Calendar.HOUR
Also you need to clear out the other fields (Calendar.MINUTE, Calendar.SECOND, and Calendar.MILLISECOND) by setting them to zero.
Sorry there's no easy way here. A pain, and that's why they're working on a new API for Java 7 I believe based on Joda Time.
...or you can do it the hacker way:
long MS_PER_DAY = 86400000L;
Date dateTime=new Date();
long offset = TimeZone.getDefault().getOffset(dateTime.getTime());
Date date= new Date(((dateTime.getTime()+offset)/MS_PER_DAY)*MS_PER_DAY-offset);
I know this is a very old question, no longer active, but it came to be on the top when I searched Google.
While all advise is very good, I can't believe no one simply answered:
Date date = new Date(System.currentTimeMillis());
System.out.println(date);
Which returns effectively, today's date.
Why the string manipulation?
Can you not just set the values you need on the Calendar object before converting to a Date using getTime()?
Another vote for JodaTime.
java.util.Date and Calendar are so bad they are broken. (And SimpleDateFormat is rubbish too!)
For what it's worth, Java 7 will include a new date time library based strongly around JodaTime.