9

I want to parse a timestamp, like this - "2016-03-16 01:14:21.6739". But when I use the SimpleDateFormat to parse it, I find that it outputs an incorrect parsed value. It will covert 6739 milliseconds to 6 seconds with 739 millseconds left. It converted the date to this format - Wed Mar 16 01:14:27 PDT 2016. Why the seconds part has changed from 21 seconds to 27 seconds(an addition of 6 seconds?). The following is my code snippet:

final SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSS");
String parsedate="2016-03-16 01:14:21.6739";
try {
    Date outputdate = sf.parse(parsedate);
    String newdate = outputdate.toString();  //==output date is: Wed Mar 16 01:14:27 PDT 2016 
    System.out.println(newdate);
} catch (ParseException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}
Bilesh Ganguly
  • 3,792
  • 3
  • 36
  • 58
Alter Hu
  • 739
  • 1
  • 7
  • 15
  • 1
    Remember that a millisecond is 0.001 of a second. So four digit millisecond will be more than 1 sec. – Ayman Mar 17 '16 at 09:08
  • yes, had changed to output the date as this code:String newdate = sf.format(outputdate); but it still output is: 2016-03-16 01:14:27.0739 ???? – Alter Hu Mar 17 '16 at 09:15

6 Answers6

5

SS in SimpleDateFormat is milliseconds. You have 6739 milliseconds, which means you are adding an extra 6.7 seconds onto your time. Perhaps you can truncate the 6739 to 673 (or if you prefer, round it to 674) so it can be parsed correctly as milliseconds.

