Java 8 DateTimeFormatter dropping millis when they're zero? - java

This seems weird. Java 8 is formatting the output differently depending on whether the millis is zero. How do you force Java 8 (1.8.0_20) to always spit out the millis regardless of if they're zero or not?
public static void main(String[] args) {
TemporalAccessor zeroedMillis = DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse("2015-07-14T20:50:00.000Z");
TemporalAccessor hasMillis = DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse("2015-07-14T20:50:00.333Z");
System.out.println(DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(zeroedMillis));
System.out.println(DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(hasMillis));
}
2015-07-14T20:50:00Z
2015-07-14T20:50:00.333Z

You don't use ISO_OFFSET_DATE_TIME, basically :)
If you follow the documentation for that, you end up in the docs of ISO_LOCAL_TIME which has:
This returns an immutable formatter capable of formatting and parsing the ISO-8601 extended local time format. The format consists of:
Two digits for the hour-of-day. This is pre-padded by zero to ensure two digits.
A colon
Two digits for the minute-of-hour. This is pre-padded by zero to ensure two digits.
If the second-of-minute is not available then the format is complete.
A colon
Two digits for the second-of-minute. This is pre-padded by zero to ensure two digits.
If the nano-of-second is zero or not available then the format is complete.
A decimal point
One to nine digits for the nano-of-second. As many digits will be output as required.
If you always want exactly 3 digits, I suspect you want DateTimeFormatter.ofPattern with a pattern of yyyy-MM-dd'T'HH:mm:ss.SSSX.

Related

SimpleDateFormat [0] issue

I've below SimpleDateFormat Code
Date date = new Date();
DateFormat inpuDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SS'Z'");
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
String dateStr = inpuDateFormat.format(cal.getTime());
It works perfectly on my dev servers but it fails on sandbox instances with following error.
org.junit.ComparisonFailure: expected:<...20-08-12T19:06:02.85[0]Z> but was:<...20-08-12T19:06:02.85[]Z>
I've handled it as
dateStr = dateStr.replace("[0]","");
dateStr = dateStr.replace("[]","");
But, I still didn't get the logic why my date is different on different server instances and is there any better way to handle it
java.time
There certainly is a much better way to handle it. Use java.time, the modern Java date and time API, for your date and time work, not Date, DateFormat, SimpleDateFormat nor Calendar.
Instant now = Instant.now();
String dateStr1 = now.toString();
System.out.println(dateStr1);
Output in one run was:
2020-07-24T18:06:07.988093Z
You notice that six decimals on the seconds were output, not two. In other runs you may have three decimals or no fraction at all. Don’t worry, for the majority of purposes you’ll be just fine. The format printed is ISO 8601, and according to ISO 8601 the count of decimals on the seconds, even the presence of seconds at all, is optional. So whatever you need the string for, as long as ISO 8601 format is expected, the string from the above code snippet should be accepted.
I am exploiting the fact that Instant.toString() produces ISO 8601 format, so we don’t need any formatter.
If for some strange reason you do need exactly two decimals on the seconds, use a formatter for specifying so (edit: now outputting Z):
DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSX")
.withZone(ZoneOffset.UTC);
String dateStr2 = formatter2.format(now);
System.out.println(dateStr2);
2020-07-24T18:06:07.98Z
To a DateTimeFormatter (opposite a SimpleDateFormat) uppercase S in the format pattern string means fraction of second, and you are free to place from one through nine of them to get from one to nine decimals.
What went wrong in your code?
First, the message that you got from your JUnit test was:
org.junit.ComparisonFailure: expected:<...20-08-12T19:06:02.85[0]Z> but was:<...20-08-12T19:06:02.85[]Z>
The square brackets is JUnit’s way of drawing our attention to the difference between the expected and the actual value. So they are not part of those values. What JUnit tells us is that the value was expected to end in .850Z but instead ended in just .85Z. So a zero was missing. Your test is probably too strict since as I said, it shouldn’t matter whether there are two or three decimals. And 02.85 and 02.850 are just different ways of denoting the exact same value.
This role of the square brackets also explains why replacing [0] and [] in the string didn’t help: the square brackets were never in the strings, so the replacements never made any change to the strings.
Second, to SimpleDateFormat (opposite DateTimeFormatter) format pattern letter uppercase S means millisecond. So putting any other number than three of them makes no sense and gives you incorrect results. In your code you put two. In nine of ten cases the millisecond value is in the interval 100 through 999, and in this case SimpleDateFormat prints all three digits in spite of the only two pattern letters S. This probably explains why your unit test passed in your development environment. On your sandbox incidentally the time ended in 2.085 seconds. The correct ways to render this include 02.08 and 02.085. Your SimpleDateFormat chose neither. To it the millisecond value of 85 was to be rendered in two positions, so it produces 02.85, which is the wrong value, 765 milliseconds later. And your unit test objected while this once there were only two decimals, not three.
Third, not what you asked, but no matter if using the troublesome SimpleDateFormat or the modern DateTimeFormatter you must never hardcode Z as a literal in the format pattern string. The trailing Z means UTC or offset zero from UTC. It needs to be printed (and parsed if that were the case) as an offset, or you get wrong results. The way to make sure you get a Z and not for example an offset of +02:00 is to make sure that an offset of 0 is specified. This was why I put .withZone(ZoneOffset.UTC) on my formatter.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Wikipedia article: ISO 8601
Try to remove the quotes around the 'Z', as 'Z' is a constant whilst without quotes it means 'time zone':
DateFormat inpuDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
(By the way, in most cases you want to use three decimal places for milliseconds: "SSS".)

