Jackson incorrectly serializes XMLGregorianCalendar - java

There is an XMLGregorianCalendar object that contains the value "2021-01-18T18:43:26.884Z" (This is its output in toString()). When I try to serialize this date with Jackson, I get a date 3 hours later in the output:
XMLGregorianCalendar date = ...;
ObjectMapper mapper = new ObjectMapper();
String out = mapper.writeValueAsString(obj); // Output: 1610995406884 (Converted to Date: Mon Jan 18 21:43:26 MSK 2021)
How can I solve this problem?

There is no problem at all.
The time "2021-01-18T18:43:26.884Z" in your XMLGregorianCalendar
is 18:43 in the GMT timezone (Greenwich mean time, London) or UTC+0 (because of the trailing Z).
In the other hand you have a Date object with the string representation
"Mon Jan 18 21:43:26 MSK 2021",
which is 21:43 in the MSK timezone (Moscow Standard Time) or UTC+3.
The Date class chose this timezone for formatting the output
simply because your computer is located near Moscow.
So both are actually the same point in time,
just only stringified for two different timezones.

java.util.Date represents the number of milliseconds since the epoch
The java.util.Date object is not a real date-time object like the modern date-time types; rather, it represents the number of milliseconds since the standard base time known as "the epoch", namely January 1, 1970, 00:00:00 GMT (or UTC). When you print an object of java.util.Date, its toString method returns the date-time in the JVM's timezone, calculated from this milliseconds value. If you need to print the date-time in a different timezone, you will need to set the timezone to SimpleDateFormat and obtain the formatted string from it.
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
public class Main {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(1610995406884L);
Date date = calendar.getTime();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
sdf.setTimeZone(TimeZone.getTimeZone("Etc/UTC"));
System.out.println(sdf.format(date));
sdf.setTimeZone(TimeZone.getTimeZone("Europe/Moscow"));
System.out.println(sdf.format(date));
}
}
Output:
2021-01-18T18:43:26.884
2021-01-18T21:43:26.884
Note that the date-time API of java.util and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern date-time API.
For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7.
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 and How to use ThreeTenABP in Android Project.
Convert the legacy, java.util.Date to the modern java.time.Instant using java.util.Date#toInstant:
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.Date;
public class Main {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(1610995406884L);
Date date = calendar.getTime();
Instant instant = date.toInstant();
System.out.println(instant);
// You can convert Instant to other types e.g.
ZonedDateTime zdt = instant.atZone(ZoneId.of("Europe/Moscow"));
// Print default format i.e. the value of zdt#toString
System.out.println(zdt);
// Custom format
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss'['z']'");
String formatted = dtf.format(zdt);
System.out.println(formatted);
}
}
Output:
2021-01-18T18:43:26.884Z
2021-01-18T21:43:26.884+03:00[Europe/Moscow]
2021-01-18T21:43:26[MSK]
The Z stands for Zulu and represents UTC (or GMT).

Related

How to format Date object to match #JsonFormat?