khelwood
  • 55,782
  • 14
  • 81
  • 108
  • thanks ,currently i need to parse this String date and compare this time with current time ,i cannot cut it to SSS ,any suggestion to parse correct time "2016-03-16 01:14:21.6739" – Alter Hu Mar 17 '16 at 09:12
  • 2
    [`Date`](https://docs.oracle.com/javase/7/docs/api/java/util/Date.html) only supports millisecond precision. You _cannot_ represent `2016-03-16 01:14:21.6739` as a `java.util.Date`. – khelwood Mar 17 '16 at 09:13
  • Take to use the package : java.time pakcage in java 8 had solved my problem: https://docs.oracle.com/javase/8/docs/api/java/time/package-summary.html – Alter Hu Mar 17 '16 at 09:44
  • @Alter.hu, you said you are comparing the dates with the current time. For that reason you mention not to be able to strip the last digit of your dates. Just as a matter of interest: How do you obtain the current date with the 10th of a millisecond precision? – dpr Mar 19 '16 at 13:47
5

It seems that is not possible to use SimpleDateFormat to express times with a finer grain than the millisecond. What is happening is that as you put 6739, Java understands it as 6739 milliseconds i.e. 6 seconds and 739 milliseconds hence the 6 seconds difference observed.

Check these ones, it is explained quite well: String-Date conversion with nanoseconds Java date parsing with microsecond or nanosecond accuracy

Community
  • 1
  • 1
  • yes, it's really java version issue ,for java version less than java 8 ,we cannot use SimpleDateFormat,Date and Calendar for nanoseconds.thanks for your help . – Alter Hu Mar 17 '16 at 09:34
  • 1
    Take to use java 8 to parse the dateTime,list the java8 code below:DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSS"); LocalDateTime newdate = LocalDateTime.parse(parsedate, dateTimeFormatter); System.out.println(newdate.toString()); – Alter Hu Mar 17 '16 at 09:37
4

tl;dr

LocalDateTime.parse(
    "2016-03-16 01:14:21.6739".replace( " " , "T" )  // Comply with ISO 8601 standard format.
)

Milliseconds versus Microseconds

As others noted, java.util.Date has millisecond resolution. That means up to 3 digits of a decimal fraction of second.

You have 4 digits in your input string, one too many. Your input value demands finer resolution such as microseconds or nanoseconds.

java.time

Instead of using the flawed, confusing, and troublesome java.util.Date/.Calendar classes, move on to their replacement: the java.time framework built into Java 8 and later.

The java.time classes have a resolution of nanosecond, up to nine digits of decimal fraction of a second. For example:

2016-03-17T05:19:24.123456789Z

ISO 8601

Your string input is almost in standard ISO 8601 format used by default in java.time when parsing/generating textual representations of date-time values. Replace that space in the middle with a T to comply with ISO 8601.

String input = "2016-03-16 01:14:21.6739".replace( " " , "T" );

Unzoned

A LocalDateTime is an approximation of a date-time, without any time zone context. Not a moment on the timeline.

LocalDateTime ldt = LocalDateTime.parse( input );

UTC

Make that LocalDateTime an actual moment on the timeline by applying the intended time zone. If meant for UTC, make an Instant.

Instant instant = ldt.toInstant( ZoneOffset.UTC );

Zoned

If meant for a particular time zone, specify a ZoneId to get a ZoneDateTime.

ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ldt.atZone( zoneId );
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • I believe that when you do ```LocalDateTime.parse( input );``` you lose time zone information (if there was any in the original string) – Clint Eastwood Jan 11 '18 at 23:34
  • @ClintEastwood I think you missed the point. The input string shown in the Question has no offset-from-UTC and no time zone. So we parse an input lacking offset/zone using a class lacking offset/zone. Nothing lost. Neither the input string nor the `LocalDateTime` object represent a specific moment in time, only some possible moments over a 26-27 hour range. Reread my Answer for more detail. – Basil Bourque Jan 12 '18 at 00:47
1

If you have to get string as final output why not use format instead of parse

        final SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ");
        sf.setTimeZone(TimeZone.getTimeZone("UTC")); 
        Date curDate = new Date();

        String outputdate = sf.format(curDate);
        // 2016-03-17 09:45:28.658+0000
        System.out.println(outputdate);

        Date strToDate = new Date();
        try {
            strToDate = sf.parse(outputdate);
        } catch (ParseException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        //Thu Mar 17 17:11:30 MYT 2016
        System.out.println(strToDate);

and instead of "yyyy-MM-dd HH:mm:ss.SSSS" use "yyyy-MM-dd HH:mm:ss.SSSZ" check it here https://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html

"yyyy-MM-dd'T'HH:mm:ss.SSSZ"    2001-07-04T12:08:56.235-0700
Raunak Kathuria
  • 3,185
  • 1
  • 18
  • 27
  • No, you use the timezon to parse the time ,but here the parse format had no timezoo,it will throw the exception for our code: java.text.ParseException: Unparseable date: "2016-03-16 01:14:21.6739" – Alter Hu Mar 17 '16 at 09:18
  • @Alter.hu you can do sf.setTimeZone(TimeZone.getTimeZone("UTC")); – Raunak Kathuria Mar 17 '16 at 09:45
0

You can use String newdate = sf.format(outputdate); in place of String newdate = outputdate.toString();

visingh
  • 233
  • 3
  • 17
  • yes, it outputs to : 2016-03-16 01:14:27.0739 , but i want the time to be "2016-03-16 01:14:21.6739" ,because it had not be 27 seconds. – Alter Hu Mar 17 '16 at 09:14
0

As mentioned in the comments above your question already contains the answer: milliseconds must not have more than 3 digits otherwise it represents at least one full second already.

That your code is working is simply due to a very questionable feature of java.text.SimpleDateFormat, the lenient (see here) option. By default a SimpleDateFormat has setLenient set to true wich means that the parser will try to interpret strings that do not match the pattern 100% and will convert them to date objects by means of some heuristics. For example it will accept a date 31.04.2016 and convert it to 01.05.2016. This feature may be nice in some situations but produces questionable results in most of the cases.

If you set lenient to false in your code, the date string won't be parsed anymore. Making the error in the pattern more obvious:

final SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSS");
sf.setLenient(false);
String parsedate="2016-03-16 01:14:21.6739";
...

As java.util.Date is not able to represent any precision lower than milliseconds, I think the best option for you to parse your date would be to simply strip of the last digits of your input date, if the part after the dot has more than four digits. Your code might look something like this:

final SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
sf.setLenient(false);
String parsedate="2016-03-16 01:14:21.6739";
try {
    // 23 is the length of the date pattern
    if (parsedate.length() > 23) {
        parsedate = parsedate.substring(0, 23);
    }
    Date outputdate = sf.parse(parsedate);
    String newdate = sf.format(outputdate);  //==output date is: 2016-03-16 01:14:21.673
    System.out.println(newdate);
} catch (ParseException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

You could try to add some rounding logic as well in order to not loose all the information of the 4th digit...

dpr
  • 10,591
  • 3
  • 41
  • 71
  • yes, great tip to debug SimpleDateFormat class ,but here i don't want to remove the last millseconds . – Alter Hu Mar 17 '16 at 09:49
  • @Alter.hu, I don't get what my answer has to do with debugging. But if you don't want to loose the 10th of a millisecond precision you should probably use java.time classes instead. See [DateTimeFormatter](https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html) – dpr Mar 17 '16 at 10:36