Elastic Search and Y10k (years with more than 4 digits)

I discovered this issue in connection with Elastic Search queries, but since the ES date format documentation links to the API documentation for the java.time.format.DateTimeFormatter class, the problem is not really ES specific.
Short summary: We are having problems with dates beyond year 9999, more exactly, years with more than 4 digits.
The documents stored in ES have a date field, which in the index descriptor is defined with format "date", which corresponds to "yyyy-MM-dd" using the pattern language from DateTimeFormatter. We are getting user input, validate the input using org.apache.commons.validator.DateValidator.isValid also with the pattern "yyyy-MM-dd" and if valid, we create an ES query with the user input. This fails with an execption if the user inputs something like 20202-12-03. The search term is probably not intentional, but the expected behaviour would be not to find anything and not that the software coughs up an exception.
The problem is that org.apache.commons.validator.DateValidator is internally using the older SimpleDateFormat class to verify if the input conforms to the pattern and the meaning of "yyyy" as interpreted by SimpleDateFormat is something like: Use at least 4 digits, but allow more digits if required. Creating a SimpleDateFormat with pattern "yyyy-MM-dd" will thus both parse an input like "20202-07-14" and similarly format a Date object with a year beyond 9999.
The new DateTimeFormatter class is much more strict and means with "yyyy" exactly four digits. It will fail to parse an input string like "20202-07-14" and also fail to format a Temporal object with a year beyond 9999. It is worth to notice that DateTimeFormatter is itself capable of handling variable-length fields. The constant DateTimeFormatter.ISO_LOCAL_DATE is for example not equivalent to "yyyy-MM-dd", but does, conforming with ISO8601, allow years with more than four digits, but will use at least four digits. This constant is created programmatically with a DateTimeFormatterBuilder and not using a pattern string.
ES can't be configured to use the constants defined in DateTimeFormatter like ISO_LOCAL_DATE, but only with a pattern string. ES also knows a list of predefined patterns, occasionally the ISO standard is also referred to in the documentation, but they seem to be mistaken and ignore that a valid ISO date string can contain five digit years.
I can configure ES with a list of multiple allowed date patterns, e.g "yyyy-MM-dd||yyyyy-MM-dd". That will allow both four and five digits in the year, but fail for a six digit year. I can support six digit years by adding yet another allowed pattern: "yyyy-MM-dd||yyyyy-MM-dd||yyyyyy-MM-dd", but then it fails for seven digit years and so on.
Am I overseeing something, or is it really not possible to configure ES (or a DateTimeFormatter instance using a pattern string) to have a year field with at least four digits (but potentially more) as used by the ISO standard?
Edit
ISO 8601
Since your requirement is to conform with ISO 8601, let’s first see what ISO 8601 says (quoted from the link at the bottom):
To represent years before 0000 or after 9999, the standard also
permits the expansion of the year representation but only by prior
agreement between the sender and the receiver. An expanded year
representation [±YYYYY] must have an agreed-upon number of extra year
digits beyond the four-digit minimum, and it must be prefixed with a +
or − sign instead of the more common AD/BC (or CE/BCE) notation; …
So 20202-12-03 is not a valid date in ISO 8601. If you explicitly inform your users that you accept, say, up to 6 digit years, then +20202-12-03 and -20202-12-03 are valid, and only with the + or - sign.
Accepting more than 4 digits
The format pattern uuuu-MM-dd formats and parses dates in accordance with ISO 8601, also years with more than four digits. For example:
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("uuuu-MM-dd");
LocalDate date = LocalDate.parse("+20202-12-03", dateFormatter);
System.out.println("Parsed: " + date);
System.out.println("Formatted back: " + date.format(dateFormatter));
Output:
Parsed: +20202-12-03
Formatted back: +20202-12-03
It works quite similarly for a prefixed minus instead of the plus sign.
Accepting more than 4 digits without sign
yyyy-MM-dd||yyyyy-MM-dd||yyyyyy-MM-dd||yyyyyyy-MM-dd||yyyyyyyy-MM-dd||yyyyyyyyy-MM-dd
As I said, this disagrees with ISO 8601. I also agree with you that it isn’t nice. And obviously it will fail for 10 or more digits, but that would fail for a different reason anyway: java.time handles years in the interval -999 999 999 through +999 999 999. So trying yyyyyyyyyy-MM-dd (10 digit year) would get you into serious trouble except in the corner case where the user enters a year with a leading zero.
I am sorry, this is as good as it gets. DateTimeFormatter format patterns do not support all of what you are asking for. There is no (single) pattern that will give you four digit years in the range 0000 through 9999 and more digits for years after that.
The documentation of DateTimeFormatter says about formatting and parsing years:
Year: The count of letters determines the minimum field width below which padding is used. If the count of letters is two, then a
reduced two digit form is used. For printing, this outputs the
rightmost two digits. For parsing, this will parse using the base
value of 2000, resulting in a year within the range 2000 to 2099
inclusive. If the count of letters is less than four (but not two),
then the sign is only output for negative years as per
SignStyle.NORMAL. Otherwise, the sign is output if the pad width is
exceeded, as per SignStyle.EXCEEDS_PAD.
So no matter which count of pattern letters you go for, you will be unable to parse years with more digits without sign, and years with fewer digits will be formatted with this many digits with leading zeroes.
Original answer
You can probably get away with the pattern u-MM-dd. Demonstration:
String formatPattern = "u-MM-dd";
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(formatPattern);
LocalDate normalDate = LocalDate.parse("2020-07-14", dateFormatter);
String formattedAgain = normalDate.format(dateFormatter);
System.out.format("LocalDate: %s. String: %s.%n", normalDate, formattedAgain);
LocalDate largeDate = LocalDate.parse("20202-07-14", dateFormatter);
String largeFormattedAgain = largeDate.format(dateFormatter);
System.out.format("LocalDate: %s. String: %s.%n", largeDate, largeFormattedAgain);
Output:
LocalDate: 2020-07-14. String: 2020-07-14.
LocalDate: +20202-07-14. String: 20202-07-14.
Counter-intuituvely but very practically one format letter does not mean 1 digit but rather as many digits as it takes. So the flip side of the above is that years before year 1000 will be formatted with fewer than 4 digits. Which, as you say, disagrees with ISO 8601.
For the difference between pattern letter y and u for year see the link at the bottom.
You might also consider one M and/or one d to accept 2020-007-014, but again, this will cause formatting into just 1 digit for numbers less than 10, like 2020-7-14, which probably isn’t what you want and again disagrees with ISO.
Links
Years section of Wikipedia article: ISO 8601
Documentation of DateTimeFormatter
uuuu versus yyyy in DateTimeFormatter formatting pattern codes in Java?
Maybe this will work:
[uuuu][uuuuu][...]-MM-dd
Format specifiers placed between square brackets are optional parts. Format specifiers inside brackets can be repeated to allow for multiple options to be accepted.
This pattern will allow a year number of either four or five digits, but rejects all other cases.
Here is this pattern in action. Note that this pattern is useful for parsing a string into a LocalDate. However, to format a LocalDate instance into a string, the pattern should be uuuu-MM-dd. That is because the two optional year parts cause the year number to be printed twice.
Repeating all possible year number digit counts, is the closest you can get in order to make it work the way you expect it to work.
The problem with the current implementation of DateTimeFormatter is that when you specify 4 or more u or ys, the resolver will try to consume exactly that number of year digits. However, with less than 4, then the resolver will try to consume as many as possible. I do not know whether this behavior is intentional.
So the intended behavior can be achieved with a formatter builder, but not with a pattern string. As JodaStephen once pointed out, "patterns are a subset of the possible formatters".
Maybe the characters #, { and }, which are reserved for future use, will be useful in this regard.
Update
You can use DateTimeFormatterBuilder#appendValueReduced to restrict the number of digits in a year in the range of 4-9 digits.
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;
public class Main {
public static void main(String[] args) {
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendValueReduced(ChronoField.YEAR, 4, 9, 1000)
.appendPattern("-MM-dd")
.toFormatter();
String[] dateStrArr = { "2017-10-20", "20171-10-20", "201712-10-20", "2017123-10-20" };
for (String dateStr : dateStrArr) {
System.out.println(LocalDate.parse(dateStr, formatter));
}
}
}
Output:
2017-10-20
+20171-10-20
+201712-10-20
+2017123-10-20
Original answer
You can use the pattern [uuuu][u]-MM-dd where [uuuu] conforms to a 4-digit year and [u] can cater to the requirement of any number of digits allowed for a year.
Demo:
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
public class Main {
public static void main(String[] args) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("[uuuu][u]-MM-dd");
String[] dateStrArr = { "2017-10-20", "20171-10-20", "201712-10-20", "2017123-10-20" };
for (String dateStr : dateStrArr) {
System.out.println(LocalDate.parse(dateStr, formatter));
}
}
}
Output:
2017-10-20
+20171-10-20
+201712-10-20
+2017123-10-20

