java format timestamps with "Z" instead of "+" - java

Using java I try to format the current date with the timezone using SimpleDateFormat
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss:SSSZ");
sdf.format(new Date());
This give me as results :
2021-04-28T13:45:52:308+0300
I want to get the timezone format with the "Z" instead of "+"
wanted results : "2021-04-28T13:45:52:308Z03:00"
I writed the date output in a file log that will be parsed by telegraf plugin writed in Go language that expect date with time zone with the following format : json_time_format = "2006-01-02T15:04:05Z07:00"
Is there a pattern allows that ?

You misunderstood. 2006-01-02T15:04:05Z07:00 does not mean that you should have a Z instead of a plus (what would you put instead of a minus, then?) This way of specifying a date and time format approximates how the fixed example date and time of Mon Jan 2 15:04:05 MST 2006 would be formatted, but it’s only an approximation. Specifically when it comes to the offset from UTC, the format requires Z when the offset is zero and +hh:mm or -hh:mm when it is non-zero. In accordance with ISO 8601 and RFC-3339. You see immediately that just giving the correct formatting of the example date and time, 2006-01-02T15:04:05-07:00, would not tell the reader that offset 0 should be given as Z. Therefore this particular requirement is specified as Z07:00 in the format. According to Format a time or date [complete guide] (link at the bottom), your particular format, 2006-01-02T15:04:05-0700, denotes ISO 8601 or RFC-3339.
So all you need to do is use DateTimeFormat.ISO_OFFSET_DATE_TIME or OffsetDateTime.toString().
A couple of examples follow.
String result = OffsetDateTime.now().toString();
System.out.println(result);
Output when running on Java 8 in my time zone just now:
2021-04-29T17:00:55.716+02:00
If the fraction of second is not allowed — well, according to ISO 8601 it is optional, so it should be, but if not:
String result = OffsetDateTime.now().truncatedTo(ChronoUnit.SECONDS)
.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
2021-04-29T17:00:55+02:00
If you have got an old-fashioned Date object from legacy code, convert it before formatting:
Date oldfashionedDate = new Date();
String result = oldfashionedDate.toInstant()
.atZone(ZoneId.systemDefault())
.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
2021-04-29T17:00:55.739+02:00
Links
Oracle tutorial: Date Time explaining how to use java.time.
Wikipedia article: ISO 8601
Format a time or date [complete guide]

Z is for "Zulu time" or zero hour offset, i.e. UTC +0:00
It's not correct to use it if you're not in that timezone. How would you know whether you're before or after the meridian if you replace it with Z? Given Z03:00 do you parse it as +03:00 or -03:00?

Since Z means Zulu time offset you can't use it as part of the format string but you can of course add Z as a hardcoded character
ZonedDateTime now = ZonedDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'Z");
System.out.println(formatter.format(now));
2021-04-29T15:34:17.661Z+0200
Then if you don't want the '+' you can remove it afterwards but it is not clear on what to do whit a '-' so I left it out of the answer.

Semantically as mentioned above its quite wrong.But you can achieve this with some custom parsing logic.
I will assume 2 things:
The date will not contain timezones with negative differences
The date format will not change
In any other cases, this is not safe!!
But here you go:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss:SSSZ");
String originalDateString = sdf.format(new Date());
String[] parts = originalDateString.split("\\+");
StringBuilder sb = new StringBuilder(parts[1]);
sb.insert(2, ':');
parts[1] = sb.toString();
String result = String.join("Z",parts);
System.out.println(result);
This will create from this:
2021-04-29T14:12:21:376+0000
This:
2021-04-29T14:12:21:376Z00:00

Related

Convert or format string to date

