How to create proper DateTimeFormatter Pattern - java

I am trying to create a DateTimeFormatter object with a pattern to fit this expression of time: 2016-07-22T00:00:00.000-05:00. I am trying to create a DateTime object using the DateTimeFormatter class with the above input string.
I have tried many different versions of the below expression but am currently getting stuck at the timezone piece "-05:00" where I'm getting the error on my junit test case:
java.lang.IllegalArgumentException: Invalid format: "2016-07-22T00:00:00.000-05:00" is malformed at "-05:00"
The current format pattern that I am using is:
yyyy-MM-dd'T'HH:mm:ss.SSSZ
I have also tried:
yyyy-MM-dd'T'HH:mm:ss.SSSTZD
yyyy-MM-dd'T'HH:mm:ss.SSSZZZ
yyyy-MM-dd'T'HH:mm:ss.SSSz
yyyy-MM-dd'T'HH:mm:ss.SSSzzz
yyyy-MM-dd'T'HH:mm:ss.SSS'TZD'
I am running on Java 7 so I am not sure if that is causing an issue as well.

In order to achieve what you wish, you can utilize the static method "ofPattern" in the DateTimeFormatter class. This method returns a DateTimeFormatter object.
And as shown by tnas, you could use the following date and time format string:
"yyyy-MM-dd'T'HH:mm:ss.SSSXXX"
DateTimeFormatter test = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
I tested the code and it compiles.

Late to the party, but you have to include some timezone information in your timestamp string. Otherwise it would be undefined from which timezone you'll want to substract your offset of five hours.
Assuming that you'll want to parse a timestamp which is 5 hours behind UTC, your string should read
2016-07-22T00:00:00.000Z-05:00
Note the 'Z' before the -05:00 part, which is short for "UTC"

DateTimeFormatter from "yyyy-MM-dd'T'HH:mm:ssX" worked for me.

The API's javadoc describe the patterns: https://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html
I've tested this code:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
Date date = new Date();
System.out.println(sdf.format(date));
The output was:
2016-08-22T18:34:26.604-03:00

Related

Converting Joda DateTime to JavaTime

I am trying to update my existing elasticsearch springboot project and as the source code is fairly old it still uses joda time. Now I have to upgrade all the functions of Joda time to java time. Now in the project We use Date Time of Joda Time
Code Sample for Joda Time
DateTime target = new DateTime(String targetDate, UTC);
We use this function currently in our code to convert a String to Date.
Using this function the String
2022-10-01T00:00:00.000
gets converted to
2022-10-01T00:00:00.000Z
I am trying to replicate the same in java time.
I tried to parse the targetDate using OffsetDateTime and ZonedDateTime but both gave me errors.
Text '2022-10-01T00:00:00.0000' could not be parsed at index 24
After some attempts I was able to move forward by using LocalDateTime
LocalDateTime target = LocalDateTime.parse(String targetDate);
Which was able to parse the String but the format was not correct the format I got was
2022-10-01T00:00Z
I also tried using the formatter with LocalDateTime
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSS'Z'");
LocalDateTime target= LocalDateTime.parse(targetDate,formatter);
But This Still gave me the Error
Text '2022-10-01T00:00:00.0000' could not be parsed at index 24
Now I am a bit confused regarding this.
Any help is appreciated.
And Please correct me if my way of asking question or formatting is wrong at any point still new to this.
Regards.
EDIT: Sorry for the confusion but as pointed out I should have mentioned that I want the returned value as the java.time datetime object and not a String so that I can further perform some logic on it. Sorry for this.
Thanks and Regards
The String "2022-10-01T00:00:00.000" can be parsed to a LocalDateTime because it only consists of year, month of year, day of month, hour of day, minute of hour, second of minute and fractions of second.
Your desired output String "2022-10-01T00:00:00.000Z" represents the same values plus an offset, the Z for Zulu time, which basically means UTC.
If you want to add an offset to the input String with java.time, you can parse it to a LocalDateTime and then append the desired offset, which results in an OffsetDateTime. You can print that in a desired format using a DateTimeFormatter, either use a prebuilt one or define one yourself.
Here's a small example:
public static void main(String[] args) {
// input value
String dateTime = "2022-10-01T00:00:00.000";
// parse it and append an offset
OffsetDateTime odt = LocalDateTime.parse(dateTime).atOffset(ZoneOffset.UTC);
// define a formatter that formats as desired
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSX");
// and print the OffsetDateTime using that formatter
System.out.println(odt.format(dtf));
}
Output:
2022-10-01T00:00:00.000Z
Clarification update:
There is just a single instance OffsetDateTime in my example with the following values:
year (2022)
month of year (10)
day of month (1)
hour of day (0)
minute of hour (0)
second of minute (0)
franctions of second (0)
offset (UTC / +00:00)
This instance of OffsetDateTime can be used for calculations (e.g. add/subtract days, months or other units) and it can be formatted as String. It also has a toString() method we don't have under control, but is used if you don't explicitly format it.
The following lines (first one is the last of my example above) show some different usages:
// print formatted by the DateTimeFormatter from the above example
System.out.println(odt.format(dtf));
// print the object directly, implicitly using its toString()
System.out.println(odt);
// print formatted by a prebuilt DateTimeFormatter (several are available)
System.out.println(odt.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME));
// print with a formatter that uses locale dependant expressions like month names
System.out.println(odt.format(
DateTimeFormatter.ofPattern("EEEE, MMM dd HH:mm:ss xxx",
Locale.ENGLISH)));
the last one also uses a different representation for UTC: instead of Z it shows the offset in hours and minutes.
Output:
2022-10-01T00:00:00.000Z
2022-10-01T00:00Z
2022-10-01T00:00:00Z
Saturday, Oct 01 00:00:00 +00:00

