Java Simpledateformatter With 10 Decimals After the Seconds, Cannot Convert to Date

Java SimpleDateformatter with 10 decimals after the seconds, cannot convert to Date

There are multiple problems at the moment. I'd strongly recommend using java.time for as much work as possible, although even that doesn't make this easy.

As you say, your value has 10 digits for "fraction of a second" - which means it goes down to 10th-of-a-nanosecond precision. That's highly unusual to start with, in my experience - and I don't think Java can handle that, even with java.time.

I suspect you'll need to massage the data first, down to 9 digits of precision. At that point, it's fairly straightforward to parse the value to a ZonedDateTime:

// Note this only has 9 digits of precision, not the original 10
String text = "2018-11-02 6:05:59.154116215 PM";
ZoneId zone = ZoneId.of("Asia/Colombo");
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(
"yyyy-MM-dd h:mm:ss.SSSSSSSSS a", Locale.US)
.withZone(zone);
ZonedDateTime parsed = ZonedDateTime.parse(text, formatter);
System.out.println(parsed);

Note how I've provided SSSSSSSSS as the fraction-of-a-second part, to handle all 9 digits. Also note the use of h instead of HH - HH would mean "24-hour hour of day, with 0 padding" - neither part of which is true for your original value, which uses "6" for 6pm. It's very rare that you would want to combine H or HH with a.

That code gives you a ZonedDateTime, which you could convert into a Date like this:

Date date = Date.from(parsed.toInstant());

I'd recommend you don't do that unless you really, really need to for interop reasons though; the Date API is nasty in various ways.

Which date pattern in java can produce this date result? (seven digits after seconds)

First point, get rid of SimpleDateFormat. That class is notoriously troublesome and long outdated. Also there is no way that SimpleDateFormat can produce 7 decimals on the seconds.

Second point, you probably don’t need to print 7 decimals on the seconds. Your format exemplified through 2019-12-18T17:11:24.2646051+03:30 is ISO 8601. In ISO 8601 the number of decimals on the seconds is free, so everyone should accept if you give them a string with 3 decimals, 9 decimals or no decimals at all (in the last case leave out the decimal point too).

java.time

So the easy solution is:

    String desiredString
= OffsetDateTime.now(ZoneId.systemDefault()).toString();
System.out.println(desiredString);

Output when I ran the code just now:

2019-12-28T11:46:07.308+01:00

I am using java.time, the modern Java date and time API. And I am exploiting the fact that the toString methods of the date and time classes of java.time print ISO 8601 format. So we need no explicit formatter so far.

If you do want or need 7 decimals, do use a DateTimeFormatter:

    DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ISO_LOCAL_DATE)
.appendLiteral('T')
.appendPattern("HH:mm:ss.SSSSSSSXXX")
.toFormatter();
String desiredString = OffsetDateTime.now(ZoneId.systemDefault())
.format(formatter);

2019-12-28T11:48:52.3840000+01:00

It is possible to specify the formatter using a format pattern string only, but I prefer to reuse the built-in ISO_LOCAL_DATE formatter for the date part also when the code gets a few lines longer.

Links

  • Oracle tutorial: Date Time explaining how to use java.time.
  • Wikipedia article: ISO 8601

Parse String to Date with Different Format in Java

Take a look at SimpleDateFormat. The code goes something like this:

SimpleDateFormat fromUser = new SimpleDateFormat("dd/MM/yyyy");
SimpleDateFormat myFormat = new SimpleDateFormat("yyyy-MM-dd");

try {

String reformattedStr = myFormat.format(fromUser.parse(inputString));
} catch (ParseException e) {
e.printStackTrace();
}

Java: Date parsing, why do I get an error

    Date date = new Date();
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");//2018-02-05T18:00:51.001+0000
String text = dateFormat.format(date);

