Can SimpleDateFormat parse floating point seconds? [duplicate] - java

Background:
In my database table, I have two timestamps
timeStamp1 = 2011-08-23 14:57:26.662
timeStamp2 = 2011-08-23 14:57:26.9
When I do an "ORDER BY TIMESTAMP ASC", timeStamp2 is considered as the greater timestamp(which is correct).
Requirement: I need to get the difference of these timestamps (timeStamp2 - timeStamp1)
My implementation:
public static String timeDifference(String now, String prev) {
try {
final Date currentParsed = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").parse(now);
final Date previousParsed = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").parse(prev);
long difference = currentParsed.getTime() - previousParsed.getTime();
return "" + difference;
} catch (ParseException e) {
return "Unknown";
}
}
The answer should have been 238ms, but the value that is returned is -653ms.
I'm not sure what I'm doing wrong. Any suggestions?

The format you are parsing and the format uses doesn't match. You expect a three digit field and are only providing one digits. It takes 9 and assumes you mean 009 when what you want is 900. Date formats are complicated and when you prove dates in a different format it may parse them differently to you.
The documentation says S means the number of milli-seconds and the number in that field is 9, so it is behaving correctly.
EDIT: This example may help
final SimpleDateFormat ss_SSS = new SimpleDateFormat("ss.SSS");
ss_SSS.setTimeZone(TimeZone.getTimeZone("GMT"));
for (String text : "0.9, 0.456, 0.123456".split(", ")) {
System.out.println(text + " parsed as \"ss.SSS\" is "
+ ss_SSS.parse(text).getTime() + " millis");
}
prints
0.9 parsed as "ss.SSS" is 9 millis
0.456 parsed as "ss.SSS" is 456 millis
0.123456 parsed as "ss.SSS" is 123456 millis

I'm not entirely sure, but the JavaDoc states this:
For parsing, the number of pattern letters is ignored unless it's needed to separate two adjacent fields.
This indicates that the milliseconds from 2011-08-23 14:57:26.9 would be parsed as 9 instead of 900. Adding the trailing zeros might work: 2011-08-23 14:57:26.900.

I'd suggest using Joda-Time. It handles these situations properly. In the following example, the milliseconds are correctly parsed as 200ms.
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
public class ParseMillis {
public static void main(String[] args) {
String s = "00:00:01.2";
DateTimeFormatter format = DateTimeFormat.forPattern("HH:mm:ss.S");
DateTime dateTime = format.parseDateTime(s);
System.out.println(dateTime.getMillisOfSecond());
}
}

I had the same problem with too accurate time from my logfiles with 6 digit milliseconds. Parsing Time gave up to 16 minutes difference! WTF?
16-JAN-12 04.00.00.999999 PM GMT --> 16 Jan 2012 04:16:39 GMT
Changing the number of digits reduced the erroneous difference and thanks to this thread I could identify the problem:
16-JAN-12 04.00.00.99999 PM GMT --> 16 Jan 2012 04:01:39 GMT
16-JAN-12 04.00.00.9999 PM GMT --> 16 Jan 2012 04:00:09 GMT
16-JAN-12 04.00.00.999 PM GMT --> 16 Jan 2012 04:00:00 GMT
As SimpleDateFormat internally handles only 3 digits I removed the unnecessary with a small regex (ignoring round-off errors, working for 1 up to n digits):
str = str.replaceAll("(\\.[0-9]{3})[0-9]*( [AP]M)", "$1$2");
Thanks to #Peter Lawrey for your answer, prevented me going insane :-)

Related

Java: milliseconds parsed wrongly when converted from string to timestamp

