Migrating to Java 8 DateTime [duplicate] - java

This question already has answers here:
How to get start and end range from list of timestamps?
(2 answers)
Date year value showing valid when invalid [duplicate]
(3 answers)
Closed 4 years ago.
I'm in the process of changing our existing SimpleDateFormat based code to use the new Java 8 LocalDateTime and ZonedDateTime classes.
I couldn't find an easy way to convert this piece.
Considering sample date and format.
String testDate = "2012-12-31T10:10:10-06:00";
String testFormat = "yyyy-MM-dd'T'HH:mm:ss";
Existing code,
SimpleDateFormat sdf = new SimpleDateFormat(testFormat);
System.out.println(sdf.parse(testDate));
This piece works for both the date value containing timezone offset like mentioned above or not. It ignores the timezone if provided and in both cases defaults to the system timezone.
However if I replace it with below code,
DateTimeFormatter df = DateTimeFormatter.ofPattern(testFormat);
LocalDateTime ldt = LocalDateTime.parse(testDate, df);
ZonedDateTime zdt = ldt.atZone(ZoneId.systemDefault());
System.out.println(zdt.toString());
It fails for the above format. To get it to work, I have to get rid of the timezone bit in the date i.e. convert it to "2012-12-31T10:10:10" or then change the format to include timezone details i.e. 'z'.
So from my initial tests at least it seems the new classes are pretty strict from matching the format to the T unlike SimpleDateFormat. I even tried marking the DateTimeFormater.setLineant(), but that too didn't help. Or maybe there are some other classes which can work in this case, just that I am not aware of them.
To add more details as why i want to have the legacy behavior using the new API's. In our product we expose these API's which end customer can use, so the API in question accepts both the datetime value in string and the format and used "SimpleDateFormat". So i want existing behavior to continue even with the new API's just so that we continue to maintain backward compatibility.
For now the only solution for above example i have come up with is handle it as an exception case,
DateTimeFormatter df = DateTimeFormatter.ofPattern(testFormat);
LocalDateTime ldt = null;
try {
ldt = LocalDateTime.parse(testDate, df);
} catch (DateTimeParseException dtpException) {
ldt = ZonedDateTime.parse(testDate).toLocalDateTime();
}
if (ldt != null) zdt = ldt.atZone(ZoneId.systemDefault());
Can someone share some pointers around this use case?

Related

SimpleDateFormat with timezone displaying dates in my timezone, how do I fix this? [duplicate]

This question already has answers here:
SimpleDateFormat returns wrong time zone during parse
(2 answers)
SimpleDateFormat parse loses timezone [duplicate]
(3 answers)
DateFormat parse - not return date in UTC
(1 answer)
Get date from device and convert it to GMT+4
(1 answer)
Closed 3 years ago.
Given these two date Strings, I am trying to create a menu that allows you to select from the two dates (these are returned from an API with the users timezone):
2019-12-20T00:00:00.000-05:00
2019-12-19T00:00:00.000-05:00
I use the following code to parse the date string in the users preferred timezone (I have downloaded this locally to their devices). I have verified that the TZUtils.getUsersTimeZone() returns this timezone: America/New York which has an offset of -5.
fun getDateForString(date: String): Date {
val parser = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX")
parser.timeZone = TZUtils.getUsersTimeZone()
return parser.parse(date)
}
When these dates are parsed, they are parsed into my local time time (offset -6) and not in the users local time zone (-5), even though I specify to use the users local time zone. When I create the popup menu, I used the following code to show the dates
public String getStringForDate(Date date) {
DateFormat dateFormat = SimpleDateFormat.getDateInstance(DateFormat.SHORT);
return dateFormat.format(date);
}
And this returns me the wrong dates to select from (the dates are based on my timezone and not the users). Furthermore, when I select the data from the menu, the function I use to filter data based on if it is the same day as the selected date doesn't work because it uses my timezone as well. How do I fix this or do I just "assume" this will work for users since their devices are in their timezones?
You can use the modern java.time package instead and specially ZonedDateTime that handles time zones.
String str = "2019-12-20T00:00:00.000-05:00";
ZonedDateTime zonedDateTime = ZonedDateTime.parse(str);
And when showing it in the UI use DateTimeFormatter to convert it to a formatted string
DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE_TIME;
or with some custom format
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
which will give you for the 2 formats
System.out.println(formatter.format(zonedDateTime));
2019-12-20T00:00-05:00
2019-12-20 00:00
SimpleDateFormat.getDateInstance(...) will give you an instance in the JVM's default time zone.
Set the time zone to whatever you need it to be.
DateFormat dateFormat = SimpleDateFormat.getDateInstance(DateFormat.SHORT);
dateFormat.setTimeZone(/* whatever */);
return dateFormat.format(date);

