Can't convert from string to Joda LocalTime with DefaultFormattingConversionService - java

Am unable to convert string to Joda LocalTime with DefaultFormattingConversionService.
If I pass time as "12:00" it says time is too short, but if I pass it as "12:00:00", it says it is malformed.
import org.joda.time.LocalTime;
import org.springframework.format.support.DefaultFormattingConversionService;
public class SpringLocalTimeFormatterTry {
public static void main(String[] args) {
DefaultFormattingConversionService service = new DefaultFormattingConversionService();
try {
System.out.println(service.convert("12:00", LocalTime.class));
}
catch (Exception e) {
System.out.println(e.getMessage());
}
try {
System.out.println(service.convert("12:00:00", LocalTime.class));
}
catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
How to use it correctly or fix?

The vanilla settings of DefaultFormattingConversionService use platform default locale, which, I assume from the error, are the same as mine, ie. English. That means, that for time you need to add the AM/PM indicator. This works for me:
System.out.println(service.convert("10:12 am", LocalTime.class));
>> 10:12:00.000
To handle your desired time format, you can add an extra converter:
service.addConverter(new Converter<String, LocalTime>() {
#Override
public LocalTime convert(String source) {
return LocalTime.parse(source);
}
});
Then, both examples pass:
System.out.println(service.convert("12:00", LocalTime.class));
>> 12:00:00.000
System.out.println(service.convert("12:00:00", LocalTime.class));
>> 12:00:00.000
You can skip registering the default converters by creating the service with
new DefaultFormattingConversionService(false);
Finally, I assume in the production code you are actually getting the ConversionService from the ConversionServiceFactoryBean, so you can configure that as follows:
#Bean
public ConversionServiceFactoryBean conversionService() {
ConversionServiceFactoryBean conversionServiceFactoryBean = new ConversionServiceFactoryBean();
Set<Converter<?, ?>> myConverters = new HashSet<>();
myConverters.add(new Converter<String, LocalTime>() {
#Override
public LocalTime convert(String source) {
return LocalTime.parse(source);
}
});
conversionServiceFactoryBean.setConverters(myConverters);
return conversionServiceFactoryBean;
}

Try this:
DateTimeFormatter dtf = DateTimeFormat.forPattern("HH:mm:ss");
LocalTime localTime = dtf.parseLocalTime("12:00:00");
System.out.println("Time"+localTime);

Related

MM/dd/yyyy date format gives incorrect date

I am providing a date input in MM/dd/yyyy format, the format produces an incorrect date on UI, this only happens with MM/dd/yyyy format, however, it is working fine if input will be in dd/MM/yyyy.
eg: if today's date 04/08/2020, then it is rendered as 07/09/2021, which is of course wrong date
The application using extjs3 as frontend and so the date picker.
function insSKUDate(element,textElement,img){
var selectHandler = function(myDP, date) {
var field = document.getElementById(textElement);
field.value = date.format('m/d/Y');
field.text = date.format('m/d/Y');
dateField = document.getElementById(textElement);
myDP.hide();
};
var myDP = new Ext.DatePicker(
{
startDay: 1,
minDate: new Date(),
listeners: {
'select':selectHandler
}
}
);
var innerdata = document.getElementById(element);
innerdata.innerHTML="";
myDP.render(element);
var clickHandler = function() {
myDP.show();
};
Ext.EventManager.on(img.id, 'click', clickHandler);
}
somewhere in HTML form
<td width="20%">
<input type="text" class="textbox"
value="<s:property value='skulaunchDate'/>"
name="skulaunchDate"
id="skulaunchDate" readonly="readonly"/>
<img style="vertical-align:text-bottom" src="images/calimg.jpg"
width="16" height="16"
onClick="insSKUDate('skulaunchDateSpan', 'skulaunchDate', this)"
id="skulaunchDateIcon"/>
<span id="skulaunchDateSpan"
style="position:absolute;z-index:1;width:50px;"></span>
<div id="skulaunchDateErrorMsg" align="left"
style="color:#FF0000"/>
</td>
Somewhere in my Struts' execute()
public String getSkulaunchDate() {
return skulaunchDate;
}
public void setSkulaunchDate(String skulaunchDate) {
this.skulaunchDate = skulaunchDate;
}
if (bean.getSkulaunchDate() != null)
setSkulaunchDate(dateUtil.getSQLDateToString(bean.getSkulaunchDate(), "MM/dd/yyyy"));//Replace dd/MM/yyyy to MM/dd/yyyy
// setSkulaunchDate(getSkuLaunchDateStrFormat(bean,dateUtil));//Replace dd/MM/yyyy to MM/dd/yyyy
System.out.println("3.getSkulaunchDate----"+ getSkulaunchDate());
setContactPersonName(bean.getContactPersonName());
setContactPersonPosition(bean.getContactPersonPosition());
setContactPersonEmail(bean.getContactPersonEmail());
setContactPersonNumber(bean.getContactPersonNumber());
setRemarks(bean.getRemarks());
setTpRemarks(bean.getTpRemarks());
try {
if(request_bean.getRequestDate() != null){
setRequest_date(dateUtil.getUtilDateToString(request_bean.getRequestDate(), "MM/dd/yyyy"));//Replace dd/MM/yyyy to MM/dd/yyyy
}
} catch (Exception ex) {
logger.error(ex.getStackTrace());
}
I have doubt on the below method, but it looks fine
public String getSQLDateToString(java.sql.Date date, String format) {
String result = null;
try {
result = new SimpleDateFormat(format).format(date);
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
It is found that the input is in SQL date having some problem while parsing, apart from this util date is working fine.
please suggest me the solution to rectify this.
The legacy date-time API is known to be confusing and error-prone. Since you are using Java-7, unfortunately, either you will have to live with it or you can backport the code using modern date-time API to Java-7 using ThreeTen-Backport library.
I recommend you address the following problems with your code:
Never use a date-time parsing/formatting API without a Locale.
If your method can not handle an exception (i.e. can not do something to recover from the failure), it should simply throw the exception so that the calling method can get an opportunity to handle it appropriately. Therefore, using e.printStackTrace() in your method, getSQLDateToString is a bad idea. By the way, SimpleDateFormat#format does not throw an exception. Probably, you got confused with SimpleDateFormat#parse which throws ParseException.
The following code incorporates these comments:
import java.text.SimpleDateFormat;
import java.util.Locale;
public class Main {
public static void main(String[] args) {
// Today
java.sql.Date date = new java.sql.Date(System.currentTimeMillis());
System.out.println(getSQLDateToString(date, "MM/dd/yyyy"));
System.out.println(getSQLDateToString(date, "dd/MM/yyyy"));
}
public static String getSQLDateToString(java.sql.Date date, String format) {
return new SimpleDateFormat(format, Locale.ENGLISH).format(date);
}
}
Output:
02/07/2021
07/02/2021
Using the modern date-time API
Use java.sql.Date#toLocalDate to convert a java.sql.Date to LocalDate.
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class Main {
public static void main(String[] args) {
// Today
java.sql.Date date = new java.sql.Date(System.currentTimeMillis());
System.out.println(getSQLDateToString(date, "MM/dd/yyyy"));
System.out.println(getSQLDateToString(date, "dd/MM/yyyy"));
}
public static String getSQLDateToString(java.sql.Date date, String format) {
LocalDate localDate = date.toLocalDate();
DateTimeFormatter dtf = DateTimeFormatter.ofPattern(format, Locale.ENGLISH);
return dtf.format(localDate);
}
}
Output:
02/07/2021
07/02/2021
Learn about the modern date-time API from Trail: Date Time.

How to enforce a date value input in a REST API?

The user needs to make a POST to /api/date with something like March 13, 2019 or 08/19/2020. As long as it's a date, it should be accepted.
I have something like this (Using Dropwizard framework)
#POST
public void post(String date)
{
validateDate(date);
//continue
}
private void validateDate(String date)
{
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
try
{
LocalDateTime.parse(date, formatter);
}
catch (DateTimeParseException e)
{
//not a date
}
}
I'm not sure if I'm in the right approach, there must be a better way to validate strings as dates.
You can accept multiple formats for a date time using the optional syntax ([<your format>])*. eg
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(
"[yyyy-MM-dd][dd-MM-yyyy][MMMM dd, yyyy]");
EDIT: It is not really clear if you want to know how to validate dates correctly or how to handle invalid inputs to your REST API. My answer shows the latter.
You should use a return value for your post method. You can return javax.ws.rs.core.Response, with that you can control the HTTP code and response object you want to return.
On success, you would normally return the created object with a 200 success code.
On failure, you would return an error code (like 400 Bad request) with a detailed error message ("Date must be in the format yyyy-MM-dd").
To create the response, you can use the ResponseBuilder.
Example:
Response.ok( yourObject ).build(); //success
Response.status( Status.BAD_REQUEST ).entity( yourErrorMessageObject ).build(); // failure
So I would change the code to this:
#POST
public Response post(String date)
{
if(!isDateValid(date)){
return Response.status( Status.BAD_REQUEST ).entity( buildErrorMessage()).build();
}
//continue
Response.ok().build(); // returns nothing on success (like void)
}
private boolean isDateValid(String date)
{
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
try
{
LocalDateTime.parse(date, formatter);
return true;
}
catch (DateTimeParseException e)
{
//not a date
return false;
}
}

How to override Gson default DateTypeAdapter?

I created a custom Date deserializer:
private static class DateJsonDeserializer implements JsonDeserializer<Date>{
#Override
public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
for (String format : DATE_FORMATS) {
try {
SimpleDateFormat df = new SimpleDateFormat(format);
return df.parse(json.getAsString());
} catch (ParseException e) {
}
}
throw new JsonParseException("Unparseable date: \"" + json.getAsString()
+ "\". Supported formats: " + Arrays.toString(DATE_FORMATS));
}
}
I added this to my gsonBuilder:
gsonBuilder.registerTypeAdapter(Date.class, new DateJsonDeserializer());
But if I would like to parse a date I see the built in DateTypeAdapter class used, why? I would like to use my date deserializer.
I use retrofit 2.1.0, Gson 2.8.0, converter-gson 2.1.0
are you sure that Date.class is the correct class? Maybe it uses a subclass of Date.class ?

How to setup LocalDateTime in Spring XML configuration

My problem relates to the following class:
public class MyClass {
private LocalDateTime startDate;
}
I am trying to setup the startDate property of this bean using Spring XML configuration:
<property name="startDate">
<value>2000-01-01</value>
</property>
I get an error:
Cannot convert value of type [java.lang.String] to required type [java.time.LocalDateTime] for property 'startDate'
Is it possible to use Spring to do this conversion? I found examples on the net how to do that for Date object, however, LocalDateTime doesn't have a constructor taking a string (and the solution seems to need such constructor). LocalDateTime is constructed by using the static method LocalDateTime.parse.
Using the annotations #DateTimeFormat, like:
public class MyClass {
#DateTimeFormat(iso=ISO.LOCAL_DATE_TIME)
private LocalDateTime startDate;
}
is not a solution, since MyClass has to be available outside Spring.
Thanks in advance
you can register your conversion. like the following code, the following will convert a string to LocalDateTime
class CustomLocalDateTimeEditor extends PropertyEditorSupport {
private final boolean allowEmpty;
private final int exactDateLength;
public CustomLocalDateTimeEditor( boolean allowEmpty) {
this.allowEmpty = allowEmpty;
this.exactDateLength = -1;
}
public CustomLocalDateTimeEditor(boolean allowEmpty, int exactDateLength) {
this.allowEmpty = allowEmpty;
this.exactDateLength = exactDateLength;
}
#Override
public void setAsText(String text) throws IllegalArgumentException {
if (this.allowEmpty && !StringUtils.hasText(text)) {
setValue(null);
}
else if (text != null && this.exactDateLength >= 0 && text.length() != this.exactDateLength) {
throw new IllegalArgumentException("Could not parse date: it is not exactly" + this.exactDateLength + "characters long");
}
else {
try {
setValue(LocalDateTime.parse(text));
}
catch (DateTimeParseException ex) {
throw new IllegalArgumentException("Could not parse date: " + ex.getMessage(), ex);
}
}
}
#Override
public String getAsText() {
LocalDateTime value = LocalDateTime.parse(String.valueOf(getValue()));
return (value != null ? value.toString() : "");
}
}
Here is what I ended up with based on Javy's comment:
import java.beans.PropertyEditorSupport;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
public class CustomLocalDateTimeEditor extends PropertyEditorSupport {
public CustomLocalDateTimeEditor() {
}
private LocalDateTime parseText(String text) {
LocalDateTime ldt;
try {
ldt = LocalDateTime.parse(text);
} catch(Exception ee) {
ldt = null;
}
if(ldt == null) {
try {
ldt = LocalDateTime.of(LocalDate.parse(text), LocalTime.of(0, 0));
} catch(Exception ee) {
ldt = null;
}
}
return ldt;
}
#Override
public void setAsText(String text) throws IllegalArgumentException {
setValue(parseText(text));
}
#Override
public String getAsText() {
LocalDateTime value = parseText(String.valueOf(getValue()));
return (value != null ? value.toString() : "");
}
}
And in the XML I have the following:
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="customEditors">
<map>
<entry key="java.time.LocalDateTime" value="com.mycompany.CustomLocalDateTimeEditor" />
</map>
</property>
</bean>
<bean id="myClass" class="MyClass">
<property name="startDate">
<value>2000-01-01</value>
</property>
</bean>
You can use #DateTimeFormat annotation and provide a pattern. For example
#DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDateTime startDate;
When Spring find the Joda localdatetime, it will convert date in appropriate format.
#DateTimeFormat in spring
Declare a dateFormat bean, in “MyClass” bean, reference “dateFormat” bean as a factory bean. The factory method will call SimpleDateFormat.parse() to convert String into Date object automatically.
reference:
http://www.mkyong.com/spring/spring-how-to-pass-a-date-into-bean-property-customdateeditor/

Conversion of a small XML containfg different data values to POJO

I am currently writing a parser for a simple XML. The XML at its longest contains 18 lines. I am trying to parse it and convert it to a POJO. I know that I can use JAXB or similar libraries, but I felt that considering the size of the XML, that would be an overkill. Also, this is an exercise to think beyond libraries.
An example XML would be:
<machineinfo>
<processorCount>4</processorCount>
<boughtDate>2014-06-09 23:17:49.0</boughtDate>
<installationStatus>COMPLETE</installationStatus>
<machineType>BASIC</machineType>
<osVersion>1849AB48DOED</osVersion>
<serverName>fjv920dk</serverName>
<machineStatus>UP</machineStatus>
<statusPay1>NA</statusPay1>
<statusPay2>NA</statusPay2>
<errorCode>NO_ERROR</errorCode>
<exceptionCode>0</exceptionCode>
<isCloneable>true</isCloneable>
<latestVersion>1849AB48DOED</latestVersion>
<mastermachineName/>
<podName>8D2</podName>
<machineName>machine2</machineName>
</machineinfo>
My core conversion logic is as follows:
if (tagName.equalsIgnoreCase("processorCount")) {
machineInfo.setProcessorCount(new Integer(data).intValue());
} else if (tagName.equalsIgnoreCase("boughtDate")) {
DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss.SSS");
DateTime dt = formatter.parseDateTime(data);
machineInfo.setBoughtDate(dt);
} else if (tagName.equalsIgnoreCase("installationStatus") {
machineInfo.setInstallationStatus(InstallationStatus.valueOf(data));
} else if (tagName.equalsIgnoreCase("installationStatus") {
machineInfo.setInstallationStatus(InstallationStatus.valueOf(data));
}
As you can see, this leads to multiple if conditions. I tried simplifying that by using a Map as follows:
Map <String, Object> map = new HashMap<String, Object>();
map.put("machineName", data);
map.put("machineType", data);
String mapValue = (String) map.get(tagName);
But how do I determine which setter in machineInfo should be invoked?
You should use Java Reflection or Java Beans API for that.
I like to invoke dynamic setters using PropertyDescriptor:
Method writerMethod = new PropertyDescriptor(fieldName, MachineInfo.class).getWriteMethod();
writerMethod.invoke(machineInstance, fieldValue);
Refer to http://docs.oracle.com/javase/7/docs/api/java/beans/PropertyDescriptor.html
Regards,
Bruno
I solved this by using the following snippet:
ConvertUtilsBean convertUtilsBean = new ConvertUtilsBean();
convertUtilsBean.deregister(DateTime.class);
convertUtilsBean.register(new JodaDateTimeConverter(), DateTime.class);
BeanUtilsBean beanUtilsBean = new BeanUtilsBean(convertUtilsBean, new PropertyUtilsBean());
try {
beanUtilsBean.setProperty(machineInfo, qName, temp);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
And by creating the following converter:
class JodaDateTimeConverter implements Converter {
/* (non-Javadoc)
* #see org.apache.commons.beanutils.Converter#convert(java.lang.Class, java.lang.Object)
*/
#SuppressWarnings("unchecked")
public <T> T convert(Class<T> arg0, Object arg1) {
DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss.SSS");
DateTime dt = formatter.parseDateTime((String) arg1);
return (T) dt;
}
}

Categories