I have trouble while parsing the given string into a timestamp.
The milliseconds is parsed wrongly or let me know if I am missing something.
I get a string from the request as:
String selectedTimeStamp = request.getParameter("selectTime");
System.out.println("selectedTimeStamp: "+selectedTimeStamp);
and then I use simpleDateFormat to parse and format the string:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US);
Date parsedDate = sdf.parse(selectedTimeStamp);
Timestamp timestamp = new java.sql.Timestamp(parsedDate.getTime());
System.out.println("createdTime: " +timestamp);
The output I get is:
selectedTimeStamp: 2016-07-04 21:09:47.66
createdTime: 2016-07-04 21:09:47.066
Not sure why the millisecond is converted from 66 to 066 ? It should be 660
any idea?
Let's break it down step-by-step
Your code(effectively)
String selectedTimeStamp = "2016-07-04 21:09:47.6";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.US);
Date parsedDate = sdf.parse(selectedTimeStamp);
System.out.println("parsedDate: " + parsedDate);
Timestamp timestamp = new java.sql.Timestamp(parsedDate.getTime());
System.out.println("createdTime: " +timestamp);
I am assuming that these are the imports you have made:
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
The output of which is
parsedDate: Mon Jul 04 21:09:47 IST 2016
createdTime: 2016-07-04 21:09:47.006
So even though I passed 6 ms as argument, it got parsed to 600 ms, this is most likely due to the fact that we are using, java library for parsing date and sql library for time stamping it. The process for treating date objects is different in both the languages, hence the inconsistency. Also millisecond is treated with a number of 1000th precision(as 1000ms = 1s) therefore SQL automatically converts the Java stored 6 to 006 (or 66 to 066 in your case).
I simple workaround this problem will be to check selectedTimeStamp using selectedTimeStamp.length() - selectedTimeStamp.lastIndexOf('.') and concatenating the remaining zeroes, i.e. if it is 2 then 2 zeroes, if 3 then 1 zero, and if 4 then no zeroes.
This will give you correct result.
Add this after the String selectesTimeStamp = "2016-07-04 21:09:47.6" line
int x = selectedTimeStamp.length() - selectedTimeStamp.lastIndexOf('.');
if (x==2)
selectedTimeStamp += "00";
if (x==3)
selectedTimeStamp += '0';
Cheers!!!
P.S.: Changing SSS to SS will not work.

SimpleDateFormat format date between to dates issue