I have a json formatted response Date to the controller that is like this:
#JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone = "America/Chicago")
private Date date;
So when I make a post call, it would look like:
"date": "2021-08-20 14:17:43"
So the response string would look something like this {"date":"2021-05-21 14:23:44"}. In JUnit, I am manually creating a response object and setting the Date object so I can then use Gson to turn it into a string and then assert that the two are equal.
I am trying to match this in my SpringMVC JUnit test case by trying to do:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
sdf.setTimeZone(TimeZone.getTimeZone("America/Chicago"));
String formattedDate = sdf.format(new Date());
LocalDate localDate = LocalDate.parse(formattedDate);
Date date = Date.from(localDate.atStartOfDay(ZoneId.of("America/Chicago")).toInstant());
But it is having an error parsing it because of the space between yyyy-MM-dd and HH:mm:ss:
java.time.format.DateTimeParseException: Text '2021-08-20 14:23:44' could not be parsed, unparsed text found at index 10
I think I may be doing this inefficiently, so I was wondering if there was a more simple way to make a Date object that would match the format of #JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone = "America/Chicago")
I'm trying to match the response body so it passes via mockito.
Do not mix the modern and the legacy Date-Time API
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class Main {
public static void main(String[] args) {
String strDate = "2021-08-20 14:17:43";
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("u-M-d H:m:s", Locale.ENGLISH);
LocalDateTime ldt = LocalDateTime.parse(strDate, dtf);
System.out.println(ldt);
// Get the required Instant
ZonedDateTime zdtUtc = ldt.atZone(ZoneOffset.UTC);
ZonedDateTime zdtChicago = zdtUtc.withZoneSameInstant(ZoneId.of("America/Chicago"));
Instant instant = zdtChicago.toInstant();
System.out.println(instant);
}
}
Output:
2021-08-20T14:17:43
2021-08-20T14:17:43Z
ONLINE DEMO
java.time
The java.util Date-Time API and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern Date-Time API*. However, for any reason, if you need to convert this object of Instant to an object of java.util.Date, you can do so as follows:
Date date = Date.from(instant);
Learn more about the modern Date-Time API from Trail: Date Time.
* For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. 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 and How to use ThreeTenABP in Android Project.
you could be missing date deserializer
#JsonFormat(pattern="yyyy-MM-dd HH:mm:ss")
#JsonDeserialize(using = LocalDateTimeDeserializer.class)
Posting this to only try and satisfy what you're trying to achieve. But you should follow #Arvind's answer:
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.TimeZone;
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
sdf.setTimeZone(TimeZone.getTimeZone("America/Chicago"));
String formattedDate = sdf.format(new Date());
// Updated the lines below
LocalDateTime localDateTime = LocalDateTime.parse(formattedDate, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
Date date = Date.from(localDateTime.atZone(ZoneId.of("America/Chicago")).toInstant());
Best is if you can skip the Date class completely and in your response use Instant or ZonedDateTime from java.time, the modern Java date and time API.
If you cannot avoid using the outdated Date class
… I was wondering if there was a more simple way to make a Date object
that would match the format of #JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone = "America/Chicago")
Essential edit: It depends very much on what you mean by match the format. A Date can neither have a format nor a time zone. The string in your JSON has got the format mentioned. The Date has not since this would no be possible. The time zone, America/Chicago, is not present neither in JSON nor in the Date. It is only used for converting between the two. Two Date objects are equal if they denote the same point in time, there is nothing more to it. When you ask about formatting the Date to match the #JsonFormat, this necessarily means formatting into a string.
To convert a string like 2021-08-20 14:23:44 into an old-fashioned Date object I would first define the format and time zone statically:
private static final DateTimeFormatter FORMATTER
= DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss", Locale.ROOT);
private static final ZoneId ZONE = ZoneId.of("America/Chicago");
And then do:
String responseDateString = "2021-08-20 14:23:44";
Instant inst = LocalDateTime.parse(responseDateString, FORMATTER)
.atZone(ZONE)
.toInstant();
Date oldfashionedDate = Date.from(inst);
System.out.println(oldfashionedDate);
Output in my time zone is:
Fri Aug 20 21:23:44 CEST 2021
If I set my time zone to America/Chicago before running, it’s easier to see that the result is correct:
Fri Aug 20 14:23:44 CDT 2021
What went wrong in your code?
First you are correct that formatting a Date into a string only to parse it back is over-complicating things. Second you noticed that your exception came from this line:
LocalDate localDate = LocalDate.parse(formattedDate);
A LocalDate is a date without time of day. So its one-arg parse method expects only 2021-08-20 in the string, nothing more. It was complaining about the space, not because it was a space but just because there were more characters after the expected ones at all.

How to convert timezones from given string [duplicate]

This question already has answers here:
Java: getTimeZone without returning a default value
(5 answers)
Strange Java Timezone Date Conversion Problem
(3 answers)
Closed 2 years ago.
I have a given string 06/17/2008T13:53:23Z, I want to convert that to EDT time zone. So my output will be 6/17/08 9:53 AM EDT.
Below is my Java Code, it is giving wrong output:
SimpleDateFormat format1 = new SimpleDateFormat("MM/dd/yyyy'T'hh:mm:ss'Z'");
SimpleDateFormat format2 = new SimpleDateFormat("M/d/yy hh:mm a");
format1.setTimeZone(TimeZone.getTimeZone("UTC"));
Date date = format1.parse("06/17/2008T13:53:23Z");
System.out.println(date);
format2.setTimeZone(TimeZone.getTimeZone("EDT"));
System.out.println(format2.format(date));
It gives output as :
Tue Jun 17 19:23:23 IST 2008
6/17/08 01:53 PM
Here the output time as 01:53 PM instead of 9:53 AM EDT
How to fix this issue?
Your parsing pattern uses hh (which is for 12-hour format) whereas the time in your date-time string is in 24-hour format for which you need to use HH. Secondly, you should avoid using the three-letter time-zone name. As you have already expected, EDT has a Zone-Offset of -4 hours and you can use this as GMT-4 with SimpleDateFormat.
While a Zone-Offset is expressed in terms of numbers (hours, minutes or seconds), a timezone is expressed as a string representing the name (e.g. America/New_York) of the timezone. The relation between timezone and Zone-Offset is many-to-one i.e. many timezones can have the same Zone-Offset.
Note that java.util date-time classes are outdated and error-prone and so is their formatting API, SimpleDateFormat. I suggest you should stop using them completely and switch to the modern date-time API. Learn more about the modern date-time API at Trail: Date Time.
If you are doing it for your Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.
Using the modern date-time API:
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
public class Main {
public static void main(String[] args) {
String str = "06/17/2008T13:53:23Z";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("M/d/u'T'H:m:sz");
ZonedDateTime zdt = ZonedDateTime.parse(str, formatter);
// Convert to Eastern Time
ZonedDateTime zdtET = zdt.withZoneSameInstant(ZoneId.of("America/New_York"));
// ZonedDateTime zdtET = zdt.withZoneSameInstant(ZoneOffset.ofHours(-4));// Or this
// Print in default format i.e. ZonedDateTime#toString
System.out.println(zdtET);
// Print in custom formats
System.out.println(zdtET.format(DateTimeFormatter.ofPattern("MM/dd/uuuu'T'HH:mm:ss zzzz")));
System.out.println(zdtET.format(DateTimeFormatter.ofPattern("M/d/uu hh:mm:ss a")));
}
}
Output:
2008-06-17T09:53:23-04:00[America/New_York]
06/17/2008T09:53:23 Eastern Daylight Time
6/17/08 09:53:23 am
Note: If you use ZoneOffset.ofHours(-4) [commented in the code above], you can not get the name of timezone (e.g. America/New_York) in the output because, as explained earlier, many timezones can have the same Zone-Offset and there is no default timezone for a Zone-Offset.
Using the legacy API:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
public class Main {
public static void main(String[] args) throws ParseException {
String str = "06/17/2008T13:53:23Z";
SimpleDateFormat formatter = new SimpleDateFormat("MM/dd/yyyy'T'HH:mm:ss");
formatter.setTimeZone(TimeZone.getTimeZone("GMT+0"));
Date date = formatter.parse(str);
// Convert to Eastern Time
SimpleDateFormat sdfOutput = new SimpleDateFormat("M/d/yy hh:mm a");
sdfOutput.setTimeZone(TimeZone.getTimeZone("America/New_York"));
// sdfOutput.setTimeZone(TimeZone.getTimeZone("GMT-4"));// Or this
System.out.println(sdfOutput.format(date));
}
}
Output:
6/17/08 09:53 am

Convert ZonedDateTime to java.util.Date

How do i convert ZonedDateTime to java.util.Date without changing the timezone.
In my below method when i call Date.from(datetime.toInstant()) it convert it to local time zone in my case SGT.
public static void printDate(ZonedDateTime datetime) {
System.out.println("---> " + datetime.format(DateTimeFormatter.ofPattern(API_TIME_STAMP_PATTERN)));
System.out.println(Date.from(datetime.toInstant()));
System.out.println("\n");
}
Output
---> 2019-03-13_08:46:26.593
Wed Mar 13 16:46:26 SGT 2019
You can add offset millis by yourself. See the example using java.util.Date:
long offsetMillis = ZoneOffset.from(dateTime).getTotalSeconds() * 1000;
long isoMillis = dateTime.toInstant().toEpochMilli();
Date date = new Date(isoMillis + offsetMillis);
The java.util Date-Time API and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern Date-Time API*. However, for any reason, if you want to change java.time.ZonedDateTime to java.util.Date, I recommend you avoid any kind of manual calculations when you already have an inbuilt API to meet the requirement.
All you need to do is to add the offset to the input datetime which you can do by using ZonedDateTime#plusSeconds as shown below:
datetime = datetime.plusSeconds(datetime.getOffset().getTotalSeconds());
Date date = Date.from(datetime.toInstant());
Demo:
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
public class Main {
public static void main(String[] args) {
// Test
printDate(ZonedDateTime.now(ZoneId.of("Asia/Singapore")));
}
public static void printDate(ZonedDateTime datetime) {
datetime = datetime.plusSeconds(datetime.getOffset().getTotalSeconds());
Date date = Date.from(datetime.toInstant());
// Showing date-time in Singapore timezone
DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.ENGLISH);
sdf.setTimeZone(TimeZone.getTimeZone("Asia/Singapore"));
System.out.println(sdf.format(date));
}
}
Output:
2021-10-03T05:11:57
ONLINE DEMO
Learn more about the modern Date-Time API* from Trail: Date Time.
* For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. 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 and How to use ThreeTenABP in Android Project.
I would do as ETO states on his answer with the exception of using TimeUnit for getting the seconds converted into milliseconds:
long offsetMillis = TimeUnit.SECONDS.toMillis(ZoneOffset.from(dateTime).getTotalSeconds());
long isoMillis = dateTime.toInstant().toEpochMilli();
Date date = new Date(isoMillis + offsetMillis);
or the other option would be:
var localDate = LocalDateTime.now();
final long offSetHours = ChronoUnit.HOURS.between(localDate.atZone(ZoneId.of("YOUR_TIME_ZONE_ID")),
localDate.atZone(ZoneId.of("UTC")));
return Date.from(Instant.parse(dateAsStringISO8601).plus(offSetHours, ChronoUnit.HOURS));
Stick to ZonedDateTime
To preserve the time zone simply preserve your ZonedDateTime. It can be formatted directly to the output your require. Don’t involve the outdated and poorly designed Date class.
private static final String API_TIME_STAMP_PATTERN = "yyyy-MM-dd_HH:mm:ss.SSS";
private static final DateTimeFormatter FORMATTER
= DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss zzz y", Locale.ROOT);
public static void printDate(ZonedDateTime datetime) {
System.out.println("---> " + datetime.format(DateTimeFormatter.ofPattern(API_TIME_STAMP_PATTERN)));
System.out.println(datetime.format(FORMATTER));
System.out.println("\n");
}
Try it out:
ZonedDateTime zdt = ZonedDateTime.of(
2019, 3, 13, 8, 46, 26, 593_000_000, ZoneId.of("Etc/UTC"));
printDate(zdt);
Output:
---> 2019-03-13_08:46:26.593
Wed Mar 13 08:46:26 UTC 2019
Your conversion is not changing the time zone
A Date falsely pretends to have a time zone. It hasn’t got any. So there is no change of time zone going on. toInstant() discards the time zone because an Instant hasn’t got a time zone either. Date.from() performs the conversion withut any regard to time zone. System.out.println() implicitly calls Date.toString(). The toString() method uses the JVM’s default time zone for rendering the string. It’s pretty confusing alright.

