convert string to joda datetime gets stuck - java

I'm trying to convert this string: 2014-01-01 00:00:00 to Joda DateTime
I have tried the following:
public static DateTime SimpleIso8601StringToDateTime(String date) {
DateTimeFormatter df = DateTimeFormat.forPattern(CONSTS_APP_GENERAL.SIMPLE_DATE_FORMAT);
return df.parseDateTime(date);
}
And also the following:
public static DateTime SimpleIso8601StringToDateTime(String date) {
DateTime dtDate = DateTime.parse(date, DateTimeFormat.forPattern(CONSTS_APP_GENERAL.SIMPLE_DATE_FORMAT));
return dtDate;
}
Where
public static final String SIMPLE_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss.SSS";
However, using the debugger I get to the formatting line and then while trying to process it the program cursor never comes back.
I should mention that this is an Android project.
Any ideas what might be the problem?

It’s because 2014-01-01 00:00:00 doesn’t match the pattern yyyy-MM-dd HH:mm:ss.SSS—there’s no fractional part on the seconds in your input.
The result is that an unhandled exception gets raised—I’m not familiar with how Android handles those, the thread probably just dies unless you set a handler. But putting the parse() call inside a try block should let you recover.

Related

Safe SimpleDateFormat parsing

I have a small block of code which parses response generation time from the response itself and turns it into a date for future purposes. It goes like this:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
Date responseTime = sdf.parse(RStime);
And it almost works like a charm. To be precise, it works 99.9% of the time, with the exception of one case: When the millisecond part is 000 then the Server doesn't append the .000 milliseconds at all, hence we have a problem.
Now, according to SimpleDateFormat docs if parsing fails, the function returns null. However, I probably misinterpreted it as it just throws an exception.
I am very new to Java and try-catch mechanisms, so could anyone please provide an elegant good-practice solution for handling such cases?
Thanks!
java.time
String rsTime = "2018-04-09T10:47:16.999-02:00";
OffsetDateTime responseTime = OffsetDateTime.parse(rsTime);
System.out.println("Parsed date and time: " + responseTime);
Output from this snippet is:
Parsed date and time: 2018-04-09T10:47:16.999-02:00
It works just as well for the version with the 000 milliseconds omitted:
String rsTime = "2018-04-09T10:47:16-02:00";
Parsed date and time: 2018-04-09T10:47:16-02:00
The classes you used, SimpleDateFormat and Date, are poorly designed and long outdated (the former in particular notoriously troublesome). So it is not only in this particular case I recommend using java.time, the modern Java date and time API, instead. However, the strings from your server are in ISO 8601 format, and OffsetDateTime and the other classes of java.time parse this format as their default, that is, without any explicit formatter, which already makes the task remarkably easier. Furthermore, in the standard the fractional seconds are optional, which is why both the variants of the string are parsed without any problems. OffsetDateTime also prints ISO 8601 back from it’s toString method, which is why in both cases a string identical to the parsed one is printed.
Only in case you indispensably need an old-fashioned Date object for a legacy API that you cannot change just now, convert like this:
Instant responseInstant = responseTime.toInstant();
Date oldfashionedDateObject = Date.from(responseInstant);
System.out.println("Converted to old-fashioned Date: " + oldfashionedDateObject);
Output on my computer in Europe/Copenhagen time zone is:
Converted to old-fashioned Date: Mon Apr 09 14:47:16 CEST 2018
Link: Oracle tutorial: Date Time explaining how to use java.time.
According to the SimpleDateFormat doc that you mentioned the parse method:
public Date parse(String text, ParsePosition pos)
Throws:
NullPointerException - if text or pos is null.
So one option is to catch that exception and do what you need inside the catch, for example:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
try {
Date responseTime = sdf.parse(RStime, position);
} catch (NullPointerException e) {
e.printStackTrace();
//... Do extra stuff if needed
}
Or the inherited method from DateFormat:
public Date parse(String source)
Throws:
ParseException - if the beginning of the specified string cannot be
parsed.
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
try {
Date responseTime = sdf.parse(RStime);
} catch (ParseException e) {
e.printStackTrace();
//... Do extra stuff if needed
}
Is it actually an exceptional situation? If it is not then you probably shouldn't use exceptions in that case. In my opinion it is normal that time can end with .000ms. In this case you can check if the string contains . (dot) and if not append .000 to the end.
if(!RStime.contains(".")){
RStime+=".000";
}
Edit: I've forgot about time zone in the time String. You probably need something a little bit more complicated for that. Something like this should do it:
if(!RStime.contains(".")){
String firstPart = RStime.substring(0, 21);
String secondPart = RStime.substring(21);
RStime = firstPart + ".000" + secondPart;
}
You can check for a dot and then use the first or second format:
String timeString = "2018-04-09T10:47:16.999-02:00";
//String timeString = "2018-04-09T10:47:16-02:00";
String format = timeString.contains(".") ? "yyyy-MM-dd'T'HH:mm:ss.SSSXXX" : "yyyy-MM-dd'T'HH:mm:ssXXX";
Date responseTime = new SimpleDateFormat(format).parse(timeString);
System.out.println("responseTime: " + responseTime);
If you comment-out the first line and comment-in the second and run it again, it will both print out:
responseTime: Mon Apr 09 14:47:16 CEST 2018
By the way:
Java 7 (the version you use obviously) returns a java.text.ParseException: Unparseable date: "2018-04-09T10:47:16-02:00"
Optionals are supported since Java 8.

