I want to calculate the time difference between two dates (in the format "yyyyMMddHHmmss"). The basic idea is to first convert the string date into milliseconds and then get the time difference.
Calendar c1 = Calendar.getInstance();
c1.setTime(new SimpleDateFormat("yyyyMMddHHmmss").parse("20110327032913"));
System.out.println(c1.getTimeInMillis());
Calendar c2 = Calendar.getInstance();
c2.setTime(new SimpleDateFormat("yyyyMMddHHmmss").parse("20110327025913"));
System.out.println(c2.getTimeInMillis());
Result:
1301189353000
1301191153000
Obviously, the first date is later than the second one, but its converted millisecond is smaller. Did I make any error on format?
The time difference between the two timestamps in ms is 30 minutes:
1301191153000 - 1301189353000 = 1800000ms = 30 min
Because of the DST changes on 27 march, the clock is being set forward at 2 AM from 2 AM to 3 AM, hence the timestamps:
20110327032913 => 2011-03-27 03:29:13
20110327025913 => 2011-03-27 02:59:13
are in fact interpreted as:
2011-03-27 03:29:13
2011-03-27 03:59:13 (+1 hour from original time)
Thus, the second timestamp comes later and when converted to ms it is bigger than the first one.
I bet you are in a locale where the time was changed from 2 o'clock (am) to 3 o'clock (am) for daylight saving time on March 27, 2011 (See this link)
Your first time is
2011 03 27 03 29 13
yyyy MM dd HH mm ss
and your second time is
2011 03 27 02 59 13 // does not exist because of time change
yyyy MM dd HH mm ss
so actually, your second time is:
,,
2011 03 27 03 59 13
yyyy MM dd HH mm ss
which is obviously 30 minutes later than your first one (and not 30 minutes earlier).
Perhaps this code can help you:
Calendar c1 = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
Calendar c2 = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
c1.setTime(sdf.parse("20110327032913"));
System.out.println(c1.getTimeInMillis());
c2.setTime(sdf.parse("20110327025913"));
System.out.println(c2.getTimeInMillis());
System.out.println((c1.getTimeInMillis()-c2.getTimeInMillis())/(1000*60)+ " minutes");
*The problem is that if your String is not in UTC you may never know if the time change was applied therefore an interval could be one hour off.
Related
Edit: It turns out the problem is not about Jackson, but about time adjustment in Thailand on 1 April 1920.
How does com.fasterxml.jackson.databind.ObjectMapper works? I thought it used Unix timestamp.
I tried converting a java.util.Date with mapper.writeValueAsString().
When I convert the string back to Date with mapper.readerFor(Date.class).readValue(), the result is correct.
However, when I trying removing the last 3 digits and and put the same string into some converter websites, the result is off for some minutes and seconds.
Please see the code below.
Date wayBack = new SimpleDateFormat("yyyy-MM-dd").parse("1900-01-31");
System.out.println(wayBack); // Wed Jan 31 00:00:00 ICT 1900
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.writeValueAsString(wayBack)); // -2206420924000
Date deserialised = mapper.readerFor(Date.class).readValue(mapper.writeValueAsString(wayBack));
System.out.println(deserialised); // Wed Jan 31 00:00:00 ICT 1900
Below is a screenshot from http://www.onlineconversion.com/unix_time.htm
Please note that 7-hour off is expected because of my timezone but I don't understand the 17:56 minutes different.
EDIT - Here is my attempt to provide a better answer than my first one.
Background
Before looking at the code in the question, some background notes:
The epoch value (in seconds) at midnight 31st Jan 1900 in Bangkok is -2206420924:
LocalDateTime localDateTime = LocalDateTime.parse("1900-01-31T00:00:00");
ZoneId z = ZoneId.of("Asia/Bangkok");
ZonedDateTime ict_1 = ZonedDateTime.of(localDateTime, z);
System.out.println("Epoch seconds: " + ict_1.toEpochSecond());
System.out.println("ICT datetime : " + ict_1);
The above prints this:
Epoch seconds: -2206420924
ICT datetime : 1900-01-31T00:00+06:42:04[Asia/Bangkok]
The epoch value (in seconds) for UTC midnight on the same date is -1570060800:
ZonedDateTime utcDateTime = ZonedDateTime.parse("1900-01-31T00:00:00Z");
System.out.println("Epoch seconds: " + utcDateTime.toEpochSecond());
System.out.println("UTC datetime : " + utcDateTime);
The above prints this:
Epoch seconds: -2206396800
UTC datetime : 1900-01-31T00:00Z
The time at midnight in Bangkok on 31st January 1900 was 24,124 seconds further into the past than the time at midnight in Greenwich, UK (the prime meridian - or UTC).
That is to say, on that date Bangkok was 6 hours, 42 minutes and 4 seconds ahead of UTC time (or GMT as I believe it was then called - as UTC had not been established at that time).
The Specific Code in the Question
First, I changed my default time zone to match the one used in the question:
System.setProperty("user.timezone", "Asia/Bangkok");
The below line from the question does the following:
(1) The SimpleDateFormat constructor, in which the date format string does not specify a locale, uses the default locale.
(2) Then the parse() method creates the Date object:
Date wayBack = new SimpleDateFormat("yyyy-MM-dd").parse("1900-01-31");
At this point we can check the date object:
System.out.println(wayBack);
System.out.println(wayBack.getTime());
This prints the following:
Wed Jan 31 00:00:00 ICT 1900
-2206420924000 // epoch milliseconds
This matches what we saw earlier, in the background section.
When you use an online tool such as the one mentioned in the question, you will see the above milliseconds value reported as the following GMT (UTC) datetime:
GMT: Tuesday, January 30, 1900 5:17:56 PM
For the above output I used this tool.
Again, this is what we expect - when it's midnight in Bangkok, it's still the afternoon of the day before in Greenwich, UK.
The remainder of the code (including the Jackson object mapper transformations) are all subject to this initial set-up of your Date object.
For the question: "How does com.fasterxml.jackson.databind.ObjectMapper works? I thought it used Unix timestamp." It shows the same behavior as the core Java date object. I believe your assumption is correct.
Regarding the Unusual Offset
Regarding the ICT offset of +06:42:04 shown above:
On April 1st 1920, an adjustment was made to the local ICT (Indochina Time), to align it with UTC time (with an offset of +7 hours, as you note). The local clocks were set forward by 17 minutes and 56 seconds, to round up the UTC (GMT) offset to 7 hours.
See this link for a specific reference to the 17 minutes & 56 seconds change.
This is why you will not see that unusual offset from April 1920 onwards.
Further Links
See this answer regarding the newer java.time classes which should be used instead of java.util.Date.
See this question and its answers for a related deep-dive into the topic of historic time zone adjustments.
The following code is giving me the parsed date as "Wed Jan 13 00:00:00 EST 2010"
instead of "Wed Jun 13 00:00:00 EST 2010". Any ideas much appreciated.
SimpleDateFormat sf = new SimpleDateFormat("yyyy-mm-dd'T'HH:mm:ss");
String str = "2010-06-13T00:00:00";
Date date = sf.parse(str);
System.out.println(" Date " + date.toString());
Try:
"yyyy-MM-dd'T'HH:mm:ss"
MM means month. mm means minutes. See the documentation for SimpleDateFormat for more details of the supported date and time patterns.
The problem is that you're using 'mm' as month and 'mm' represents minutes.
Below is all date formats available, read more doc here.
Symbol Meaning Kind Example
D day in year Number 189
E day of week Text E/EE/EEE:Tue, EEEE:Tuesday, EEEEE:T
F day of week in month Number 2 (2nd Wed in July)
G era designator Text AD
H hour in day (0-23) Number 0
K hour in am/pm (0-11) Number 0
L stand-alone month Text L:1 LL:01 LLL:Jan LLLL:January LLLLL:J
M month in year Text M:1 MM:01 MMM:Jan MMMM:January MMMMM:J
S fractional seconds Number 978
W week in month Number 2
Z time zone (RFC 822) Time Zone Z/ZZ/ZZZ:-0800 ZZZZ:GMT-08:00 ZZZZZ:-08:00
a am/pm marker Text PM
c stand-alone day of week Text c/cc/ccc:Tue, cccc:Tuesday, ccccc:T
d day in month Number 10
h hour in am/pm (1-12) Number 12
k hour in day (1-24) Number 24
m minute in hour Number 30
s second in minute Number 55
w week in year Number 27
G era designator Text AD
y year Number yy:10 y/yyy/yyyy:2010
z time zone Time Zone z/zz/zzz:PST zzzz:Pacific Standard
Modern answer:
String str = "2010-06-13T00:00:00";
LocalDateTime dateTime = LocalDateTime.parse(str);
System.out.println("Date-time " + dateTime);
Output:
Date-time 2010-06-13T00:00
I am using and recommending java.time, the modern Java date and time API. We don’t even need an explicit formatter for parsing. This is because your string is in ISO 8601 format, the international standard that the java.time classes parse as their default. java.time came out in 2014.
While in 2010 when this question was asked, SimpleDateFormat was what we had for parsing dates and times, that class is now considered long outdated, fortunately, because it was also troublesome.
In case your string contained only a date without time of day, use the LocalDate class in quite the same manner (this was asked in a duplicate question).
String dateStr = "2018-05-23";
LocalDate date2 = LocalDate.parse(dateStr);
System.out.println(date2);
2018-05-23
Link: Oracle tutorial: Date Time explaining how to use java.time.
Example if Date is 06 07 2016
SimpleDateFormat sdf= new SimpleDateFormat("dd MM yyyy");
you can use comma, full-stop, slash, or hyphen between these format.
Ex: dd-mm-yyyy, it will display like(06-07-2016)
dd/mm/yyyy,it will display like(06/07/2016)
dd.mm.yyyy,it will display like(06.07.2016)
dd,mm,yyyy ,it will display like(06,07,2016)
MM - will display number of the Month.
MMM - will display Month Three character only(Ex: Jul)
MMMM - will display full month(Ex: July)
yyyy - will display full year(2016)
yy - will display last two digits(16)
hh - will display hours
mm -will display minutes
ss - will display seconds
a - will display AM or PM
Ex: if time is 12:09:10 PM means (hh:mm:ss a)
EEE- will display short week name(Ex: Wed)
EEEE- will display full week name(Ex: Wednesday)
This question already has answers here:
How to convert "Mon Jun 18 00:00:00 IST 2012" to 18/06/2012?
(5 answers)
Closed 5 years ago.
I have a problem with date converting. I use the following program and I expect the output: 19.05.2017
But the output is: 05.00.2017
Can anybody help?
String t = "Fri May 19 00:00:00 CEST 2017";
Date d = new SimpleDateFormat("EEE MMM DD hh:mm:ss zzzz YYYY", Locale.US).parse(t);
String s = new SimpleDateFormat("dd.mm.yyyy").format(d).toString();
System.out.println(s);
A surprising result. The oldfashioned classes SimpleDateFormat and friends are full of surprises. This is meant as a negative thing.
Uppercase DD is day of year. Lowercase hh is hour of AM or PM (1 through 12). Uppercase YYYY is weekbased year (only useful with week number). So you are asking for a date that is a Friday in May and the 19th day of the year. Obviously this is not possible.
The result of parsing is Thu Jan 05 23:00:00 CET 2017. Apparently SimpleDateFormat opts for giving you a Friday and for using the zone offset of 2 hours implied by CEST even though the date it has chosen is not at the time of year where CEST (summer time) is in use. I don’t know whether it just gives you the first Friday of the weekbased year (Friday in week 1 of the year). Friday at 0000 hours at offset GMT+2 equals Thursday at 23 at GMT+1, which is CET.
Next for the formatting, 05 is the date as expected, but lowercase mm means minutes. Since the minutes are 0, you get 00. You got the right year.
Rather than using the outdated classes that give you such surprises, I agree with Sam’s answer that you should use the newer classes in java.time:
ZonedDateTime dt = ZonedDateTime.parse(t,
DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss zzz yyyy", Locale.US));
String s = dt.format(DateTimeFormatter.ofPattern("dd.MM.uuuu"));
This code gives you 19.05.2017 as you had expected. One of the good things about the modern classes is, if you try to parse with your original format pattern string, you will get a DateTimeParseException so you will know something is wrong. I certainly prefer an exception over incorrect output.
Another good thing is these classes respect the time zone in the input and use it in the output too (unless you explicitly instruct them otherwise). They will never turn Friday 6 January into Thursday 5 January because of some funny time zone issue.
Your input date is in Central European Summer Time and your date format is a bit wrong. Try
SimpleDateFormat input = new SimpleDateFormat("EEE MMM dd hh:mm:ss zzzz yyyy");
You might want to set the timezone on the output date format in order to get the date in the correct local time.
Ideally you'd move over to use a java.time style as shown here:
https://www.mkyong.com/java/java-convert-date-and-time-between-timezone/
Currently I have a AIX SERVER and if I issue the command date on the terminal, I get the response as Mon Nov 4 00:28:40 EST 2013.
And I have the seconds value 1383561560 which is the number of seconds since Wednesday, 31 December 1969, 19:00:00 (UTC time).
Now I have a code which computes the date and time since 31 December 1969, 19:00:00 (UTC time) to EST time.
DateFormat df = new SimpleDateFormat("MM/dd/yyyy_HH:mm:ss a");
df.setTimeZone(TimeZone.getTimeZone("EST"));
long ms = (long)(1383560920) *1000;
Date d1 = new Date(ms);
String formattedDate = df.format(d1);
System.out.println("Now the date/time is "+formattedDate );
Now the date/time displayed is 11/04/2013_05:28:40 AM
Here why is there a 5 hour difference?
Java Date base time start from 1 January 1970, 00:00:00 but your time is 31 December 1969, 19:00:00. So the difference of calculated time will be 5 hour.
For details, read this docs.
Allocates a Date object and initializes it to represent the specified
number of milliseconds since the standard base time known as "the
epoch", namely January 1, 1970, 00:00:00 GMT.
Parameters: date - the milliseconds since January 1, 1970, 00:00:00 GMT.
Date now = new Date();
long timeInterval = now.getTime() - (15705 * 24 * 60 * 60 * 1000L);
long hours = timeInterval / (60 * 60 * 1000L);
LOG.debug(String.format("current date:%s, timeInterval:%d,hours:%d",now.toString(),timeInterval, hours));
The result that system print is(15705 means the number of days since 1970s):
12/12/31 22:06:47 DEBUG stat.TimeTest: current date:Mon Dec 31
22:06:47 CST 2012, timeInterval:50807153, hours:14
You can see the current hour is 21 hours, but the result displays as 14 hours.
Mon Dec 31 22:06:47 CST 2012 is Mon Dec 31 14:06:47 2012 in GMT time, which is the time zone used for the start of the epoch.
In other words, now.getTime() returns the number of milliseconds since January 1, 1970, 00:00:00 GMT and you use a different time zone.
now.getTime() would get you the value in UTC millis - thats GMT+0.
the log print you showed probably uses the system time zone, where it was 22:06:47 and probably wasnt anywhere near england :-)
also, please use the Calendar class for date arithmatic because it, unlike your code, would take into account things like leap years, leap seconds and timezone changes (wehich dont happen in UTC, might mihg thappen in any other zone)