Single class to parse any Date Format in Java

I have been parsing dates in the below formats. I maintain an array of these formats and parse every date string in all these formats.
The code I used was -
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormat);
simpleDateFormat.setTimeZone(timeZone); //timeZone is a java.util.TimeZone object
Date date = simpleDateFormat.parse(dateString);
Now I want to parse yyyy-MM-dd'T'HH:mm:ss.SSSSSSXXX format as well but using SimpleDateFormat the 6 digit microseconds are not considered. So I looked into java.time package.
To parse yyyy-MM-dd'T'HH:mm:ss.SSSSSSXXX formats I will be needing OffsetDateTime class and for other formats, I need ZonedDateTime class. The format will be set in DateTimeFormatter class.
Is there a way to use a single class like SimpleDateFormat to pass all the formats?
Since your Java 8 doesn’t behave as would be reasonably expected, I suggest that a workaround is trying to parse without zone first. If a zone or an offset is parsed from the string, this will be used. If the parsing without zone fails, try with a zone. The following method does that:
private static void parseAndPrint(String formatPattern, String dateTimeString) {
// Try parsing without zone first
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(formatPattern);
Instant parsedInstant;
try {
parsedInstant = formatter.parse(dateTimeString, Instant::from);
} catch (DateTimeParseException dtpe) {
// Try parsing with zone
ZoneId defaultZone = ZoneId.of("Asia/Calcutta");
formatter = formatter.withZone(defaultZone);
parsedInstant = formatter.parse(dateTimeString, Instant::from);
}
System.out.println("Parsed instant: " + parsedInstant);
}
Let’s try it:
parseAndPrint("yyyy-MM-dd'T'HH:mm:ss.SSSSSSXXX", "2018-10-22T02:17:58.717853Z");
parseAndPrint("yyyy-MM-dd'T'HH:mm:ss.SSSSSS", "2018-10-22T02:17:58.717853");
parseAndPrint("EEE MMM d HH:mm:ss zzz yyyy", "Mon Oct 22 02:17:58 CEST 2018");
Output on Java 8 is:
Parsed instant: 2018-10-22T02:17:58.717853Z
Parsed instant: 2018-10-21T20:47:58.717853Z
Parsed instant: 2018-10-22T00:17:58Z
The first example has an offset in the string and the last a time zone abbreviation in the string, and in both cases are these respected: the instant printed has adjusted the time into UTC (since an Instant always prints in UTC, its toString method makes sure). The middle example has got neither offset nor time zone in the string, so uses the default time zone of Asia/Calcutta specified in the method.
That said, parsing a three or four letter time zone abbreviation like CEST is a dangerous and discouraged practice since the abbreviations are often ambiguous. I included the example for demonstration only.
Is there a way to use a single class…?
I have used Instant for all cases, so yes there is a way to use just one class. The limitation is that you do not know afterward whether any time zone or offset was in the string nor what it was. You didn’t know when you were using SimpleDateFormat and Date either, so I figured it was OK?
A bug in Java 8?
The results from your demonstration on REX tester are disappointing and wrong and do not agree with the results I got on Java 11. It seems to me that you have been hit by a bug in Java 8, possibly this one: Parsing with DateTimeFormatter.withZone does not behave as described in javadocs.

Parse time with microseconds in Java