SimpleDateFormat giving wrong date and time after some time of deployment

I have 2 files In my code :
File 1 Content :
public static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
public static final SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
File 2 Content :
sdf.format(formatter.parse("2015-02-02")));
Issue : Line above in file 2 prints "2015-02-02 12:00:00" initially for few hours , but after that it prints "2015-02-01 06:00:00" .
Any idea what could be the issue here.
Additional info :
My server is running on some cloud machine located in US .
new java.util.Date( ) gives UTC timezone value correctly all the time.
Server is started using command java -jar xyz.jar.
There are other files which are using sdf and formatter variables.
I am unable to reproduce this on local machine.
Once the issue starts happening on servers, it shows wrong date time until server is restarted.
If you check the official Oracle documentation, it says that
Date formats are not synchronized. It is recommended to create
separate format instances for each thread. If multiple threads access
a format concurrently, it must be synchronized externally.
By looking at your code, you seem to be reusing the same instance across multiple threads. That is incorrect!!!
Either maintain a pool of formatters OR synchronize the access (not recommended) OR you can create a new instance every time.
The comments by Nathan Hughes and myself are good enough to be combined into an answer: Use java.time, the modern date time API, and specifically its DateTimeFormatter.
public static final DateTimeFormatter printFormatter
= DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss");
Now your formatting may for example go like this:
String stringToPrint = LocalDate.parse("2015-02-02")
.atStartOfDay(ZoneOffset.UTC)
.format(printFormatter);
System.out.println(stringToPrint);
This prints:
2015-02-02 00:00:00
In the format conversion code I am taking advantage of the fact that your original string, 2015-02-02, is in the standard ISO 8601 format for a date. LocalDate parses this format as its default, that is, without any explicit formatter.
What went wrong in your code?
It would seem from your question that there are two likely explanations for the behaviour you have observed:
One of the other classes of the program on the server that uses the two formatters, sets the time zone of one of them, for example to America/Chicago.
Two or more threads use the formats simultaneously, which causes one of them to behave incorrectly.
The observed behaviour, an error of 6 hours, where after it has turned up, it continues until server restart, seems more consistent with the first explanation, which you also confirmed in your own answer, and thank you for doing that.
Contrary to SimpleDateFormat the modern DateTimeFormatter is thread-safe, which prevents any thread problems, and immutable, which prevents other classes from modifying the formatter. So it solves your problem in both cases.
As an aside, I think you are aware of your incorrect use of lowercase hh in the format pattern string. hh is for hour within AM or PM from 01 through 12, whereas you need uppercase HH for hour of day from 00 through 23 (this goes both for SimpleDateFormat and for DateTimeFormatter).
Link: Oracle tutorial: Date Time explaining how to use java.time.
Timezone was getting set for sdf by some piece of code in another api , which was causing the issue.Here is sample example to replicate the issue locally :
import java.text.SimpleDateFormat;
import java.util.TimeZone;
public class SimpleDateFormatTExample {
private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
private static SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
private static String timeZone = "CST";
public static void main(String[] args) {
//-Duser.timezone=UTC
try {
String dateTimeString1 = sdf.format(formatter.parse("2018-01-01"));
System.out.println("Thread Main->> " + dateTimeString1);
//output : Thread Main->> 2018-01-01 12:00:00
} catch (Exception e) {
e.printStackTrace();
}
new Thread(() -> {
try {
//timezone is changed by another thread
sdf.setTimeZone(TimeZone.getTimeZone(timeZone));
String dateTimeString = sdf.format(formatter.parse("2018-01-01"));
System.out.println("Thread child->> " + dateTimeString);
//output : Thread child->> 2017-12-31 06:00:00
} catch (Exception e) {
e.printStackTrace();
}
}).start();
try {
Thread.sleep(1000);
String dateTimeString1 = sdf.format(formatter.parse("2018-02-15"));
System.out.println("Thread Main:After timezone changes by another thread->> " + dateTimeString1);
//output : Thread Main:After timezone changes by another thread->> 2018-02-14 06:00:00
} catch (Exception e) {
e.printStackTrace();
}
}
}