I am struggling with this ..
I have an input string - like this: 2021-10-13 11:33:16.000-04
Using Java.
I need to get a Date object from it.
which formatting pattern can I use ?
I try with these
SimpleDateFormat inFormatter = new SimpleDateFormat ("yyyy-MM-dd HH:mm:ss.SSS'-'ZZ");
and
SimpleDateFormat inFormatter = new SimpleDateFormat ("yyyy-MM-dd HH:mm:ss.SSSZZ");
and I keep getting
java.text.ParseException: Unparseable date: "2021-10-13 11:33:16.000-04"
at java.base/java.text.DateFormat.parse(DateFormat.java:396)
at com.dima.tests.DatesConversions.main(DatesConversions.java:24)
Please, help !!
Don't use Date as it is outdated. Use the classes in the java.time
OffsetDateTime odt = OffsetDateTime.parse(str,
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSX"));
System.out.println(odt);
Prints
2021-10-13T11:33:16-04:00
java.time
Even though you need to give an old-fashionede Date object to a legacy API beyond your control, I still recommend that you use java.time, the modern Java date and time API, in your own code. The final conversion to Date is pretty straight-forward.
I’d use this formatter for maximum reuse of existing formatters:
private static final DateTimeFormatter PARSER = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ISO_LOCAL_DATE)
.appendLiteral(' ')
.append(DateTimeFormatter.ISO_LOCAL_TIME)
.appendOffset("+HHmm", "+00")
.toFormatter(Locale.ROOT);
Then we parse and convert like this:
String input = "2021-10-13 11:33:16.000-04";
OffsetDateTime dateTime = OffsetDateTime.parse(input, PARSER);
System.out.println(dateTime);
Instant i = dateTime.toInstant();
Date oldfashionedDate = Date.from(i);
System.out.println(oldfashionedDate);
Output in my time zone, Europe/Copenhagen:
2021-10-13T11:33:16-04:00
Wed Oct 13 17:33:16 CEST 2021
Denmark is at offset +02:00 at this time of year, so 6 hours ahead of the UTC offset -04 from your string. Therefore Date.toString() confusingly prints a clock hour that is 6 hours ahead of the original time of day.
Note: if your forward service accepts anything else than an old-fashioned Date, you should not be using that class. For example, if a String is required, the OffsetDateTime that we got can be formatted into a new string using a second DateTimeFormatter (or in lucky cases, its toString method).
What went wrong in your code?
First, a UTC offset can have positive or negative sign. Instead of -04 you could have had for example +09. Formatters are designed for to take the sign, + or -, as part of the offset. Therefore hardcoding the minus sign as a literal, as in your first attempt, is bound to fail. In your second attempt, yyyy-MM-dd HH:mm:ss.SSSZZ, you are already closer. However, ZZ is for an offset with sign and four digits (like +0530 or -0400; hour and minute), so does not work for a two-digit offset like -04. Your SimpleDateFormat expected more digits where your string ended and therefore threw the exception that you saw.
Link
Oracle tutorial: Date Time explaining how to use java.time.
Since you are using ISO 8601 time zone timezone, you have the use the below pattern.
SimpleDateFormat inFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSX");
And then, to get the date:
Date date = inFormatter.parse("2021-10-13 11:33:16.000-04");
Always check the documentation.

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.

Convert String to Date in format mm/dd/yyyy HH:mm-HH-mm Zone

I have a date in the format 07/18/2018 01:00-04:00 CDT in a String variable.
where 01:00-04:00 is a time range.
I need convert this into a date format in two separate variables keeping timezone in mind.
Var1 should have 07/18/2018 01:00 CDT
Var2 should have 07/18/2018 04:00 CDT
Should i tokenize this string and seperate out 01:00-04:00? or is there a way in simpleDateParser to this?
I have a date in the format 07/18/2018 01:00-04:00 CDT in a String variable.
Since the input string is not in standard date/time format, you need split the string to two strings then parse them separatly:
String input = "07/18/2018 01:00-04:00 CDT";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM/dd/yyyy HH:mm z");
// parse 07/18/2018 01:00 CDT
ZonedDateTime first = ZonedDateTime.parse(input.replaceAll("-\\d{2}:\\d{2}", ""), formatter);
// parse 07/18/2018 04:00 CDT
ZonedDateTime second = ZonedDateTime.parse(input.replaceAll("\\d{2}:\\d{2}-", ""), formatter);
The problem is that what you have is not a datetime. It is an interval. A datetime formatter is not going to be able to cope with it. The formatter APIs produce a Date object (for the legacy DateFormat classes) or a TemporalAccessor object (for DateTimeFormatter). There is no direct support for intervals in either the current or legacy Java date / time APIs.
Given that the format / formatter classes can't return an interval, it is not surprising that the format strings don't support this1.
So, the only alternative is to do some parsing / string bashing yourself. #Sun's answer is one solution.
1 - If you could write a format / formatter pattern that tell the parser to "ignore the next 5 characters" for example, you could create two different formatters that parsed your example to extract the first datetime and the second datetime. But you can't. One reason is that this approach would not work for formatting.

How to convert String to Date with TimeZone?