Remove the day name from date and set it as only 'yyyy-MM-dd HH:mm:ss' [duplicate]

This question already has answers here:
Java Date changing format [duplicate]
(4 answers)
java.util.Date format conversion yyyy-mm-dd to mm-dd-yyyy
(8 answers)
Closed 5 years ago.
I'm trying to get the current day's date at 6 am in the format yyyy-MM-dd HH:mm:ss, but it shows as : Wed Dec 20 06:00:00 CST 2017
This is my code:
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd 06:00:00");
Date date0 = new Date();
String x = dateFormat.format(date0);
try{
DateFormat formatter ;
Date date ;
formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
date = formatter.parse(x);
}
catch (Exception e){}
java.util.Date is a container for the number of milliseconds since the Unix Epoch, it doesn't not maintain any kind of internal formatting concept, instead, when you print it, it use Date#toString which generally uses the current Locale to provide a human readable representation of the value.
While I'm sure you could continue to mess about with Date to make this work, a much simpler approach would be to take advantage of the newer Date/Time API, something like...
LocalDateTime now = LocalDateTime.now();
LocalDateTime then = now.withHour(6).withMinute(0).withSecond(0).withNano(0);
String formatted = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(then);
System.out.println(formatted);
Which, for me, prints out 2017-12-21 06:00:00
TL;DR
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss");
LocalDate date0 = LocalDate.now(ZoneId.of("America/Winnipeg"));
String at6Am = date0.atTime(LocalTime.of(6, 0)).format(formatter);
System.out.println(at6Am);
Running just now this printed
2017-12-21 06:00:00
Details
The classes that you use, Date and SimpleDateFormat, have been around since Java 1.0, some 20 years. They have proved to be poorly designed and cumbersome to use. Maybe for that reason too, much has been written about them, and from searching the web you could easily get the impression that these are the classes you should use. On the contrary, they are the classes you should avoid. Their replacement came out with Java 8, it will soon be 4 years ago.
Formatters are for formatting and parsing. You shouldn’t use a formatter, even less two formatters, for changing the time-of-day to 6 AM.
It is never the same date everywhere on the globe. So getting today’s date is an operation that depends on a time zone. I have made the time zone explicit in my code so the reader will also be aware of this fact. Please substitute your desired time zone if it didn’t happen to be America/Winnipeg.
You are modifying existing software. If you got an old-fashioned Date object from it, first convert it to the modern Instant type, then use the modern API for further operations. For example:
Date date0 = getOldfashionedDateFromLegacyApi();
String at6Am = date0.toInstant()
.atZone(ZoneId.of("America/Winnipeg"))
.with(LocalTime.of(6, 0))
.format(formatter);
What went wrong in your code?
I don’t think there’s anything really wrong with the code in your question. You wanted your date-time formatted as 2017-12-20 06:00:00, and you got that in the string x in the third code line. Be happy with that and leave out the remainder of the code.
There is no such thing as imposing the format on the date-time objects, (no matter if we talk the outdated or the modern API). Formatting a date-time means converting it to a String in the desired format.
After parsing, you need to format it as follows: formatter.format(date). So modify your code as follows:
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd 06:00:00");
Date date0 = new Date();
String x = dateFormat.format(date0);
try{
DateFormat formatter ;
Date date ;
formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
date = formatter.format(formatter.parse(x));
}
catch (Exception e){}