Kotlin/Java SimpleDateFormat giving weird values

Here is the issue I'm having. In my kotlin code (in an Android project), I have something like this:
val rDate = Util.formatDateAsIso8601(Date())
The formatDateAsIso8601(Date inputDate) is a Java method and looks like this:
public static String formatDateAsIso8601(final Date inputDate) {
TimeZone tz = TimeZone.getDefault();
mDateFormat.setTimeZone(tz);
return mDateFormat.format(inputDate);
}
where
mDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.ENGLISH);
Once I get rDate, it populates an Object field and then serialized to json. The issue is that the value for the date is really weird. The first issue I saw was getting a date of 2018-11-31, which is an invalid date. Now I'm seeing values with extra 0's, such as '2018-11-007T20:09:26.533-0500' and '2018-0011-007T020:18:00.367-0500' and even '2018-0011-007T020:27:22.712-0500'. This issue is happening sporadically. Most of the time the dates are fine, but sometimes there are quite a few instances of this stuff happening, and it really only started happening fairly relatively recently.
Any ideas what is going on here?
--Edit--
I now create the SimpleDateFormat object with every call, but I am still getting these weird date values. Here is the new format method:
public static String formatDateAsIso8601(final Date inputDate) {
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.ENGLISH);
TimeZone tz = TimeZone.getDefault();
dateFormat.setTimeZone(tz);
return dateFormat.format(inputDate);
}
SimpleDateFormat is not thread safe so it should not be shared. Create new date format object in each call to formatDateAsIso8601.

What is the Standard way to Parse different Dates passed as Query-Params to the REST API?