I am having problems parsing time strings in Java that are in the format of 2013-01-09 09:15:03.000000. In my data, the last three digits are always 0 (meaning the input strings have only millisecond precision), so I passed this format to SimpleDateFormat:
formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS'000'");
but formatter.parse("2013-01-09 09:15:02.500000"); throws an exception:
Unparseable date: "2013-01-09 09:15:02.500000"
at java.text.DateFormat.parse(DateFormat.java:357)
Anyone knows how to do it correctly? I can work around by using format yyyy-MM-dd HH:mm:ss.SSS and using substring to get rid of last three digits but that's really hacky.
EDIT: can anyone explain why the format string yyyy-MM-dd HH:mm:ss.SSS'000' can't be used to parse time "2013-01-09 09:15:02.500000"
try java.sql.Timestamp
Timestamp ts = Timestamp.valueOf("2013-01-09 09:15:03.500000");
Date date = new Date(ts.getTime())
it's also thread-safe and fast as opposed to SimpleDateFormat
java.time
I should like to contribute the modern answer. Use java.time, the modern Java date and time API. One option, you may use a formatter:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss.SSSSSS");
LocalDateTime dateTime = LocalDateTime.parse(timeString, formatter);
System.out.println(dateTime);
When using the string from your question, "2013-01-09 09:15:02.500000", this printed:
2013-01-09T09:15:02.500
If you want the value printed with six decimals on the seconds even when the last three decimals are 0, use the same formatter to format the time back into a string:
System.out.println(dateTime.format(formatter));
The other option, you may exploit the fact that your string resembles the ISO 8601 format, the format that the modern classes parse as their default, that is, without any explicit formatter. Only ISO 8601 has a T to denote the start of the time part, but we can fix that easily:
LocalDateTime dateTime = LocalDateTime.parse(timeString.replace(' ', 'T'));
It gives the same result, 2013-01-09T09:15:02.500. It’s shorter, but also more tricky.
Why bother?
The classes Date and Timestamp are long outdated, and SimpleDateFormat in particular has proven troublesome. Its surprising behaviour in your situation is just one little story out of very many. The modern API is generally so much nicer to work with.
Why didn’t your formatter work?
While the format pattern strings used by SimpleDateFormat and DateTimeFormatter are similar, there are differences. One is that SimpleDateFormat understands uppercase S as milliseconds no matter of there are one or nine of them, whereas to DateTimeFormatter they mean fraction of second. Your SimpleDateFormat furthermore grabbed all six digits after the decimal point, ignoring the fact that you had typed only three S, so there were no zeroes left to match the '000' (by the way, the apostrophes are not necessary, only letters need them).
Link
Oracle Tutorial
I've figured out myself. Just FYI, Apache commons' FastDateFormat seems accepting the SSS000 format and parses the time correctly.

Convert String to Date in java - Time zone

I have a String, 2013-10-07T23:59:51.205-07:00, want to convert this to Java date object. I am getting parsing error.
date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").parse("2013-10-07T23:59:51.205-07:00");
try
date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
.parse("2013-10-07T23:59:51.205-0700");
The Z is not a literal and the timezone does not have a colon
See the examples at http://docs.oracle.com/javase/6/docs/api/java/text/SimpleDateFormat.html
If java7 is being used then Z can be replaced with X and the timezone can have a colon
Z shouldn't be inside quotes. I don't think Z would work for your given timezone. Before Java 7, I guess there wasn't any format to parse ISO 8601 format timezone with colon in between. You should use -0700 instead.
However, from Java 7 onwards, you have an option for parsing ISO 8601 format timezone using X instead of Z. See javadoc for SimpleDateFormat. Just use the following format:
// This would work from Java 7 onwards
date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX")
.parse("2013-10-07T23:59:51.205-07:00");
Your pattern is wrong, you should use the following:
date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX")
.parse("2013-10-07T23:59:51.205-07:00");
The 'X' indicates the Time zone in the ISO 8601 format as expressed in your String here: '.205-07:00'
For more information read the doc: SimpleDateFormat
Use this trick to parse ISO8601 datetime format. I admit have not tried this with millisecond part within a string value maybe it gives you an extra headache. This works for Java6.
import javax.xml.bind.DatatypeConverter;
Calendar cal = DatatypeConverter.parseDateTime(strDatetime);
If am remembering correct cal instance may not use a system-default timezone. Its initialized to the origin string value timezone. If you want instance to use system timezone you can do this conversion.
long ts = cal.getTimeInMillis();
cal = Calendar.getInstance();
cal.setTimeInMillis(ts);
You should use XXX for the format -07:00, instead of Z and X.
Date sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX")
.parse("2013-10-07T23:59:51.205-07:00");
Look at the example of this docs.
The problem is that -07:00 is not a valid Time zone . The Time Zone should have this format, for example something like -0800.

Datetime Parsing - Unable to get back original value

DateTimeFormatter DATEFORMAT = ISODateTimeFormat.dateTimeNoMillis();
String testDate = "2013-10-26T04:23:14-08:00";
System.out.println(testDate);
System.out.println(DATEFORMAT.parseDateTime(testDate).toString(DATEFORMAT));
Output:
2013-10-26T04:23:14-08:00
2013-10-26T05:23:14-07:00
Why are outputs off by around an hour ? Is there any other parameter I need to specify for converting DateTime back to string ?
Any help is much appreciated.
I'm assuming this is using the joda-time library. As the comments above suggest, it's a TZ issue. You can easily convert any of the times into the TZ of your choice or UTC through a method call on the DateTime object. Something along the lines of:
dt.withZone(DateTimeZone.UTC);
You're getting different results since the default behavior is to print the DateTime in the system's current timezone.

Categories