Java Calendar TimeZone mess

Here is my simple code:
String defaultSimpleDateFormatPattern = "MMM dd, yyyy HH:mm:ss";
TimeZone tzNY = TimeZone.getTimeZone("America/New_York");
TimeZone tzLos = TimeZone.getTimeZone("America/Los_Angeles");
String dateToTest = "Jan 03, 2015 23:59:59";
SimpleDateFormat df = new SimpleDateFormat(defaultSimpleDateFormatPattern);
Calendar c = Calendar.getInstance();
c.setTime(df.parse(dateToTest));
c.setTimeZone(tzLos);
System.out.println(c.getTimeZone());
System.out.println(c.getTime());
System.out.println(df.format(c.getTime()));
Calendar c1 = Calendar.getInstance();
c1.setTime(df.parse(dateToTest));
c1.setTimeZone(tzNY);
System.out.println(c1.getTimeZone());
System.out.println(c1.getTime());
System.out.println(df.format(c1.getTime()));
System.out.println(c.after(c1)? "after" : (c.before(c1)? "before" : "equal"));
The printout is "equal". How is that? any explanation on this result?
There are two problems here:
You're using an invalid time zone ID (you want America/New_York)
You're parsing using a formatter that hasn't got a time zone set (so it'll use the default time zone) and then setting the time zone in the Calendar afterwards... that doesn't change the instant in time being represented
So basically you're parsing to the same Date twice, doing things which don't affect the Date being represented, and then comparing the two equal Date values.
If at all possible, you should use Joda Time or java.time instead of java.util.Calendar, but if you really need to use it, just create two different formatters, one with each time zone. (You'll need to set the time zone in the Calendar as well, if you actually need the Calendar...)
java.time
The java.util Date-Time API and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern Date-Time API*.
Also, quoted below is a notice from the home page of Joda-Time:
Note that from Java SE 8 onwards, users are asked to migrate to java.time (JSR-310) - a core part of the JDK which replaces this project.
Solution using java.time, the modern Date-Time API: Your Date-Time string does not have timezone information and therefore it can be described as a local Date-Time. So, parse it to LocalDateTime and apply the timezone to it to get the ZonedDateTime.
Demo:
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class Main {
public static void main(String[] args) {
String defaultSimpleDateFormatPattern = "MMM dd, uuuu HH:mm:ss";
ZoneId tzNY = ZoneId.of("America/New_York");
ZoneId tzLos = ZoneId.of("America/Los_Angeles");
String dateToTest = "Jan 03, 2015 23:59:59";
DateTimeFormatter dtf = DateTimeFormatter.ofPattern(defaultSimpleDateFormatPattern, Locale.ENGLISH);
LocalDateTime ldt = LocalDateTime.parse(dateToTest, dtf);
ZonedDateTime zdtNY = ldt.atZone(tzNY);
ZonedDateTime zdtLos = ldt.atZone(tzLos);
System.out.println(zdtNY.isAfter(zdtLos) ? "after" : zdtNY.isBefore(zdtLos) ? "before" : "equal");
}
}
Output:
before
ONLINE DEMO
Alternatively, Create separate DateTimeFormatter specific to each timezone i.e. ask Java to parse the local Date-Time string applying the given timezone.
Demo:
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class Main {
public static void main(String[] args) {
String defaultSimpleDateFormatPattern = "MMM dd, uuuu HH:mm:ss";
ZoneId tzNY = ZoneId.of("America/New_York");
ZoneId tzLos = ZoneId.of("America/Los_Angeles");
String dateToTest = "Jan 03, 2015 23:59:59";
DateTimeFormatter dtfNY = DateTimeFormatter.ofPattern(defaultSimpleDateFormatPattern, Locale.ENGLISH)
.withZone(tzNY);
DateTimeFormatter dtfLos = DateTimeFormatter.ofPattern(defaultSimpleDateFormatPattern, Locale.ENGLISH)
.withZone(tzLos);
ZonedDateTime zdtNY = ZonedDateTime.parse(dateToTest, dtfNY);
ZonedDateTime zdtLos = ZonedDateTime.parse(dateToTest, dtfLos);
System.out.println(zdtNY.isAfter(zdtLos) ? "after" : zdtNY.isBefore(zdtLos) ? "before" : "equal");
}
}
Output:
before
ONLINE DEMO
Learn more about the modern Date-Time API from Trail: Date Time.
What is wrong with your code?
You have not set a timezone to your SimpleDateFormat: Unlike the modern Date-Time API with which you have multiple ways to create a Date-Time object specific to a timezone, you have only this way with the legacy API to deal with such a situation (because java.util.Date does not hold timezone information). It is similar to the alternative example shown above.
You have not set a Locale to your SimpleDateFormat: Never use SimpleDateFormat or DateTimeFormatter without a Locale. Luckily, your program did not crash because your JVM's timezone must be an English locale.
Demo:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;
import java.util.TimeZone;
public class Main {
public static void main(String[] args) throws ParseException {
String defaultSimpleDateFormatPattern = "MMM dd, yyyy HH:mm:ss";
TimeZone tzNY = TimeZone.getTimeZone("America/New_York");
TimeZone tzLos = TimeZone.getTimeZone("America/Los_Angeles");
String dateToTest = "Jan 03, 2015 23:59:59";
SimpleDateFormat df = new SimpleDateFormat(defaultSimpleDateFormatPattern, Locale.ENGLISH);
df.setTimeZone(tzNY);
Calendar c = Calendar.getInstance();
c.setTime(df.parse(dateToTest));
df.setTimeZone(tzLos);
Calendar c1 = Calendar.getInstance(tzNY);
c1.setTime(df.parse(dateToTest));
System.out.println(c.after(c1) ? "after" : (c.before(c1) ? "before" : "equal"));
}
}
Output:
before
ONLINE DEMO
* For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. 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 and How to use ThreeTenABP in Android Project.

Custom String from ISO Compliant Date

I am getting a date string as 2014-01-11-T00:00:00Z
I want to convert this date to 20140111 i.e YYYYMMDD it should be a string.
Any standard method/function to achieve above?
java.time
Your date-time string, 2014-01-11-T00:00:00Z is a bit weird as I have never seen such a date-time string where there is a hyphen (-) before T. For this kind of string, the following pattern meets the parsing requirement:
yyyy-M-d-'T'H:m:sXXX
Also, with java.time API, I recommend you replace y with u as explained in this answer. For the output string, you do NOT need to define any pattern as there already exists an inbuilt DateTimeFormatter for this pattern: DateTimeFormatter.BASIC_ISO_DATE.
Demo:
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class Main {
public static void main(String args[]) {
DateTimeFormatter dtfInput = DateTimeFormatter.ofPattern("u-M-d-'T'H:m:sXXX", Locale.ENGLISH);
OffsetDateTime odt = OffsetDateTime.parse("2014-01-11-T00:00:00Z", dtfInput);
System.out.println(odt);
String output = odt.toLocalDate().format(DateTimeFormatter.BASIC_ISO_DATE);
System.out.println(output);
}
}
Output:
2014-01-11T00:00Z
20140111
Note:
Had your date-time string been ISO 8601 compliant, you would NOT have needed to use a DateTimeFormatter object explicitly for parsing i.e. you could have simply parsed it as
OffsetDateTime odt = OffsetDateTime.parse("2014-01-11T00:00:00Z");
The Z in the date-time stands for Zulu which specifies UTC time (that has a timezone offset of +00:00 hours) in ISO 8601 standard. Thus, this solution will also work for a date-time string like 2014-01-11-T00:00:00+02:00 which has a timezone offset of +02:00 hours.
In case, you need a java.util.Date object from this object of OffsetDateTime, you can do so as follows:
Date date = Date.from(odt.toInstant());
Learn more about the the modern date-time API* from Trail: Date Time.
Note that the legacy date-time API (java.util date-time types and their formatting API, SimpleDateFormat) are outdated and error-prone. It is recommended to stop using them completely and switch to java.time API. Just for the sake of completeness, I am providing you with a solution using the legacy API.
Using the legacy API:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
public class Main {
public static void main(String args[]) throws ParseException {
SimpleDateFormat sdfInput = new SimpleDateFormat("yyyy-M-d-'T'H:m:sXXX", Locale.ENGLISH);
SimpleDateFormat sdfOutput = new SimpleDateFormat("yyyyMMdd", Locale.ENGLISH);
sdfOutput.setTimeZone(TimeZone.getTimeZone("Etc/UTC"));// Change it as required
Date date = sdfInput.parse("2014-01-11-T00:00:00Z");
String output = sdfOutput.format(date);
System.out.println(output);
}
}
Output:
20140111
* For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. 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 and How to use ThreeTenABP in Android Project.
Take this
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateFormat {
public static void main(String[] args) throws ParseException {
SimpleDateFormat inFormat = new SimpleDateFormat("yyyy-MM-dd-'T'HH:mm:ss'Z'");
Date inDate = inFormat.parse("2014-01-11-T00:00:00Z");
SimpleDateFormat outFormat = new SimpleDateFormat("yyyyMMdd");
String output = outFormat.format(inDate);
System.out.println("Date: " + output);
}
}
Take a look at this thread for Date formatting in Java using Zoulou notation :
Converting ISO 8601-compliant String to java.util.Date
Then create a new SimpleDateFormat using the "yyyyMMdd" format string.
Here an improved version of given answer by #drkunibar:
SimpleDateFormat inFormat = new SimpleDateFormat("yyyy-MM-dd-'T'HH:mm:ss'Z'");
inFormat.setTimeZone(TimeZone.getTimeZone("GMT")); // Z denotes UTC in ISO-8601
Date inDate = inFormat.parse("2014-01-11-T00:00:00Z");
SimpleDateFormat outFormat = new SimpleDateFormat("yyyyMMdd");
outFormat.setTimeZone(TimeZone.getTimeZone("...")); // set your timezone explicitly!
String output = outFormat.format(inDate);
System.out.println("Date: " + output);
Note that the format YYYYMMDD is also ISO-8601-compliant (a so-called basic calendar date). The question you have to ask yourself is in which timezone you want to get your output. If in UTC you have to set "GMT", too. Without setting timezone it can happen that your output date differs from input UTC date by one day dependent where your default system timezone is (for example US is several hours behind UTC, in this case one calendar day before UTC midnight).
Update: This Answer is now obsolete. See the modern solution using java.time in the Answer by Avinash.
Joda-Time
This date-time work is much easier with the Joda-Time 2.3 library.
String input = "2014-01-11T00:00:00Z"; // In standard ISO 8601 format.
DateTime dateTime = new DateTime( input, DateTimeZone.UTC ); // Parse string into date-time object.
DateTimeFormatter formatter = ISODateTimeFormat.basicDate(); // Factory to make a formatter.
String output = formatter.print( dateTime ); // Generate string from date-Time object.

Categories

Resources