I am working on a REST API which supports Date as a query param. Since it is Query param it will be String. Now the Date can be sent in the following formats in the QueryParams:
yyyy-mm-dd[(T| )HH:MM:SS[.fff]][(+|-)NNNN]
It means following are valid dates:
2017-05-05 00:00:00.000+0000
2017-05-05 00:00:00.000
2017-05-05T00:00:00
2017-05-05+0000
2017-05-05
Now to parse all these different date-times i am using Java8 datetime api. The code is as shown below:
DateTimeFormatter formatter = new DateTimeFormatterBuilder().parseCaseInsensitive()
.append(DateTimeFormatter.ofPattern("yyyy-MM-dd[[ ][['T'][ ]HH:mm:ss[.SSS]][Z]"))
.toFormatter();
LocalDateTime localDateTime = null;
LocalDate localDate = null;
ZoneId zoneId = ZoneId.of(ZoneOffset.UTC.getId());
Date date = null;
try {
localDateTime = LocalDateTime.parse(datetime, formatter);
date = Date.from(localDateTime.atZone(zoneId).toInstant());
} catch (Exception exception) {
System.out.println("Inside Excpetion");
localDate = LocalDate.parse(datetime, formatter);
date = Date.from(localDate.atStartOfDay(zoneId).toInstant());
}
As can be seens from the code I am using DateTimeFormatter and appending a pattern. Now I am first trying to parse date as LocalDateTime in the try-block and if it throws an exception for cases like 2017-05-05 as no time is passed, I am using a LocalDate in the catch block.
The above approach is giving me the solution I am looking for but my questions are that is this the standard way to deal with date sent as String and is my approach is in line with those standards?
Also, If possible what is the other way I can parse the different kinds of date (shown as the Valid dates above) except some other straightforward solutions like using an Array list and putting all the possible formats and then using for-loop trying to parse the date?
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ISO_LOCAL_DATE)
// time is optional
.optionalStart()
.parseCaseInsensitive()
.appendPattern("[ ]['T']")
.append(DateTimeFormatter.ISO_LOCAL_TIME)
.optionalEnd()
// offset is optional
.appendPattern("[xx]")
.parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
.parseDefaulting(ChronoField.OFFSET_SECONDS, 0)
.toFormatter();
for (String queryParam : new String[] {
"2017-05-05 00:00:00.000+0000",
"2017-05-05 00:00:00.000",
"2017-05-05T00:00:00",
"2017-05-05+0000",
"2017-05-05",
"2017-05-05T11:20:30.643+0000",
"2017-05-05 16:25:09.897+0000",
"2017-05-05 22:13:55.996",
"2017-05-05t02:24:01"
}) {
Instant inst = OffsetDateTime.parse(queryParam, formatter).toInstant();
System.out.println(inst);
}
The output from this snippet is:
2017-05-05T00:00:00Z
2017-05-05T00:00:00Z
2017-05-05T00:00:00Z
2017-05-05T00:00:00Z
2017-05-05T00:00:00Z
2017-05-05T11:20:30.643Z
2017-05-05T16:25:09.897Z
2017-05-05T22:13:55.996Z
2017-05-05T02:24:01Z
The tricks I am using include:
Optional parts may be included in either optionalStart/optionalEnd or in [] in a pattern. I use both, each where I find it easier to read, and you may prefer differently.
There are already predefined formatters for date and time of day, so I reuse those. In particular I take advantage of the fact that DateTimeFormatter.ISO_LOCAL_TIME already handles optional seconds and fraction of second.
For parsing into an OffsetDateTime to work we need to supply default values for the parts that may be missing in the query parameter. parseDefaulting does this.
In your code you are converting to a Date. The java.util.Date class is long outdated and has a number of design problems, so avoid it if you can. Instant will do fine. If you do need a Date for a legacy API that you cannot change or don’t want to change just now, convert in the same way as you do in the question.
EDIT: Now defaulting HOUR_OF_DAY, not MILLI_OF_DAY. The latter caused a conflict when only the millis were missing, but it seems the formatter is happy with just default hour of day when the time is missing.
I usually use the DateUtils.parseDate which belongs to commons-lang.
This method looks like this:
public static Date parseDate(String str,
String... parsePatterns)
throws ParseException
Here is the description:
Parses a string representing a date by trying a variety of different parsers.
The parse will try each parse pattern in turn. A parse is only deemed successful if it parses the whole of the input string. If no parse patterns match, a ParseException is thrown.
The parser will be lenient toward the parsed date.
#Configuration
public class DateTimeConfig extends WebMvcConfigurationSupport {
/**
* https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#format-configuring-formatting-globaldatetimeformat
* #return
*/
#Bean
#Override
public FormattingConversionService mvcConversionService() {
DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService(false);
conversionService.addFormatterForFieldAnnotation(new NumberFormatAnnotationFormatterFactory());
// Register JSR-310 date conversion with a specific global format
DateTimeFormatterRegistrar dateTimeRegistrar = new DateTimeFormatterRegistrar();
dateTimeRegistrar.setDateTimeFormatter(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
dateTimeRegistrar.setDateTimeFormatter(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
dateTimeRegistrar.setDateTimeFormatter(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'"));
dateTimeRegistrar.registerFormatters(conversionService);
// Register date conversion with a specific global format
DateFormatterRegistrar dateRegistrar = new DateFormatterRegistrar();
dateRegistrar.setFormatter(new DateFormatter("yyyy-MM-dd"));
dateRegistrar.setFormatter(new DateFormatter("yyyy-MM-dd HH:mm:ss"));
dateRegistrar.setFormatter(new DateFormatter("yyyy-MM-dd'T'HH:mm:ss'Z'"));
dateRegistrar.registerFormatters(conversionService);
return conversionService;
}
}

Java util.numberFormatException for input string: "2014-01-12 05-44-56"

I'm new in OFBiz, and Java. I used bellow block of code for checking date time input and use that for searching in table.
Timestamp strtDate = UtilDateTime.getTimestamp((String)request.getParameter("strt_date"));
if(strtDate != null)
{
// then here i used the date for taking data.
}
When i fill the date time field of form to search or when no date is selected for searching error occure that show numberFormatException, so how i can solve that? thanks for any help and guide.
Based on the Apache ofbiz API it looks like UtilDateTime#getTimestamp(String) expects milliseconds value. You are passing in "2014-01-12 05-44-56". You need to parse your date first. With pure pre 1.8 java (keep in mind that formatters aren't thread safe):
String dateString = "2014-01-12 05-44-56";
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss");
Date date = formatter.parse(dateString);
UtilDateTime.getTimestamp(date.getTime());
Since java 1.8 (highly recommended to switch if you can!):
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH-mm-ss");
ZonedDateTime date = ZonedDateTime.parse(text, formatter);
long millis = date.toInstant().toEpochMilli();
you have to pass time in milliseconds not as you are passing.
you can check code also :
public static Timestamp getTimestamp(String milliSecs) throws NumberFormatException {
return new Timestamp(Long.parseLong(milliSecs));
}
it will parse the data in long which you are passing and that should be valid long value.
request.getParameter("strt_date") will anyways return String, so no need to cast it explicitly to String. Moreover, there will be a contract between Client & Server on the required Date Format. So you have to parse the String-Date in the same format using SimpleDateFormat. Code outlook will look like bellow:
SimpleDateFormat formatter = new SimpleDateFormat("contract-date-format");
Date date = formatter.parse(request.getParameter("strt_date"));
UtilDateTime.getTimestamp(date.getTime());

Categories

Resources