I need to calculate period between two dates, one date is now. And I'm using SimpleDateFormat for formatting date.
public String getPeriod(Date endDate) {
String format;
Date now = new Date();
long period = endDate.getTime() - now.getTime();
if (now.after(endDate)) {
return "passed";
} else {
if (period < 1000 * 60 * 60)
format = "m'M' s'S'";
else if (period < 1000 * 60 * 60 * 24)
format = "k'H' m'M'";
else
format = "'Too much'";
SimpleDateFormat formatter = new SimpleDateFormat(format);
return formatter.format(new Date(period)) + " / for testing - " + period / 3600000 + " hours";
}
}
As a result I have following input for example if endDate equals Wed Nov 12 13:30:02 EET 2014 (EST):
1 H 36 M / for testing - 22 hours
As you can see my test calculation and format's method result do not match. What am i doing wrong?
The difference is due to the timezone. For example, in my case, given as the parameter the time that would be in an hour, I get 3H as output, because the date would be Thu Jan 01 03:00:00 EET 1970. Notice the EET (I'm from Eastern Europe).
Your code would work if you'd notify java to use GMT time, as it says in the new Date(long) description:
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.
Also, keep in mind that Date does not give perfect results. Using programatically determined dates exactly 1h appart (no millies / minutes difference), date calculations give an offset of 59 minutes, 59 seconds and 999 milies. If you require more exact values, you should use nanoseconds.
However, the other commenters are right. You should not use Java Date / Calendar in such a way, as it is a bug factory (this is only one corner case). You should check out other libraries (such as yoda time), or if you only need simple calculations such as this, do it yourself.
Hope it helps.

Parsing date incorrectly

I'm trying to convert an input date string to a date format and then to a datetime format.
As a test, I gave an input of an incorrect date format, but this doesn't seem to be throwing any parse exceptions and gives me the wrong output. Any thoughts on what my code below is doing wrong?
String OLD_FORMAT ="MM/dd/yyyy";
String NEW_FORMAT ="yyyyMMdd HHmmss";
SimpleDateFormat sdf = new SimpleDateFormat(OLD_FORMAT);
String oldDateString = "03/01211/2012"; //Incorrect input
Date myOldDate;
Datetime myNewDate;
try {
myOldoldDate = sdf.parse(oldDateString);
//Returns Wed Jun 24 00:00:00 IST 2015...why??
//Shouldn't this be throwing a parse exception?
} catch (ParseException e) {
logger.error("Error while parsing Date");
}
sdf.applyPattern(NEW_FORMAT);
//Converting date to datetime format
try {
myNewDate= DateHelper.toDatetime(sdf.parse((sdf.format(myOldDate))));
//Returns 2015-06-24 00:00:00.0
} catch (ParseException e) {
logger.error("Error while parsing Date");
}
"03/01211/2012" => Jun 24 00:00:00 IST 2015 ... why?
My guess is that June 24th, 2015 is 1211 days from March 1st, 2012.
Excessive rollover, reads it as March 1211th.
You should be able to turn this off with:
sdf.setLenient(false)
public void setLenient(boolean lenient)
Specify whether or not date/time interpretation is to be lenient. With lenient interpretation, a date such as "February 942, 1996" will be treated as being equivalent to the 941st day after February 1, 1996. With strict interpretation, such dates will cause an exception to be thrown.
You can set strict format
SimpleDateFormat.setLenient(false)
Specify whether or not date/time parsing is to be lenient. With lenient parsing, the parser may use heuristics to interpret inputs that do not precisely match this object's format. With strict parsing, inputs must match this object's format.
Without looking at the source code, I assume 01211 is parsed to 1211 days which are added to 2012-03-01 thus resulting in 2015-06-24. As #Thilo said sdf.setLenient(false) should help here.
The problem is that by default the parser is more tolerant to wrong input (lenient mode is on by default) and thus won't throw an exception here.

String to a HH:mm:ss time and finding the difference between 2 times

public static void stringToDate(String time, String time2) {
try {
DateFormat formatter = new SimpleDateFormat("HH:mm:ss");
Date t1 = formatter.parse(time);
System.out.println("Users first date: " + t1);
} catch (ParseException e){
System.out.println("Exception :" + e);
}
}
So above, I pass in 2 string parameters which are in the format of something like '17:23:56' and want them both converted into proper time objects that i can then find the difference between the 2, possibly in miliseconds or whatevers available if anyone knows how that'd be great.
Problem i'm having so far is that the output is: "Users first date: Thu Jan 01 17:23:56 GMT 1970", even though I thought I specified it to only parse it in HH:mm:ss. Anyone got the solution, thanks.
You're printing the result of Date#toString() ( <-- click the link!) which is indeed in the given format. If you want to present it in HH:mm:ss you have to use the format() method on the obtained Date.
System.out.println(formatter.format(t1));
Don't worry about this. Just parse the other time string to Date as well, do a getTime() on both and finally do the math.

Why is this code giving me a date 39k years in the future?

I've written a method that returns the milisecond value of a string formatted date, and for some reason it's giving me dates 39000 years in the future. any ideas why?
private long getTimeInMs(String currentStartTimeString) {
//String newDateString = currentStartTimeString.substring(6,10)+"-"+currentStartTimeString.substring(3, 5)+"-"+currentStartTimeString.substring(0, 2)+ "T" + currentStartTimeString.substring(11);
String newDateString = currentStartTimeString.substring(0,19);
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
long timeInMs;
try {
timeInMs = df.parse(newDateString).getTime();
} catch (ParseException e) {
log.error("Failed to parse current Start Time",e);
return 0;
}
return timeInMs;
}
If I enter the date string "2009-07-07 10:51:01.15" it returns 1246960261000 which is actually Wed Aug 06 41484 11:16:40 GMT+0100 (GMT Daylight Time)
Okay I think the issue is that it's giving ms past the Java epoc and I'm evaluating it against the unix epoch...
I'm guessing that you interpreted the returned value from getTime() as if it was a Unix time_t value. It's not - it's milliseconds past the Java epoch, not seconds past the Unix epoch.
It looks fine to me. The Date object that comes out of the parse toString's to "Tue Jul 07 10:51:01 BST 2009" (I'm in the UK timezone here), but that should make no big difference). The millis value of 1246960261000 is correct, why do you think that evaluates to the far future? How did you calculate that?
The value is correct, in fact :
(((((1246989061000 / 1000) / 60)/60)/24)/365)
gives
39.54176373033992 years which is about correct given that 0 is 1970.
running your code in Java 6, i get 1246978261000, which turns out to be correct.
System.out.println(new Date(timeInMs));
returns
Tue Jul 07 10:51:01 EDT 2009
edit:
to confirm others' suggestions that you are looking at seconds (not millis):
System.out.println(new Date(timeInMs*1000));
yields
Mon Mar 02 13:16:40 EST 41485

Categories

Resources