Java Date changing format [duplicate]

This question already has answers here:
java.util.Date format conversion yyyy-mm-dd to mm-dd-yyyy
(8 answers)
Closed 5 years ago.
I am trying to change the format of Date objects, I am trying to do it in this way:
for(Date date : dates){
DateFormat formatter = new SimpleDateFormat("yyyy/MM/dd");
String formatterDate = formatter.format(date);
Date d = formatter.parse(formatter.format(date));
}
But this does not have any effect on the d object, it is still with the old format, can't really understand why it is like that.
Please try to keep two concepts apart: your data and the presentation of the data to your user (or formatting for other purposes like inclusion in JSON). An int holding the value 7 can be presented as (formatted into) 7, 07, 007 or +7 while still just holding the same value without any formatting information — the formatting lies outside the int. Just the same, a Date holds a point in time, it can be presented as (formatted into) “June 1st 2017, 12:46:01.169”, “2017/06/01” or “1 Jun 2017” while still just holding the same value without any formatting information — the formatting lies outside the Date.
Depending on your requirements, one option is you store your date as a Date (or better, an instance of one of the modern date and time classes like LocalDate) and keep a formatter around so you can format it every time you need to show it to the user. If this won’t work and you need to store the date in a specific format, then store it as a String.
Java 8 (7, 6) date and time API
Now I have been ranting about using the newer Java date and time classes in the comments, so it might be unfair not to show you that they work. The question tries to format as yyyy-MM-dd, which we may do with the following piece of code.
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("uuuu/MM/dd");
for (LocalDate date : localDates) {
String formatterDate = date.format(dateFormatter);
System.out.println(formatterDate);
}
In one run I got
2017/05/23
2017/06/01
Should your objects in the list have other types than LocalDate, most other newer date and time types can be formatted in exactly the same way using the same DateTimeFormatter. Instant is a little special in this respect because it doesn’t contain a date, but you may do for example myInstant.atZone(ZoneId.of("Europe/Oslo")).format(dateFormatter) to obtain the date it was/will be in Oslo’s time zone at that instant.
The modern classes were introduced in Java 8 and are enhanced a bit in Java 9. They have been backported to Java 6 and 7 in the ThreeTen Backport with a special edition for Android, ThreeTenABP. So I really see no reason why you should not prefer to use them in your own code.
Try this one.
String formattedDate = null;
SimpleDateFormat sdf = new SimpleDateFormat(format you want);
formattedDate = sdf.format( the date you want to format );
return formattedDate;
some not best solution, but it works: this method will convert Date object to String of any pattern you need
public static void format(Date date){
String pattern = "MMM d yyyy";
LocalDateTime localDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
String result = formatter.format(localDate);
// new Date() -> Jun 1 2017
}
SimpleDateFormat is useful while converting Date to String or vice-versa. java.util.Date format will always remain same. If you want to display it in standalone application then use date.getxxx() methods and choose your design.

Converting a time String to ISO 8601 format

