This question already has answers here:
Unix epoch time to Java Date object
(7 answers)
convert millisecond to Joda Date Time or for zone 0000
(3 answers)
Closed 4 years ago.
I have Long value 1282680754000 where if I check this value in https://www.epochconverter.com/ it gives me Tuesday, August 24, 2010 8:12:34 PM
But if I use new DateTime(1282680754000).toDate() I get Wed Aug 25 01:42:34 IST 2010 (It is adding +5.30 hour)
How to get Tuesday, August 24, 2010 8:12:34 PM for 1282680754000 in java
Just use
Instant.ofEpochMilli(1_282_680_754_000L)
or
Instant.ofEpochMilli(1_282_680_754_000L).atOffset(ZoneOffset.UTC)
(using java.time, the modern Java date and time API; you may consider it the successor of Joda-Time).
The latter will give you an OffsetDateTime, which you can then format into youe desired format.
What went wrong in your code?
Your code is correct. You got the correct Date. The only things are:
For most purposes you shouldn’t want a java.util.Date. That class is long outdated and has design problems, which was the major background for development of Joda-Time and later java.time.
Your Date was printed in your local time (IST, probably India Standard Time or Asia/Kolkata) where you expected UTC. A Date has got neither time zone nor offset in it. When you print it, its toString method grabs your JVM’s time zone setting and renders the time in this time zone — in your case in IST. This behaviour surprises many.
Link: All about java.util.Date on Jon Skeet’s coding blog
String result = DateTimeFormatter.ofPattern("EEEE, MMMM dd, yyyy h:mm:ss a")
.withZone(ZoneId.of("UTC"))
.toFormat()
.format(Instant.ofEpochMilli(1282680754000L));
System.out.println(result); // Tuesday, August 24, 2010 8:12:34 PM
Omit the toDate, you are converting it to a Date object, which its toString method uses your operating system default time zone to print its value (you can see the IST in your question).
Just for reference examine the follows:
public static void main(String[] args) {
DateTime dateTime = new DateTime(1282680754000L, DateTimeZone.forID("GMT"));
System.out.println(dateTime.toDate().toGMTString());
}
24 Aug 2010 20:12:34 GMT
against (my default time zone is IDT):
public static void main(String[] args) {
DateTime dateTime = new DateTime(1282680754000L,DateTimeZone.forID("GMT"));
System.out.println(dateTime.toDate());
}
Tue Aug 24 23:12:34 IDT 2010
Related
I had the below issue During daylight change CST-CDT reset.
Am getting the Input from Was8.5 server 2018-03-11-05.00 (UTC-5) as expected, but when it comes to WAS7 server, the below method returns Sun Mar 10 00.00.00 CST 2018 instead of Sun Mar 11 00.00.00 CDT 2018
/*
* Converts XMLGregorianCalendar to java.util.Date
*/
public static Date toDate(XMLGregorianCalendar calendar){
if(calendar == null) {
return null;
}
return calendar.toGregorianCalendar().getTime();
}
I know the server date/timezone reset didn’t take place properly, but in case if I want to get right Time when CST to CDT change or vise versa. How can I rewrite the code to convert XMLGregorianCalendar to java.util.Date in Java?
Something like,
If incoming request was CST(UTC-6), the toDate(XMLGregorianCalendar calendar) returns CDT (UTC-5). then I want toDate() should return CST (UTC-6).
the same way,
If incoming request was CDT(UTC-5), the toDate(XMLGregorianCalendar calendar) returns CST(UTC-6). then i want toDate() should return CDT(UTC-5).
java.util.Date doesn't have a timezone. It just have a long value that represents the number of milliseconds since unix epoch.
What you see (Sun Mar 10 00.00.00 CST 2018) is the result of toString() method, and it uses the JVM default timezone to convert the long value to a date and time in that timezone. See this article for more details:
https://codeblog.jonskeet.uk/2017/04/23/all-about-java-util-date/
Anyway, one way to really know what's happening is to check this long value:
long millis = calendar.toGregorianCalendar().getTimeInMillis();
And then you can print this value in UTC:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss XXX");
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
System.out.println(sdf.format(new Date(millis)));
Or, if you use Java 8:
System.out.println(Instant.ofEpochMilli(millis));
This will tell you the UTC instant that the Date corresponds to, so you can debug your code a little better than relying on Date::toString() method, which is confusing and misleading.
Regarding your main issue, I've tried to reproduce (I'm using Java 8 because it's easier to manipulate than using Date). First I created a date/time corresponding to 2018-03-11 in UTC-05:00, and I assumed the time to be midnight:
// March 11th 2018, midnight, UTC-05:00
OffsetDateTime odt = OffsetDateTime.parse("2018-03-11T00:00-05:00");
Then I converted this to America/Chicago timezone, which is a zone that uses CST/CDT:
// get the same instant in Central Time
ZonedDateTime zdt = odt.atZoneSameInstant(ZoneId.of("America/Chicago"));
Then I printed this:
// print the date/time with timezone abbreviation
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm xxx z", Locale.US);
System.out.println(zdt.format(fmt)); // 2018-03-10 23:00 -06:00 CST
Note that the result is 2018-03-10 23:00 -06:00 CST: March 10th in UTC-06:00.
That's because in 2018, Daylight Saving Time starts only at 2 AM of March 11th. At midnight, DST has not started yet, so the offset is still UTC-06:00.
Anyway, your conversion code is correct, because Date just represents a point in time (a count of elapsed time since epoch) and doesn't have timezone attached to it. Perhaps the problem lies somewhere, and checking the millis value might help you to understand what's going on (my guess is that XmlGregorianCalendar sets the time to midnight when it's not present, which would explain the result of Sun Mar 10 00.00.00 CST 2018).
If that helps, the exact UTC instant where DST transition occurs (March 11th 2018 at 2 AM in UTC-06:00) corresponds to the millis value 1520755200000. If your dates in March 2018 have a value lower than that, it means they're before DST starts, and they'll be in CST.
My first suggestion is that you don’t need what you are asking for. As I see it, you’ve got a date and a UTC offset, and I don’t really see that the offset adds any useful information. Just take the date. I believe what has happened was that a point in time after the transition to summer time on March 11 was stripped of the time-of-day, but the UTC offset was kept for whatever reason or maybe for no reason at all. When giving the time at start of day (00:00), the offset disagrees with your time zone of America/Chicago (or Central Time Zone, but the ID in region/city format is unambiguous and recommended).
And don’t use java.util.Date for your date. That class is long outdated. Today we have so much better in java.time, the modern Java date and time API. Furthermore its LocalDate class is better suited for a date without time-of-day because this is exactly what it is, while a Date is really a point a in time, that is, a whole different story. Depending on taste conversion from XMLGregorianCalendar can happen in two ways.
The direct way
return LocalDate.of(calendar.getYear(), calendar.getMonth(), calendar.getDay());
With your XMLGregorianCalendar of 2018-03-11-05:00 the result is a LocalDate of 2018-03-11.
The indirect way via GregorianCalendar and ZonedDateTime:
return calendar.toGregorianCalendar().toZonedDateTime().toLocalDate();
The result is the same. The advantage of the latter is you don’t need to concern yourself with the individual fields of year, month and day-of-month. Among other things this means you don’t risk putting them in the wrong order.
If you do insist on keeping the time zone or UTC offset, at least take the offset. Sun Mar 11 00.00.00 CDT 2018 doesn’t make sense because March 11 at 00:00 hours DST was not yet in effect (it began at 02:00). Such a non-existing time will just confuse everyone. Convert your calendar object to OffsetDateTime:
return calendar.toGregorianCalendar().toZonedDateTime().toOffsetDateTime();
Result: 2018-03-11T00:00-05:00. This point in time exists.:-)
Since your calendar comes from a foreign system, you will probably want to validate it since any field may be undefined and return DatatypeConstants.FIELD_UNDEFINED. When using LocalDate.of(), you may decide that its argument validation is enough since it will object to DatatypeConstants.FIELD_UNDEFINED being passed as an argument. toGregorianCalendar() on the other hand will tacitly use default values, so when using it I would consider validation indispensable.
What went wrong in your code?
I ran your code, and similarly to iolus (see the other answer) I got Sat Mar 10 23:00:00 CST 2018. This the correct point in time. As iolus also explained, this is Date.toString rendering the point in time this way. The Date object itself doesn’t have a time zone or UTC offset in it. So I should say that your code was correct. It was just you getting confused by the toString method. Many have been before you, and the good solution is to avoid the Date class completely. Also I would think that your observations have nothing to do with any difference between WAS 7 and WAS 8.5.
I am planning to convert ZonedDateTime to instant as per the below logic.
Say, I am in PST timeZone and the current time is 11A.M. If I convert now( no daylight saving as of today March 04 2018) and the toInstant will be 7P.M.
For the same 11 A.M, the toInstant will return 6 P.M as of April 04 2018 as daylight saving will be observed.
So, The below code returns correctly.
ZonedDateTime dateTime = ZonedDateTime.now(); --->>> March 04th 2018 at 11 A.M PST
dateTime.plusMonths(1).toInstant(); -->> returns April 04th 2018 at 6 PM PST as daylight saving will be observed
But,
The result will be different if i convert to Instant and then add a month.
Instant dateTime = ZonedDateTime.now().toInstant(); --->>> March 04th 2018 at 7 P.M UTC
dateTime.plus(1,ChronoUnit.MONTHS).toInstant(); -->> returns April 04th 2018 at 7 PM UTC ( but the actual time should be 6 PM UTC ).
This is ok, as we already converted to UTC and it just adds from there.
Hence, To include the daylight saving time, I need to add days or months or years .... to ZonedDateTime and then convert to Instant.
ZonedDateTime dateTime = ZonedDateTime.now(); ---> March 04th 2018 at 11A.M
dateTime.plusDays(10).toInstant(); ---> March 14th 2018 at 6P.M
dateTime.plusMonths(1).toInstant(); ---> April 04th 2018 at 6P.M
The above code works as expected. But the below one is not returning 6P.M, But it returns 7P.M.
dateTime.plusSeconds(org.joda.time.Period.days(1).multipliedBy(10).toStandardSeconds().getSeconds())
.toInstant()) --> ---> March 14th 2018 at 7P.M
Not sure, what is wrong with this and how to make it work with seconds.
The cause is found in the documentation for the ZonedDateTime class. For the method plusDays we see this in the method documentation:
This operates on the local time-line, adding days to the local date-time. This is then converted back to a ZonedDateTime, using the zone ID to obtain the offset.
When converting back to ZonedDateTime, if the local date-time is in an overlap, then the offset will be retained if possible, otherwise the earlier offset will be used. If in a gap, the local date-time will be adjusted forward by the length of the gap.
However, in the documentation for the plusSeconds method we see this:
This operates on the instant time-line, such that adding one second will always be a duration of one second later. This may cause the local date-time to change by an amount other than one second. Note that this is a different approach to that used by days, months and years.
So the two methods are designed to behave differently, and you need to consider this when choosing which method to use to suit your purpose.
As I understand your requirement you have a number of minutes or hours to add to your time, for example
long minutesToAdd = Duration.ofDays(10).toMinutes();
I am using java.time since I haven’t got experience with Joda-Time. Maybe you can translate my idea to Joda-Time if you wish.
As I further understand, the result of adding the above minutes should not be a point in time that number of minutes later. Instead it should work the same as adding 10 days. So if it’s 9 PM in California, you want 9 PM in California 10 days later. I suggest you solve this by converting to LocalDateTime before adding the minutes or hours, and then convert back to ZonedDateTime afterwards.
ZonedDateTime now = ZonedDateTime.now(ZoneId.of("America/Los_Angeles"));
System.out.println(now);
System.out.println(now.toInstant());
Instant inTenDays = now.toLocalDateTime()
.plusMinutes(minutesToAdd)
.atZone(now.getZone())
.toInstant();
System.out.println(inTenDays);
This just printed
2018-03-04T21:16:19.187690-08:00[America/Los_Angeles]
2018-03-05T05:16:19.187690Z
2018-03-15T04:16:19.187690Z
Since summer time (DST) is in effect on March 15 (it is from March 11 this year), you don’t get the same hour-of-day in UTC, but instead the same hour-of-day in your time zone.
Is it possible to create a java.util.Date object that is guaranteed to be UTC and at a specific time in the past, like an hour ago or a day ago, and is this a good idea?
I have reviewed the Stack Overflow question
get date as of 4 hours ago and its answers. But I want to avoid having to add more dependencies, like jodaTime, and the accepted answer uses System.currentTimeMillis() which would be the local timezone, would it not?
As discussed vividly in the comments, the recommendation is to use the java.time package. The easy solution:
Instant fourHoursAgo = Instant.now().minus(Duration.ofHours(4));
System.out.println(fourHoursAgo);
This just printed
2018-01-31T14:57:44.667255Z
Since UTC time is now 18:58, the output is what you asked for. The Instant itself is offset neutral. Its toString delivers time in UTC, but there was no mention of UTC in producing the Instant, so whether it gives you what you want, I am not sure. I will give you a result that is explicitly in UTC later.
But first, if you do need a java.util.Date, typically for a legacy API that you cannot change, the conversion is easy:
Date oldfashionedDate = Date.from(fourHoursAgo);
System.out.println(oldfashionedDate);
On my computer in Europe/Copenhagen time zone this printed:
Wed Jan 31 15:57:44 CET 2018
Again, this agrees with the time four hours before running the snippet. And again, a Date doesn’t have a UTC offset in it. Only its toString method grabs my JVM’s time zone setting and uses it for generating the string, this does not affect the Date. See the Stack Overflow question, How to set time zone of a java.util.Date?, and its answers.
As promised, if you do need to represent not only the time but also the offset, use an OffsetDateTime:
OffsetDateTime fourHoursAgoInUtc = OffsetDateTime.now(ZoneOffset.UTC).minusHours(4);
System.out.println(fourHoursAgoInUtc);
This printed
2018-01-31T14:57:44.724147Z
Z at the end means offset zero from UTC or “Zulu time zone” (which isn’t a true time zone). The conversion to a Date is not much more complicated than before, but again, you will lose the offset information in the conversion:
Date oldfashionedDate = Date.from(fourHoursAgoInUtc.toInstant());
System.out.println(oldfashionedDate);
This printed:
Wed Jan 31 15:57:44 CET 2018
Link: Oracle tutorial explaining how to use java.time
You can achieve this using the java.time package, as follows:
LocalDateTime localDateTime = LocalDateTime.now(ZoneOffset.UTC).minusHours(4);
Date date = Date.from(localDateTime.atZone(ZoneOffset.UTC).toInstant());
Gives the following output:
2018-01-31T14:58:28.908
Wed Jan 31 20:28:28 IST 2018 //20:28:28 IST is 14:58:28 UTC
Which is correctly 4+5:30 hours behind my current time - Asia/Kolkata ZoneId.
Calendar c = Calendar.getInstance();
System.out.println(c.getTime());
c.set(2007, 0, 1);
System.out.println(c.getTime());
Output:
Tue Sep 12 12:36:24 IST 2017
Mon Jan 01 12:36:24 IST 2007
But, When I use the same code in a different environment, Output changes to below:
Output:
Tue Sep 12 12:36:24 IST 2017
Mon Jan 01 12:36:24 GMT 2007
FYI, I tried to print the timezone of the calendar instance, before and after setting the values and both are in "IST".
I want to know the root cause of this.
The second output in your question is the correct and expected behaviour on a JVM running Irish time (Europe/Dublin). On September 12, 2017 Ireland is on summer time (DST). While it is not clearly documented, Date.toString() (which you invoke implicitly when printing the Date you get from c.getTime()) prints the date and time in the JVM’s time zone, which in September is rendered as IST for Irish Summer Time.
When you set the date on the Calendar object also using Irish time, the hour of day is preserved; in your case you get Jan 01 2007 12:36:24 Irish standard time. Now imagine the confusion if both Irish Summer Time and Irish Standard Time were rendered as IST. You would not be able to distinguish. Instead, since Irish standard time coincides with GMT, this is what Date.toString() prints when the date is not in the summer time part of the year (which January isn’t).
My guess is that your first output is from a JVM running India time. It too is rendered as IST, and since India doesn’t use summer time, the same abbreviation is given summer and winter.
java.time
Before understanding the explanation for the behaviour you observed, I posted a comment about the outdated and the modern Java date and time classes. I still don’t think the comment is way off, though. This is the modern equivalent of your code:
ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("Europe/Dublin"));
System.out.println(zdt);
zdt = zdt.with(LocalDate.of(2007, Month.JANUARY, 1));
System.out.println(zdt);
It prints
2017-09-12T11:45:33.921+01:00[Europe/Dublin]
2007-01-01T11:45:33.921Z[Europe/Dublin]
If you want to use the JVM’s time zone setting, use ZoneId.systemDefault() instead of ZoneId.of("Europe/Dublin"). As the name states, contrary to Date, ZonedDateTime does include a time zone. It corresponds more to the old Calendar class. As you can see, its toString method prints the offset from UTC (Z meaning zero offset) and the time zone name in the unambiguous region/city format. I believe that this leaves a lot less room for confusion. If you want to print the date in a specific format, use a DateTimeFormatter.
Appendix: sample output from your code
For the sake of completeness, here are the outputs from your code when running different time zones that may be rendered as IST:
Europe/Dublin (agrees with your second output)
Tue Sep 12 11:19:28 IST 2017
Mon Jan 01 11:19:28 GMT 2007
Asia/Tel_Aviv
Tue Sep 12 13:19:28 IDT 2017
Mon Jan 01 13:19:28 IST 2007
Asia/Kolkata (agrees with your first output)
Tue Sep 12 15:49:28 IST 2017
Mon Jan 01 15:49:28 IST 2007
You need to set time zone and you will get desired result.
TimeZone.setDefault(TimeZone.getTimeZone("IST"));
Here is a working code.
import java.util.Calendar;
import java.util.TimeZone;
public class Cal {
public static void main(String[] args) {
// TODO Auto-generated method stub
TimeZone.setDefault(TimeZone.getTimeZone("IST")); // Add this before print
Calendar c = Calendar.getInstance();
System.out.println(c.getTime());
c.set(2007, 0, 1);
System.out.println(c.getTime());
}
}
As per Doc "Typically, you get a TimeZone using getDefault which creates a TimeZone based on the time zone where the program is running. For example, for a program running in Japan, getDefault creates a TimeZone object based on Japanese Standard Time."
SO when you running in different timezone it is using as default timezone. Hope you clear now. I attach doc. please read.
To talk about this interesting behaviour:
The source code from the Calendar class:
public final void set(int year, int month, int date)
{
set(YEAR, year);
set(MONTH, month);
set(DATE, date);
}
Which leads to the set method:
public void set(int field, int value)
{
// If the fields are partially normalized, calculate all the
// fields before changing any fields.
if (areFieldsSet && !areAllFieldsSet) {
computeFields();
}
internalSet(field, value);
isTimeSet = false;
areFieldsSet = false;
isSet[field] = true;
stamp[field] = nextStamp++;
if (nextStamp == Integer.MAX_VALUE) {
adjustStamp();
}
}
The interesting part here is the computeFields() method which has two implementation (one for Gregorian and one for Japenese calendar). These methods are quite complex but as far as I can see this is the only place where your Calendar instance may change time zone in your usecase.
Our server is running in netherlands but users uses the application in UK .
For UK 2017-03-26 02:30:00 is valid date-time but not in Netherlands.
I am using the code to convert the time by setting the timezone . But its not giving me right output.
String toDate ="2017-03-26 02:30:00";//Valid Time in UK
Date date = new Date();
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// Use London's time zone to format the date in
df.setTimeZone(TimeZone.getTimeZone("London"));
System.out.println("Date and time in London: " + df.parse(toDate));
Output from Program : Date and time in London: Sun Mar 26 04:30:00 CEST 2017
Output Required : Date and time in London: Sun Mar 26 02:30:00 CEST 2017
Java version used : 1.7 . Can not use joda time for some dependency.
First a detail, TimeZone.getTimeZone() is dangerous, if it doesn’t recognize the ID string, it will tacitly give you GMT. Exactly in London this is so close to the correct you may get fooled for a while. TimeZone.getTimeZone("London") gives you UTC (or GMT). As Stefan Freitag pointed out, it has to be TimeZone.getTimeZone("Europe/London") for the correct British time zone.
Next a very common misunderstanding: A Date object does not have a time zone in it. It’s a point in time only. So how come it is printed as Sun Mar 26 04:30:00 CEST 2017, where CEST obviously refers to a time zone (Central European Summer Time, used in the Netherlands)? When printing the date, you are implicitly invoking Date.toString(). This methods unconditionally prints the time according the default time zone for the JVM. Therefore, setting the time zone for the DateFormat you used for parsing has no effect here. And therefore changing the JVM’s time zone setting, as Hugo did in a comment, works. The other way of getting correct output for British users is to format the Date back into a string using a DateFormat with British time zone.
If you use Hugo’s trick in the beginning, before creating the DateFormat, it too will have the British time zone, and you need not call df.setTimeZone() (but you may if you think it makes the code clearer, of course).
Look forward to using Java 8 or later some day. The new date and time classes in java.time generally don’t come with the surprises experienced with the old ones.