How to parse time in any format with LocalTime.parse?

I am having trouble using java's LocalTime to parse a string with hours, minutes, and seconds.
LocalTime t = LocalTime.parse("8:30:17"); // Simplification
This throws the following exception:
Exception in thread "main" java.time.format.DateTimeParseException:
Text '8:30:17' could not be parsed at index 0
The default formatter expects an ISO format, which uses 2 digits for each of the hours, minutes and seconds.
If you want to parse the time you showed, which only has one digit for hours, you will need to provide a custom formatter (note the single H):
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("H:mm:ss");
LocalTime t = LocalTime.parse(times.get(i), formatter);
You'll need to pass in a custom DateTimeFormatter like this:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("H:mm:ss");
LocalTime t = LocalTime.parse(times.get(i), formatter);
Take a look at the docs, as the letters you need to use might be different.
You need to use DateTimeFormatter to give parser a format pattern to parse.
DateTimeFormatter formatter =DateTimeFormatter.ofPattern("H:mm:ss");
LocalTime t = LocalTime.parse(times.get(i), formatter);
Format Pattern Letters:
H hour-of-day (0-23)
m minute-of-hour
s second-of-minute
h clock-hour-of-am-pm (1-12)
From LocalTime.parse documentation :
The string must represent a valid time and is parsed using
DateTimeFormatter.ISO_LOCAL_TIME.
According to the ISO_LOCAL_TIME documentation the condition for the hours is this:
Two digits for the hour-of-day. This is pre-padded by zero to ensure
two digits.
You are parsing the value 8:30:17, one digit instead of two digits and so you are breaking the condition, causing the error.
By default, LocalTime#parse parses the input string using DateTimeFormatter.ISO_LOCAL_TIME whose format consists of:
Two digits for the hour-of-day. This is pre-padded by zero to ensure two digits.
A colon
Two digits for the minute-of-hour. This is pre-padded by zero to ensure two digits.
If the second-of-minute is not available then the format is complete.
A colon
Two digits for the second-of-minute. This is pre-padded by zero to ensure two digits.
If the nano-of-second is zero or not available then the format is complete.
A decimal point
One to nine digits for the nano-of-second. As many digits will be output as required.
Since your input string is not in this format, you have encountered the exception. You can get around the exception by using a DateTimeFormatter which allows you to specify a custom format. You can use H:m:s as the format.
Demo:
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class Main {
public static void main(String[] args) {
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("H:m:s", Locale.ENGLISH);
LocalTime t = LocalTime.parse("8:30:17", dtf);
System.out.println(t);
}
}
Output:
08:30:17
ONLINE DEMO
Note that for formatting, the single letter, H works for single as well as a two-digit hour. Similar is the case with m and s as well.
Another thing you should always keep in mind is that the Date-Time formatting types are Locale-sensitive and therefore you should always specify the applicable Locale when using them. Check Never use SimpleDateFormat or DateTimeFormatter without a Locale to learn more about it.
Learn more about the modern Date-Time API* from Trail: Date Time.
* If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring. Note that Android 8.0 Oreo already provides support for java.time.