I am trying to create a String in a format like 2015-08-20T08:26:21.000Z
to 2015-08-20T08:26:21Z
I know it can be done with some String splitting techniques, but i am wondering if there is an elegant solution for that (with minimal code changes).
Both of the above are time strings, the final one which i need is Date in ISO 8601 . https://www.rfc-editor.org/rfc/rfc3339#section-5.6
I have tried a few similar questions like converting a date string into milliseconds in java but they dont actually solve the purpose.
Also tried using :
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mmZ");
String nowAsString = df.format(new Date());
But it still does not do any String to String conversions. Getting the following error:
23:04:13,829 WARN [RuntimeExceptionMapper] caught RuntimeException: {}: java.lang.IllegalArgumentException: Cannot format given Object as a Date
Is there some library which someone can suggest ?
Thanks.
tl;dr
Instant.parse( "2015-08-20T08:26:21.000Z" )
.toString()
2015-08-20T08:26:21Z
Date-Time Formatter
If all you want to do is eliminate the .000, then use date-time objects to parse your input string value, then generate a new string representation of that date-time value in a different format.
ISO 8601
By the way, if that is your goal, the Question’s title make no sense as both strings mentioned in the first sentence are valid ISO 8601 formatted strings.
2015-08-20T08:26:21.000Z
2015-08-20T08:26:21Z
java.time
Java 8 and later has the new java.time package. These new classes supplant the old java.util.Date/.Calendar & java.text.SimpleDateFormat classes. Those old classes were confusing, troublesome, and flawed.
Instant
If all you want is UTC time zone, then you can use the Instant class. This class represents a point along the timeline without regard to any particular time zone (basically UTC).
DateTimeFormatter.ISO_INSTANT
Calling an Instant’s toString generates a String representation of the date-time value using a DateTimeFormatter.ISO_INSTANT formatter instance. This formatter is automatically flexible about the fractional second. If the value has a whole second, no decimal places are generated (apparently what the Question wants). For a fractional second, digits appear in groups of 3, 6, or 9, as needed to represent the value up to nanosecond resolution. Note: this format may exceed ISO 8601 limit of milliseconds (3 decimal places).
Example code
Here is some example code in Java 8 Update 51.
String output = Instant.parse( "2015-08-20T08:26:21.000Z" ).toString( );
System.out.println("output: " + output );
output: 2015-08-20T08:26:21Z
Changing to a fractional second, .08
String output = Instant.parse( "2015-08-20T08:26:21.08Z" ).toString( );
output: 2015-08-20T08:26:21.080Z
If interested in any time zone other than UTC, then make a ZonedDateTime object from that Instant.
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , ZoneId.of( "America/Montreal" ) ) ;
Your format is just not right try this :-
try {
String s = "2015-08-20T08:26:21.000Z";
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX");
Date d = df.parse(s);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");
System.out.println(sdf.format(d));
} catch (ParseException e) {
e.printStackTrace();
}
Conversion of a date String of unknown formatting into a date String that uses known formatting can be accomplished using two DateFormat objects- one dynamically configured to parse the format of the input String, and one configured to generate the formatted output String. For your situation the input String formatting is unspecified and must be provided by the caller, however, the output String formatting can be configured to use ISO 8601 formatting without additional input. Essentially, generating an ISO 8601 formatted date String output requires two inputs provided by the caller- a String containing the formatted date and another String that contains the SimpleDateFormat format.
Here is the described conversion as Java code (I deliberately have left out null checks and validations, add these as appropriate for your code):
private String formatDateAsIso8601(final String inputDateAsString, final String inputStringFormat) throws ParseException {
final DateFormat iso8601DateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'", Locale.ENGLISH);
iso8601DateFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));
final DateFormat inputDateFormatter = new SimpleDateFormat(inputStringFormat, Locale.ENGLISH);
final Date inputDate = inputDateFormatter.parse(inputDateAsString);
return iso8601DateFormatter.format(inputDate);
}
If you want to modify that method please note that SimpleDateFormat is not thread-safe, and that you should not use it from a static context without a workaround for multi-threaded code (ThreadLocal is commonly used to do just such a workaround for SimpleDateFormat).
An additional "gotcha" is the use of a Locale during the construction of the SimpleDateFormat objects- do not remove the Locale configuration. It is not safe to allow the system to choose to use the default Locale because that is user/machine specific. If you do allow it to use the default Locale, you run the risk of transient bugs because your development machine uses a Locale different than the Locale of your end-user. You do not have to use my selected ENGLISH Locale, it is perfectly fine to use a different Locale (you should understand the rules of that Locale and modify the code as appropriate however). Specification of no Locale and utilization of the system default is incorrect however, and likely will lead to many frustrating hours trying to diagnose an elusive bug.
Please understand this solution is not ideal as of Java 8 and the inclusion of the JodaTime based classes, like Instant. I chose to answer using the outdated API's because those were what you seemed concerned with in your question. If you are using Java 8 I strongly urge to learn and utilize the new classes as they are an improvement in almost every conceivable way.

