How to format Date object to match #JsonFormat? - java

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.

Related

parsing date from "06/25/2021 10:26:33.0" format to "2021-06-25T10:26:33.000-04:00" using java.time

I have to convert a date for the purpose of comparison using junit. I get a date from DB which is "06/25/2021 10:26:33.0" and I have to convert it to "2021-06-25T10:26:33.000-04:00" before I use it in the asserts.
I am trying not to use SimpleDate in java and use the the inbuilt java.time instead. However, I don't think I really understand everything in it. Here is the code snippet I have been playing around with. I have tried many things with this and I always get an error when the parse happens.
public String test() throws ParseException {
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.S");
LocalDateTime ldt = LocalDateTime.parse("06/25/2021 10:26:33.0", dtf);
//After the above line I have a date like: 2021-06-25T10:26:33.0
ZoneId zone = ZoneId.of("UTC-04:00");
ZonedDateTime zdt = ldt.atZone(zone);
Instant instant = zdt.toInstant();
return instant.toString();
}
In my mind, I think I have to first convert the date to an "acceptable" format because I feel like this format of the string - "yyyy-MM-dd HH:mm:ss.S" is not something that java.time can handle. it gives me an error such as "Text could not be parsed at index 19" and then probably in the second pass convert it to into this "2021-06-25T10:26:33.000-04:00".
I have consulted several articles regarding this on SO but haven't been able to find something that helps in converting custom formats. I am aware that "parse" and "format" are 2 API methods that have to be leveraged here but not sure how to go about it. Could someone pls nudge me in the right direction?
You can use LocalDateTime#atOffset to meet this requirement.
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class Main {
public static void main(String[] args) {
String input = "06/25/2021 10:26:33.0";
DateTimeFormatter dtfInput = DateTimeFormatter.ofPattern("M/d/u H:m:s.S", Locale.ENGLISH);
LocalDateTime ldt = LocalDateTime.parse(input, dtfInput);
OffsetDateTime odt = ldt.atOffset(ZoneOffset.of("-04:00"));
System.out.println(odt);
// Formatted output
DateTimeFormatter dtfOutput = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSXXX", Locale.ENGLISH);
String formatted = dtfOutput.format(odt);
System.out.println(formatted);
}
}
Output:
2021-06-25T10:26:33-04:00
2021-06-25T10:26:33.000-04:00
ONLINE DEMO
Notes:
If the fraction-of-second can be of zero to nine digits in the input, use the pattern, M/d/u H:m:s[.[SSSSSSSSS][SSSSSSSS][SSSSSSS][SSSSSS][SSSSS][SSSS][SSS][SS][S]] where optional patterns have been specified using the square bracket.
OffsetDateTime#toString omits the second and the fraction-of-second part if they are zero. Use a DateTimeFormatter to get them in the formatted string.
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.
convert it to into this "2021-06-25T10:26:33.000-04:00".
I think you'll need a custom formatter for that. Maybe the following:
public String test() {
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("MM/dd/uuuu HH:mm:ss.S");
LocalDateTime ldt = LocalDateTime.parse("06/25/2021 10:26:33.0", dtf);
ZoneOffset zoff = ZoneOffset.ofHours(-4);
OffsetDateTime ldt2 = ldt.atOffset(zoff);
DateTimeFormatter tsf = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSxxx");
return tsf.format(ldt2);
}

How to parse UTC timestamp of format yyyy-MM-ddTHH:mm:ss.SSS+ZZZZ