Java SimpleDateFormat.format() returns incorrect date when using miliseconds

I want to parse a date string from "2016-09-23T09:14:52.555000000" format to "23-SEP-2016" format.
Here is my code :
public class Tester {
public static void main(String[] args) {
System.out.println(displayDate("2016-09-23T09:14:52.555000000"));
System.out.println(displayDate("2016-09-28T11:56:24.552000000"));
System.out.println(displayDate("2016-09-23T09:29:12.507000000"));
System.out.println(displayDate("2016-09-26T14:55:02.702000000"));
System.out.println(displayDate("2016-09-26T09:50:24.880000000"));
System.out.println(displayDate("2016-09-26T15:20:49.397000000"));
System.out.println(displayDate("2016-09-26T15:21:21.559000000"));
}
public static String displayDate(String dateString) {
String formattedDateString = "NA";
if(dateString == null) {
return formattedDateString;
}
SimpleDateFormat oracleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'kk:mm:ss.S");
SimpleDateFormat dateFormatToDisplay = new SimpleDateFormat("dd-MMM-yyyy");
try {
Date date = oracleDateFormat.parse(dateString);
formattedDateString = dateFormatToDisplay.format(date).toUpperCase();
} catch (ParseException e) {
e.printStackTrace();
}
return formattedDateString;
}
}
The problem is if I use this line
SimpleDateFormat oracleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'kk:mm:ss.S");
The output is (incorrect date values) :
29-SEP-2016
04-OCT-2016
29-SEP-2016
04-OCT-2016
06-OCT-2016
01-OCT-2016
03-OCT-2016
Whereas If use this line
SimpleDateFormat oracleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'kk:mm:ss");
The output is (correct date values)
23-SEP-2016
28-SEP-2016
23-SEP-2016
26-SEP-2016
26-SEP-2016
26-SEP-2016
26-SEP-2016
I want to know why adding "S (Millisecond)" to the format string results in incorrect values.
Edit1 :
Even "yyyy-MM-dd'T'kk:mm:ss.SSS" returns incorrect values.
Edit2 :
I am using this code in Android App. It works on perfectly on emulator(API 23). For some devices it displays incorrect date. Can it be related to Java version?
TL;DR:
In Java, it's not a fraction of seconds, it's a number of milliseconds. Don't make it larger than 999.
Change your date format in Oracle to include FF3 for the fractions of seconds to yield milliseconds.
Explanation
In Java, the S, or rather SSS, in your date format stands for milliseconds, which are thousands of a second. There are only thousand milliseconds in one second.
In Oracle, the date format doesn't specify milliseconds, but fractions of a second.
FF [1..9]
Fractional seconds; no radix character is printed. Use the X format element to add the radix character. Use the numbers 1 to 9 after FF to specify the number of digits in the fractional second portion of the datetime value returned. If you do not specify a digit, then Oracle Database uses the precision specified for the datetime datatype or the datatype's default precision. Valid in timestamp and interval formats, but not in DATE formats.
In Java, if you need a third of a second, you can't get more precise than 333 milliseconds. In Oracle however, you could express it as 333333 microseconds, or perhaps even 333333333 nanoseconds.
Oracle lets you specify the number of decimal digits you want, but if you don't, you get as much as the precision for the type allows. In your case, that seems to be 9.
Then your date format in Java interprets that as a number of milliseconds. Millions and billions of them. These are added to the rest of your date. Since there are only 86,400,000 milliseconds in a day, anything over that is another day added to your date.
Example
Let's take a look at your first test case, 2016-09-23T09:14:52.555000000.
555000000 milliseconds = 555000 seconds &approx; 154 hours &approx; 6 days and 10 hours.
Adding 6 days and 10 hours to the rest of your date, which is 2016-09-23 09:14:52, should get you to about 2016-09-29 19:00 and a bit. Change your output format (dateFormatToDisplay) to include the hours and you'll see what's happening.
Fix
Your Java date format expects no more than 3 digits for the milliseconds. Specify the number of fractional digits in Oracle. FF uses the maximal precision available for the type, FF3 only outputs 3 fractional digits — milliseconds.
If you can't alter the date format used in Oracle, trim it down to three decimal digits in Java. Note that anything less than 3 digits should be padded with zeroes to a length of three digits; 34.12 is interpreted as 34 seconds and 12 milliseconds, while you might be looking for 120 milliseconds.
The mystery stays in the correct interpretation of the S specifier.
In SimpleDateFormat, on the Date and Time Patterns section, the definition for S states:
S Millisecond
Does it strikes you as peculiar in some way? No?! It took myself by surprise as well, so let me put it in a negative way:
Not fractional part of the seconds, but Milliseconds
Still don't get it? I mean literaly the count of milliseconds.
Like in:
SimpleDateFormat oracleDateFormat =
new SimpleDateFormat("yyyy-MM-dd'T'kk:mm:ss.S");
SimpleDateFormat dateFormatToDisplay =
new SimpleDateFormat("dd-MMM-yyyy--HH:mm:ss.SSS");
Date d=oracleDateFormat.parse("2016-09-23T09:14:52.1");
System.out.println(dateFormatToDisplay.format(d));
will result in... waaait for it... boom-tshhh...
"2016-09-23--09:14:52.001"
That's right folks! "And one millisecond" not "and one tenth of a second".
So, does it go the same way if we increase the number of digits after the dot? Sure it does.
d=oracleDateFormat.parse("2016-09-23T09:14:52.1000");
System.out.println(dateFormatToDisplay.format(d));
"2016-09-23--09:14:53.000"
So if you put in T09:14:53.555000000 you just added 555 millions of milliseconds to your base time or 555000 full seconds. Which means an extra 6 days, 10 hours and 11 minutes` over your base time.
It seems that when you add 'S' then parser adds all milliseconds eg "555000000" to date. When you do calculation (555000000 ms ~ 6,4d) it matches result (incorrect date).
You should use "yyyy-MM-dd HH:mm:ss.SSS", but also trim "2016-09-23T09:14:52.555000000" to "2016-09-23T09:14:52.555", eg:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
sdf.parse("2016-09-23T09:14:52.555000000".substring(0,23));
Milliseconds should be represented in 3 digits only. Change code to:
System.out.println(displayDate("2016-09-23T09:14:52.555"));
System.out.println(displayDate("2016-09-28T11:56:24.552"));
System.out.println(displayDate("2016-09-23T09:29:12.507"));
System.out.println(displayDate("2016-09-26T14:55:02.702"));
System.out.println(displayDate("2016-09-26T09:50:24.880"));
System.out.println(displayDate("2016-09-26T15:20:49.397"));
System.out.println(displayDate("2016-09-26T15:21:21.559"));
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");

Timestamp in ISO 8601 - the last 6 digits yyyy-MM-dd'T'HH:mm:ss.?

I have timestamps looking like this:
2015-03-21T11:08:14.859831
2015-03-21T11:07:22.956087
I read a Wiki article on ISO 8601, but did not get the meaning of the last 6 digits here.
I tried getting it down to milliseconds using "yyyy-MM-dd'T'HH:mm:ss.sss" or "yyyy-MM-dd'T'HH:mm:ss.ssssss". Is it just more precise than milliseconds - up to microseconds?
Is it just more precise than milliseconds?
Yes, it's microseconds in this case.
ISO-8601 doesn't actually specify a maximum precision. It states:
If necessary for a particular application a decimal fraction of hour, minute or second may be included. If a decimal fraction is included, lower order time elements (if any) shall be omitted and the decimal fraction shall be divided from the integer part by the decimal sign specified in ISO 31-0, i.e. the comma [,] or full stop [.]. Of these, the comma is the preferred sign. If the magnitude of the number is less than unity, the decimal sign shall be preceded by two zeros in accordance with 3.6.
The interchange parties, dependent upon the application, shall agree the number of digits in the decimal fraction. [...]
(You very rarely actually see comma as the decimal separator - at least, that's my experience.)
Unfortunately in my experience, parsing a value like this in Java 7 is tricky - there isn't a format specifier for "just consume fractional digits and do the right thing". You may find you need to manually chop the trailing 3 digits off before parsing as milliseconds.
As Java 8 supports a precision of nanoseconds, it's rather simpler - and in fact, the built-in ISO formatter can parse it fine:
import java.time.*;
import java.time.format.*;
public class Test {
public static void main(String[] args) {
DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE_TIME;
System.out.println(LocalDateTime.parse("2015-03-21T11:07:22.956087", formatter));
}
}
You do not need a DateTimeFormatter
java.time API is based on ISO 8601 and therefore you do not need a DateTimeFormatter to parse a date-time string which is already in ISO 8601 format (e.g. your date-time string, 2015-03-21T11:08:14.859831).
Demo:
import java.time.LocalDateTime;
class Main {
public static void main(String args[]) {
String strDateTime = "2015-03-21T11:08:14.859831";
LocalDateTime ldt = LocalDateTime.parse(strDateTime);
System.out.println(ldt);
}
}
Output:
2015-03-21T11:08:14.859831
I read a Wiki article on ISO 8601, but did not get the meaning of the
last 6 digits here.
It is a fraction of a second, to microsecond resolution.
I tried getting it down to milliseconds using
"yyyy-MM-dd'T'HH:mm:ss.sss" or "yyyy-MM-dd'T'HH:mm:ss.ssssss".
Your pattern is not correct. The correct letter for the fraction of second is S (capital S) i.e. if you want the value to the millisecond resolution, the pattern should be yyyy-MM-dd'T'HH:mm:ss.SSS. Check the DateTimeFormatter documentation to learn more about this.
Is it just more precise than milliseconds - up to microseconds?
Yes, it is. The java.time API can give you an even more precise resolution, to nanoseconds (depending on the system clock).
Learn more about the modern Date-Time API from Trail: Date Time.

Categories

Resources