Convert Java Date into XML Date Format (and vice versa)

Is there a nice and easy way to convert a Java Date into XML date string format and vice versa?
Cheers,
Andez
Original answer
I am guessing here that by "XML Date Format" you mean something like "2010-11-04T19:14Z". It is actually ISO 8601 format.
You can convert it using SimpleDateFormat, as others suggested, FastDateFormat or using Joda Time which was I believe especially created for this purpose.
Edit: code samples and more
As earnshae stated in a comment, this answer could be improved with examples.
First, we have to make clear that the original answer is pretty outdated. It's because Java 8 introduced the classes to manipulate date and time - java.time package should be of interest. If you are lucky enough to be using Java 8, you should use one of them. However, these things are surprisingly difficult to get right.
LocalDate(Time) that isn't
Consider this example:
LocalDateTime dateTime = LocalDateTime.parse("2016-03-23T18:21");
System.out.println(dateTime); // 2016-03-23T18:21
At first it may seem that what we're using here is a local (to the user date and time). However, if you dare to ask, you'll get different result:
System.out.println(dateTime.getChronology()); // ISO
This actually, the ISO time. I believe it should read 'UTC' but nonetheless this has no notion of local time zone. So we should consider it universal.
Please notice, that there is no "Z" at the end of the string we are parsing. Should you add anything apart of date and time, you'll be greeted with java.time.format.DateTimeParseException. So it seems that this class is of no use if we want to parse ISO8601 string.
ZonedDateTime to the rescue
Fortunately, there is a class that allows for parsing ISO8601 strings - it's a java.time.ZonedDateTime.
ZonedDateTime zonedDateTime = ZonedDateTime.parse("2016-03-23T18:21+01:00");
System.out.println(zonedDateTime); // 2016-03-23T18:21+01:00
ZonedDateTime zonedDateTimeZulu = ZonedDateTime.parse("2016-03-23T18:21Z");
System.out.println(zonedDateTimeZulu); // 2016-03-23T18:21Z
The only problem here is, you actually need to use time zone designation. Trying to parse raw date time (i.e. "2016-03-23T18:21") will result in already mentioned RuntimeException. Depending on the situation you'd have to choose between LocalDateTime and ZonedDateTime.
Of course you can easily convert between those two, so it should not be a problem:
System.out.println(zonedDateTimeZulu.toLocalDateTime()); // 2016-03-23T18:21
// Zone conversion
ZonedDateTime cetDateTime = zonedDateTimeZulu.toLocalDateTime()
.atZone(ZoneId.of("CET"));
System.out.println(cetDateTime); // 2016-03-23T18:21+01:00[CET]
I recommend using this classes nowadays. However, if your job description includes archeology (meaning you are not lucky enough to be working with more than 2 year old Java 8...), you may need to use something else.
The joy of SimpleDateFormat
I am not a very big fan of https://docs.oracle.com/javase/8/docs/api/java/text/SimpleDateFormat.html, but sometimes you just have no other choice. Problem is, it is not thread-safe and it will throw a checked Exception (namely ParseException) in your face if it dislikes something. Therefore the code snippet is rather ugly:
private Object lock = new Object();
// ...
try {
synchronized (lock) {
// Either "2016-03-23T18:21+01:00" or "2016-03-23T18:21Z"
// will be correctly parsed (mind the different meaning though)
Date date = dateFormat.parse("2016-03-23T18:21Z");
System.out.println(date); // Wed Mar 23 19:21:00 CET 2016
}
} catch (ParseException e) {
LOG.error("Date time parsing exception", e);
}
FastDateFormat
FastDateFormat is synchronized, therefore you can at least get rid of the synchronized block. However, it is an external dependency. But since it's the Apache Commons Lang and it is thoroughly used, I guess it is acceptable. It is actually very similar in usage to SimpleDateFormat:
FastDateFormat fastDateFormat = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mmX");
try {
Date fastDate = fastDateFormat.parse("2016-03-23T18:21+01:00");
System.out.println(fastDate);
} catch (ParseException e) {
LOG.error("Date time parsing exception", e);
}
JodaTime
With Joda-Time you may think that following works:
DateTimeFormatter parser = ISODateTimeFormat.dateTimeParser();
LocalDateTime dateTime = LocalDateTime.parse("2016-03-23T20:48+01:00", parser);
System.out.println(dateTime); // 2016-03-23T20:48:00.000
Unfortunately, no matter what you put at last position (Z, +03:00, ...) the result will be the same. Clearly, it isn't working.
Well, you really should be parsing it directly:
DateTimeFormatter parser = ISODateTimeFormat.dateTimeParser();
DateTime dateTime = parser.parseDateTime("2016-03-23T21:12:23+04:00");
System.out.println(dateTime); // 2016-03-23T18:12:23.000+01:00
Now it will be OK. Please note, that unlike one of other answers, I used dateTimeParser() and not dateTime(). I noticed subtle, but important difference in behavior between them (Joda-Time 2.9.2). But, I leave it to the reader to test it and confirm.
As already suggested use SimpleDateFormat.
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
String date = sdf.format(new Date());
System.out.println(date);
Date d = sdf.parse(date);
My guess is that the format/pattern that your looking for is yyyy-MM-dd'T'HH:mm:ss
Also have a look at http://www.w3schools.com/schema/schema_dtypes_date.asp
Using Joda Time you would do the following:
DateTimeFormatter fmt = ISODateTimeFormat.dateTime(); // ISO8601 (XML) Date/time
DateTime dt = fmt.parseDateTime("2000-01-01T12:00:00+100"); // +1hr time zone
System.out.println(fmt.print(dt)); // Prints in ISO8601 format
Thread safe, immutable and simple.
The Perfect method, use XMLGregorianCalendar:
GregorianCalendar calendar = new GregorianCalendar();
calendar.setTime(v);
DatatypeFactory df = DatatypeFactory.newInstance();
XMLGregorianCalendar dateTime = df.newXMLGregorianCalendar(calendar);
return dateTime.toString();
Just by using SimpleDateFormat in java we can do this...
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
Date date = sdf.parse("2011-12-31T15:05:50+1000");
I would recommend to use the java built in class javax.xml.bind.DatatypeConverter. It can handle conversion to and from most of the xml simple types. It is a little bit cumbersome for dates that you have to go through a Calendar object but on the other hand it handles all variants of zone information that can occur in a xml datetime field.
From xml:
Calendar c = DatatypeConverter.parseDateTime("2015-10-21T13:25");
Date d = c.getTime();
To xml:
Date yourDate = new Date()
Calendar c = Calendar.getInstance();
c.setTime(yourDate);
String xmlDateTime = DatatypeConverter.printDateTime(c);
EDIT
The DatatypeConverter class is no longer publicly visible in Java 9 and above since it belongs to the javax.xml.bind package. See this question for more information and possible solutions. The solution proposed by loic vaugeois to use XmlGregorianCalendar is much better in this case.
You can parse and format dates to and from any format using SimpleDateFormat
To comply with ISO8601, the timezone must be in the format +HH:MM or - HH:MM
With Simpledateformat you must use XXX instead of Z (see NovelGuy answer)
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
Without knowing exactly what format you need, the generic response is: you're going to want DateFormat or SimpleDateFormat. There is a nice tutorial on both here.

Categories

Resources