I have timestamp as 2020-12-03T05:35:59.398+0000 in String format being recieved in a streaming batch.
I want only 2020-12-03 05:35:59 as java.sql.Timestamp instance in order to be able to compare it with other Timestamp instance.
Getting following error with Timestamp.valueOf() function:
Exception in thread "main" java.time.format.DateTimeParseException : Text '2020-12-03T05:35:59.398+0000' could not be parsed at index 23
I tried the answer given here , and conversion did happen but the time was changed to 2020-12-03 11:05:59
I have tried changing between the formats given here but still no solution.
Is there even a format for timestamp with wierd + in between 398+0000?
java.time
I recommend that you use java.time, the modern Java date and time API, for your date and time work.
DateTimeFormatter isoFormatter = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSxx");
DateTimeFormatter sqlTimestampFormatter = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ISO_LOCAL_DATE)
.appendLiteral(' ')
.append(DateTimeFormatter.ISO_LOCAL_TIME)
.toFormatter();
String aTimestampString = "2020-12-03T05:35:59.398+0000";
String anotherTimestampString = "2020-12-04 06:43:58.556385";
Instant anInstant = isoFormatter.parse(aTimestampString, Instant::from);
Instant anotherInstant = LocalDateTime.parse(anotherTimestampString, sqlTimestampFormatter)
.atOffset(ZoneOffset.UTC)
.toInstant();
if (anInstant.isBefore(anotherInstant)) {
System.out.println(aTimestampString + " is earlier");
} else {
System.out.println(anotherTimestampString + " is earlier");
}
Output from this example is:
2020-12-03T05:35:59.398+0000 is earlier
The +0000 in the former string above is an offset from UTC — an offset of 00 hours 00 minutes. Since it is zero, we know that the time is in UTC. I don’t know the time zone or UTC offset of the other string. You need to know, or you will get incorrect results. In the code above I have assumed that the other string is in UTC too.
Don’t use Timestamp
I tried the answer given here, and conversion did happen but the time
was changed to 2020-12-03 11:05:59
This is how confusing the Timestamp class is. You got the correct timestamp value. What happens when you print the Timestamp object, is that you are (implicitly or explicitly) calling its toString method. Timestamp.toString() confusingly uses the default time zone of your JVM for rendering the string. So if your timestamp is equal to 2020-12-03T05:35:59.398 UTC and your time zone is, say, Asia/Kolkata, then time is converted to Asia/Kolkata time zone and the string 2020-12-03 11:05:59 is returned and printed.
You have nothing good to use the old-fashioned java.sql.Timestamp class for. It was originally meant for transferring timestamp values with and without time zone to and from SQL databases. Since JDBC 4.2 we prefer OffsetDateTime, Instant and LocalDateTime for that purpose. So just forget about the Timestamp class.
Link
Oracle tutorial: Date Time explaining how to use java.time.
Is there even a format for timestamp with wierd + in between 398+0000?
The 398 part is fraction-of-second (millisecond) while the +0000 part is the zone offset part.
You can parse 2020-12-03T05:35:59.398+0000 into an OffsetDateTime using the format pattern, uuuu-MM-dd'T'HH:mm:ss.SSSX.
Demo:
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
public class Main {
public static void main(String[] args) {
String text = "2020-12-03T05:35:59.398+0000";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSX");
OffsetDateTime odt = OffsetDateTime.parse(text, formatter);
System.out.println(odt);
}
}
Output:
2020-12-03T05:35:59.398Z
Check the DateTimeFormatter documentation page to learn more about the letters used for formatting.
You can use isBefore and isAfter functions of OffsetDateTime to compare its two instances.
Demo:
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
public class Main {
public static void main(String[] args) {
String text = "2020-12-03T05:35:59.398+0000";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSX");
OffsetDateTime odt = OffsetDateTime.parse(text, formatter);
OffsetDateTime odtNow = OffsetDateTime.now();
System.out.println(odtNow.isBefore(odt));
System.out.println(odtNow.isAfter(odt));
}
}
Output:
false
true
Learn more about the modern date-time API at 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 and How to use ThreeTenABP in Android Project.
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. Since java.sql.Timestamp extends java.util.Date, it is recommended to stop using that as well. However, for any reason, if you still want to use conversion between the modern and the legacy date-time API, use Instant as the bridge.
import java.sql.Timestamp;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
public class Main {
public static void main(String[] args) {
String text = "2020-12-03T05:35:59.398+0000";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSX");
OffsetDateTime odt = OffsetDateTime.parse(text, formatter);
Instant instant = odt.toInstant();
Timestamp timestamp = new Timestamp(instant.toEpochMilli());
System.out.println(timestamp);
}
}
Output:
2020-12-03 05:35:59.398
You can use a custom DateFormatter for non-standard formats. Here is a working example for your use case.
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import static java.time.temporal.ChronoField.*;
public class Main {
private static final DateTimeFormatter INPUT_NON_STANDARD_FORMAT;
static {
INPUT_NON_STANDARD_FORMAT =
new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.append(DateTimeFormatter.ISO_LOCAL_DATE)
.appendLiteral('T')
.appendValue(HOUR_OF_DAY, 2)
.appendLiteral(':')
.appendValue(MINUTE_OF_HOUR, 2)
.optionalStart()
.appendLiteral(':')
.appendValue(SECOND_OF_MINUTE, 2)
.optionalStart()
.appendLiteral('.')
.appendValue(MILLI_OF_SECOND, 3)
.appendLiteral("+0000")
.toFormatter();
}
public static void main(String[] args) {
String timestamp = "2020-12-03T05:35:59.398+0000";
final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("YYYY-MM-dd HH:mm:ss");
LocalDateTime localDateTime = LocalDateTime.parse(timestamp, INPUT_NON_STANDARD_FORMAT);
System.out.println(localDateTime.format(dateTimeFormatter));
}
}
Output
2020-12-03 05:35:59

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