try {
Date test = dateFormat.parse(text);
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

worked for me. With "SSSZ" instead of "SZ" at the end of the pattern.

Change date from MM/dd to yyyy-MM-dd'T'HH:mm:ss.SSZ in Java

In the question, the date format pattern indicates a desire for 2-digit fractional seconds. SimpleDateFormat cannot do that.

The newer Java 8 Time API can, and you should be using that anyway.

If you're running on Java 6 or 7, get the ThreeTen-Backport library.

To parse a MM/dd formatted string and get a full timestamp with current year and time-of-day, in the default time zone, use the following code:

String strDate = "06/05";
MonthDay monthDay = MonthDay.parse(strDate, DateTimeFormatter.ofPattern("MM/dd"));
ZonedDateTime date = ZonedDateTime.now().with(monthDay);
System.out.println(date.format(DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSZ")));

Sample Output

2020-06-05T14:52:48.45-0400

Java problem with Java.util.Date and Java.util.TimeZone

tl;dr

java.time.OffsetDateTime.parse( "2020-04-11T14:52:34.8121672+00:00" ) 

See this code run live at IdeOne.com.

Details

Never use java.util.Date. That flawed class was supplanted years ago by the modern java.time classes defined in JSR 310. Specifically replaced by java.time.Instant.

Avoid setting the JVM’s current default time zone. Doing so immediately affects all other code in all threads of all apps running in that JVM. Write your code such that you never depend on the default time zone. Always specify your desired/expected time zone.

No need to define this custom formatting pattern: "yyyy-MM-dd'T'HH:mm:ss.SSSSSSSXXX". That format is defined in the ISO 8601 standard, used by default in java.time when parsing/generating text.

By convention, the decimal fractions are written in groups of three digits. So suggest to the publisher of your data writing that textual value as 2020-04-11T14:52:34.812167200+00:00 rather than 2020-04-11T14:52:34.8121672+00:00. And congratulate the publisher on using both the hours and the minutes of the offset, as well as the colon. While technically optional in ISO 8601, including them maximizes compatibility with various date-time handling libraries in the real world.

Your format is asking for hundreds of nanoseconds. But the legacy date-time types resolve to mere milliseconds (thousandths of a second). So you were pressing a square peg into a round hole. Fortunately, java.time resolves to nanoseconds (billionths of a second). So we can handle parsing your input.

Parse your particular input as a OffsetDateTime.

OffsetDateTime odt = OffsetDateTime.parse( "2020-04-11T14:52:34.8121672+00:00" ) ;

Generate a string in standard ISO 8601 format.

String output = odt.toString() ;

2020-04-11T14:52:34.812167200Z

Convert to the terrible legacy java.util.Date only if you must. Find new conversion methods added to the old classes.

java.util.Date juDate = Date.from( odt.toInstant() ) ;

All of this has been addressed many many times on Stack Overflow. Please search more thoroughly before posting. And, search to learn more.

Converting string date to string in yyyy-MM-dd'T'HH:mm:ss.SSSSSS format in java 7

As others have already mentioned, your requirement does not fit the use of Date and SimpleDateFormat since these only support milliseconds, that is, three decimals on the seconds, where you have six decimals (microseconds). So we need to find some other way. This is basically a good idea anyway, since Date and SimpleDateFormat are long outdated, and today we have better tools for the job.

I have got two suggestions for you.

java.time

Even in Java 7 I think that it’s a good idea to use the modern Java date and time API that came out with Java 8, AKA JSR-310. Can you do that? Certainly; use the backport for Java 6 and 7, ThreeTen Backport. The modern API supports anything from 0 through 9 decimals on the seconds, and the code is straightforward when you know how:

private static DateTimeFormatter inputParser 
= DateTimeFormatter.ofPattern("yyyy-MM-dd-HH.mm.ss.SSSSSS");
private static DateTimeFormatter outputFormatter
= DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSS");

public static String convertDate(String strDate) {
return LocalDateTime.parse(strDate, inputParser)
.format(outputFormatter);
}

I am using your own two format pattern strings. convertDate("2017-08-23-11.19.02.234850") returns 2017-08-23T11:19:02.234850.

There is a simplification possible: Since the format you want to obtain, conforms with ISO 8601, you don’t need an explicit formatter for it. The modern classes understand and produce ISO 8601 formats natively, so you may use:

    return LocalDateTime.parse(strDate, inputParser).toString();

However, if the decimals on the seconds happened to end in 000, this will not print the last three zeroes. So if six decimals are required even in this case, use the formatter.

Regular expression

If you don’t want to rely on an external library, even temporarily until once you upgrade to Java 8 (or 9), your job can be done with a regular expression:

    return strDate
.replaceFirst("^(\\d{4}-\\d{2}-\\d{2})-(\\d{2})\\.(\\d{2})\\.(\\d{2}\\.\\d{6})$",
"$1T$2:$3:$4");

It’s less elegant and harder to read, and it doesn’t offer the level of input validation you get from using a proper date and time API. Other than that, it’s a way through.

java.sql.Timestamp?

As others have said, java.sql.Timestamp offers nanosecond precision and thus will hold your date-time. Parsing your string into a Timestamp isn’t straightforward, though, so I don’t think it’s worth the trouble. Usagi Miyanmoto correctly identifies Timestamp.valueOf() as the method to use, but before you could do that, you would have change the format, so you would end up changing the format twice instead of just once. Or maybe three times since Timestamp also doesn’t produce your desired ISO 8601 format readily. Additionally you would need to decide a time zone for the timestamp, but I assume you could do that without any trouble.

If you needed to keep the the date-time around, a Timestamp object might be worth considering, but again, it’s a long outdated class. In any case, for reformatting alone, I certainly would not use it.

What happened in your code?

SimpleDateFormat understood 234850 as milliseconds, that is, 234 seconds 850 milliseconds. So it added 234 seconds to your time, 11:19:02. And then printed the remaining 850 milliseconds in 6 decimal places as you had requested.

How to display a date mm/dd/yy as a decimal in Java?

Avoid legacy date-time classes

You are using old date-time classes that have proven to be troublesome, confusing, and flawed.

The old Calendar and Date classes are now legacy. Supplanted by the java.time classes.

java.time

Use the java.time classes for date-time work.

Caveat: I do not recommend representing a span of time as a decimal number. Counting a span of time as something like “1.062 years later” has no utility that I know of. Instead I suggest using the standard ISO 8601 format for a textual representation (PnYnMnDTnHnMnS). Nevertheless, I took on this Question as a puzzle challenge.

To obtain a decimal fraction representing a fraction of the year, use the Period and Duration classes, with Year helping out. The strategy is to get a fraction of year and determine the length of that fractional year in nanoseconds. Divide that number by the number of nanoseconds in that particular year (years vary in length by Leap Year and by anomalies such as redefinition of time zone), to get our decimal fraction. Add the total number of whole years for a result.

Determine the start and stop of our span of time.

ZoneId z = ZoneId.of ( "America/Montreal" );
ZonedDateTime zdtStart = ZonedDateTime.of ( 2015 , 2 , 21 , 12 , 34 , 56 , 0 , z );
ZonedDateTime zdtStop = ZonedDateTime.of ( 2016 , 3 , 15 , 12 , 34 , 56 , 0 , z );

Get the count of whole years via Period.

Period p = Period.between ( zdtStart.toLocalDate () , zdtStop.toLocalDate () );
int years = p.getYears ();

Now focus on the fraction of a year. Add the number of whole years to the starting moment to get the start of that last fractional year.

ZonedDateTime zdtStartFractional = zdtStart.plusYears ( years );

Get the span of time, unattached from timeline, between our start of fractional year and the stopping point of our original span.

Duration fractionalYear = Duration.between ( zdtStartFractional , zdtStop );

Calculate our divisor, the length of this particular year in nanoseconds. Get the moment the year starts, and get a year later when following year starts.

ZonedDateTime zdtFractionalYear_StartOfYear = zdtStartFractional.with ( TemporalAdjusters.firstDayOfYear () ).toLocalDate ().atStartOfDay ( z );
Duration wholeYear = Duration.between ( zdtFractionalYear_StartOfYear , zdtFractionalYearStart.plusYears ( 1 ) );

Get the nanoseconds. To avoid the extraneous extra digits of floating point technology, we avoid float/Float and double/Double, using BigDecimal for accuracy. Specify the scale (number of decimal fraction digits) you want in your result; here we use 32 arbitrarily.

BigDecimal fractionalYearAsNanos = new BigDecimal ( fractionalYear.toNanos () );
BigDecimal wholeYearAsNanos = new BigDecimal ( wholeYear.toNanos () );
BigDecimal ratio = fractionalYearAsNanos.divide ( wholeYearAsNanos , 32 , RoundingMode.HALF_EVEN );
BigDecimal result = ratio.add ( new BigDecimal ( years ) );

Dump to console.

System.out.println ( zdtStart + "/" + zdtStop );
System.out.println ( "Years: " + result.toPlainString () );

2015-02-21T12:34:56-05:00[America/Montreal]/2016-03-15T12:34:56-04:00[America/Montreal]

Years: 1.06272768670309653916211293260474

See live code in IdeOne.com.


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.

Where to obtain the java.time classes?

  • Java SE 8 and 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 SE 7

    • Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
  • Android

    • The ThreeTenABP project adapts ThreeTen-Backport (mentioned above) for Android specifically.
    • See How to use….

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.

Parsing string to date: Illegal pattern character 'T'.

Given your input of 2014-09-17T12:00:44.0000000Z, it is not sufficient to escape the letter T only. You also have to handle the trailing Z. But be aware, this Z is NOT a literal, but has the meaning of UTC+00:00 timezone offset according to ISO-8601-standard. So escaping Z is NOT correct.

SimpleDateFormat handles this special char Z by pattern symbol X. So the final solution looks like:

 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSSSSSX");
Date d = sdf.parse("2014-09-17T12:00:44.0000000Z");
System.out.println(d); // output: Wed Sep 17 14:00:44 CEST 2014

Note that the different clock time is right for timezone CEST (toString() uses system timezone), and that the result is equivalent to UTC-time 12:00:44. Furthermore, I had to insert seven symbols S in order to correctly process your input which pretends to have precision down to 100ns (although Java pre 8 can only process milliseconds).



Related Topics



Leave a reply



Submit