I'm trying to convert my String in Date + Timezone.
I get my String from a DateTime Variable (here: xyz).
My code:
String abc = xyz.toString("yyyy-MM-ddZZ");
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-ddXXX");
java.util.Date date = sdf.parse(abc);
System.out.println("Date: " + sdf.format(date));
Error:
Invalid format: "2017-01-03+01:00" is malformed at "+01:00"
If I try SimpleDateFormat("yyyy-MM-dd"); it works but without the Timezone ("+01:00")
The input has a date - year, month, day - and an offset - the difference from UTC - but to build a java.util.Date, you also need the time: hour, minutes, seconds, fraction of seconds.
SimpleDateFormat is terrible because it does some "magic", setting the missing fields to default values. Another problem is that the X pattern doesn't work for all Java versions, and the documentation sucks.
You can use the new Java 8 classes, as explained. With them, you can parse the input, choose the default values to be used for the time fields and convert to java.util.Date, if that's what you need:
DateTimeFormatter fmt = new DateTimeFormatterBuilder().append(DateTimeFormatter.ISO_OFFSET_DATE)
// set hour to midnight
.parseDefaulting(ChronoField.HOUR_OF_DAY, 0).toFormatter();
OffsetDateTime odt = OffsetDateTime.parse("2017-01-03+01:00", fmt); // 2017-01-03T00:00+01:00
The OffsetDateTime will have the time set to midnight, but you can change it to whatever values you need, while with SimpleDateFormat it's not possible, because it uses internal default values and you can't control it.
And the date and offset were correctly set to the values in the input string. You can then convert to java.util.Date if you want:
Date date = Date.from(odt.toInstant());
You can also get the individual "pieces" of the date if you want:
// get just the date
LocalDate localDate = odt.toLocalDate(); // 2017-01-03
// get just the offset
ZoneOffset offset = odt.getOffset(); // +01:00
PS: the offset +01:00 is not the same thing as a timezone. See the difference here
String abc = "2017-01-03+01:00";
TemporalAccessor parsed = DateTimeFormatter.ISO_OFFSET_DATE.parse(abc);
LocalDate date = LocalDate.from(parsed);
ZoneOffset offset = ZoneOffset.from(parsed);
System.out.println("Date: " + date + "; offset: " + offset + '.');
This prints:
Date: 2017-01-03; offset: +01:00.
I am using java.time, the modern Java date and time API, and recommend you do the same. The Date class is long outdated (sorry, no pun intended) and SimpleDateFormat in particular notoriously troublesome. Don’t use them. The modern API is so much nicer to work with. Only if you need a java.util.Date and/or a java.util.TimeZone for a legacy API that you cannot change, convert like this:
Date oldfashionedDate = Date.from(date.atStartOfDay(offset).toInstant());
TimeZone oldfashionedTimeZone = TimeZone.getTimeZone(offset);
System.out.println("Old-fashioned date: " + oldfashionedDate
+ "; old-fashioned time-zone: " + oldfashionedTimeZone.getDisplayName() + '.');
On my computer this prints:
Old-fashioned date: Tue Jan 03 00:00:00 CET 2017; old-fashioned time-zone: GMT+01:00.
I happen to be in a time zone that agrees with your offset from UTC, so it’s fairly obvious that the conversion has given the correct result. In other time zones the output will be more confusing because Date.toString() uses the JVM’s time zone setting for generating the string, but the Date will still be correct.
A date with a time zone? Neither a LocalDate nor a Date can hold a time zone in them, so you need to have the offset information separately. Interestingly your string seems to follow a “ISO-8601-like” format for an offset date that is even represented by a built-in formatter that has ISO in its name. If Java had contained an OffsetDate or a ZonedDate class, I would have expected such a class to parse your string into just one object and even without an explicit formatter. Unfortunately no such class exists, not even in the ThreeTen-Extra project, as far as I can tell at a glance.
Links
Oracle tutorial: Date Time, explaining how to use java.time.
ThreeTen Extra, more classes developed along with java.time.
EDIT: See my updated code run live on ideone.
"2017-01-03+01:00"
I thought it a similar ISO 8601 format date string, but actually not ISO 8601. Thanks #Meno Hochschild and #Basil Bourque's indication.
It is so luck that this method works for such format's string: javax.xml.bind.DatatypeConverter.parseDateTime, it will return a Calendar:
System.out.println(DatatypeConverter.parseDate("2017-01-03+01:00").getTime());
Output:
Tue Jan 03 07:00:00 CST 2017
From the method javadoc:
public static Calendar parseDate(String lexicalXSDDate)
Converts the string argument into a Calendar value.
Parameters: lexicalXSDDate - A string containing lexical
representation of xsd:Date.
Returns: A Calendar value represented by
the string argument.
Throws: IllegalArgumentException - if string
parameter does not conform to lexical value space defined in XML
Schema Part 2: Datatypes for xsd:Date.

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.

Categories