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.
Related
I am implementing an application that gets the data from certin endponts in json formats and tries to deserialize tem to Java Objects but I have problems with the parsing of the date in the JSON.This is how the Date looks like in the JSON: "/Date(1633122000000+0300)/" and I cannot find information in Google how to successfully parse this format.
{
"Date": "/Date(1633122000000+0300)/",
"Filled": 0,
"Needed": 0,
"Paid": 0
}
This is the pojo I use to deserialize the data to using Jackson ObjectMapper:
import java.util.Date;
#Data
#AllArgsConstructor
#NoArgsConstructor
public class TimeByDateSheet {
#JsonProperty("Date")
#JsonFormat(timezone = "GMT+03:00")
#JsonDeserialize(using = DateDeserializer.class, as=Date.class)
private Date date;
#JsonProperty("Filled")
private Long filled;
#JsonProperty("Needed")
private Long needed;
#JsonProperty("Paid")
private Integer paid;
}
And here is my DateDeserializer:
#SuppressWarnings("serial")
public class DateDeserializer extends JsonDeserializer<Date> {
#Override
public Date deserialize(JsonParser jsonParser, DeserializationContext context)
throws IOException, JsonProcessingException {
SimpleDateFormat simpleDateFormat =
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss zzz", Locale.getDefault());
String dateStr = jsonParser.getText();
Date date;
try{
date = simpleDateFormat.parse(dateStr);
}catch(ParseException e){
throw new RuntimeException(e);
}
return date;
}
}
But it does not work correctly. I get the following exception:
Connected to the target VM, address: '127.0.0.1:52760', transport: 'socket'
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: java.text.ParseException: Unparseable date: "/Date(1633035600000+0300)/" (through reference chain: java.util.ArrayList[0]->com.dataart.forecasts.pojo.timebydate.TimeByDateSheet["Date"])
at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:392)
at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:351)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.wrapAndThrow(BeanDeserializerBase.java:1821)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:315)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:176)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer._deserializeFromArray(CollectionDeserializer.java:355)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:244)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:28)
at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:322)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4675)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3630)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3613)
at com.dataart.forecasts.DataProcessor.deserializeTimeByDateSheetsList(DataProcessor.java:198)
at com.dataart.forecasts.ForecastReportApplication.main(ForecastReportApplication.java:50)
Caused by: java.lang.RuntimeException: java.text.ParseException: Unparseable date: "/Date(1633035600000+0300)/"
at com.dataart.forecasts.DateDeserializer.deserialize(DateDeserializer.java:28)
at com.dataart.forecasts.DateDeserializer.deserialize(DateDeserializer.java:16)
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:129)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:313)
... 10 more
Caused by: java.text.ParseException: Unparseable date: "/Date(1633035600000+0300)/"
at java.base/java.text.DateFormat.parse(DateFormat.java:395)
at com.dataart.forecasts.DateDeserializer.deserialize(DateDeserializer.java:26)
... 13 more
Could someone help me, please. I searched a lot in internet but could not find a solution.
Thank you in advance! :)
It looks like there is a problem generating the JSON. I really don't think you want to have the dates formatted like that. Right now, you have some odd text surrounding a unix timestamp in milliseconds followed by a zone offset. You are also using the old and rather frowned-upon Date and SimpleDateFormat classes rather than the newer java.time API. However, it is possible to deserialize your date format. Here is one way:
public class DateDeserializer extends JsonDeserializer<Date> {
#Override
public Date deserialize(JsonParser jsonParser, DeserializationContext context)
throws IOException, JsonProcessingException {
Pattern pattern = Pattern.compile("/Date\\((\\d+)([+-]\\d+)\\)/");
Matcher matcher = pattern.matcher(jsonParser.getText());
if (matcher.find()) {
String timestamp = matcher.group(1);
String offset = matcher.group(2);
Instant instant = Instant.ofEpochMilli(Long.parseLong(timestamp));
ZonedDateTime zdt = instant.atZone(ZoneId.of(offset));
instant = zdt.toInstant();
return Date.from(instant);
} else {
throw new RuntimeException("Invalid format: " + jsonParser.getText());
}
}
}
java.time
For this answer I am assuming:
The time in your JSON may come with or without the UTC offset.
You can go all-in on java.time, the modern Java date and time API, and declare your variable to be of type Instant or OffsetDateTime, for example (not Date).
For JSON that comes with an offset such as +0300 declare your variable an OffsetDateTime. Then use the following deserializer.
public class OdtDeserializer extends JsonDeserializer<OffsetDateTime> {
private static final DateTimeFormatter JSON_DATE_FORMATTER = new DateTimeFormatterBuilder()
.appendLiteral("/Date(")
.appendValue(ChronoField.INSTANT_SECONDS)
.appendValue(ChronoField.MILLI_OF_SECOND, 3)
.appendOffset("+HHMM", "Z")
.appendLiteral(")/")
.toFormatter();
#Override
public OffsetDateTime deserialize(JsonParser jsonParser, DeserializationContext context)
throws IOException {
String dateStr = jsonParser.getText();
return OffsetDateTime.parse(dateStr, JSON_DATE_FORMATTER);
}
}
For JSON that comes without offset like /Date(1636510000000)/ declare your variable Instant. Use a similar deserializer. Leave out the offset from the formatter. Parse into an Instant — the syntax is a bit different.
public class InstantDeserializerWIthoutOffset extends JsonDeserializer<Instant> {
private static final DateTimeFormatter JSON_DATE_FORMATTER = new DateTimeFormatterBuilder()
.appendLiteral("/Date(")
.appendValue(ChronoField.INSTANT_SECONDS)
.appendValue(ChronoField.MILLI_OF_SECOND, 3)
.appendLiteral(")/")
.toFormatter();
#Override
public Instant deserialize(JsonParser jsonParser, DeserializationContext context)
throws IOException {
String dateStr = jsonParser.getText();
return JSON_DATE_FORMATTER.parse(dateStr, Instant::from);
}
}
For JSON that may come with or without the offset still use Instant and just modify the formatter of the latter deserializer to include an optional offset:
private static final DateTimeFormatter JSON_DATE_FORMATTER = new DateTimeFormatterBuilder()
.appendLiteral("/Date(")
.appendValue(ChronoField.INSTANT_SECONDS)
.appendValue(ChronoField.MILLI_OF_SECOND, 3)
.optionalStart()
.appendOffset("+HHMM", "Z")
.optionalEnd()
.appendLiteral(")/")
.toFormatter();
If you cannot modify your POJO class and need to stay with Date, modify my Instant deserializer into a Date deserializer by changing the declaration and returning a Date like this:
String dateStr = jsonParser.getText();
Instant inst = JSON_DATE_FORMATTER.parse(dateStr, Instant::from);
return Date.from(inst);
Final solution catching both: /Date(1633035600000+0300)/ and /Date(-62135596800000)/ (the latter was also present at one place in oneof the JSONs). Thank you #DavidConrad
#Override
public Date deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException, JsonProcessingException {
String dateString = jsonParser.getText();
Pattern pattern = Pattern.compile("/Date\\((-)?(\\d+)([+-]\\d+)?\\)/");
Matcher matcher = pattern.matcher(dateString);
if (!matcher.find()) {
throw new RuntimeException("Invalid format: " + dateString);
}
String timestamp = matcher.group(2);
String offset = matcher.group(3);
Instant instant = Instant.ofEpochMilli(Long.parseLong(timestamp));
if (nonNull(offset)) {
ZonedDateTime zdt = instant.atZone(ZoneId.of(offset));
instant = zdt.toInstant();
}
return Date.from(instant);
}
#DavidConrad Thank you, I will try your solution.
By the way, for now I made a workaround that works for me for now:
#Override
public Date deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException, JsonProcessingException {
SimpleDateFormat dateFormattter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateStr = jsonParser.getText();
String timeZone = dateStr.substring(dateStr.indexOf("+") + 1, dateStr.indexOf(")"));
String timeZoneShift = String.format("%s:%s",
timeZone.substring(0, timeZone.length()/2),
timeZone.substring(timeZone.length()/2));
dateFormattter.setTimeZone(TimeZone.getTimeZone(String.format("GMT+%s", timeZoneShift)));
Long millis = 0L;
if (dateStr.contains("+") && !dateStr.contains("-")) {
millis = Long.parseLong(dateStr.substring(dateStr.indexOf("(") + 1, dateStr.indexOf("+")));
} else if (dateStr.contains("+") && !dateStr.contains("-")) {
millis = Long.parseLong(dateStr.substring(dateStr.indexOf("(") + 1, dateStr.indexOf(")")));
}
Date date = new Date(millis);
String stringDate= dateFormattter.format(date);
try {
date = dateFormattter.parse(stringDate);
} catch (ParseException e) {
throw new RuntimeException(e);
}
return date;
}
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;
}
}
I use Spring Boot and I try to save some date in MongoDB. My input date is
"2017-08-14T12:59"
I get this error while saving:
Failed to read HTTP message: org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Failed to parse Date value '2017-08-14T12:59': Can not parse date "2017-08-14T12:59.000Z": while it seems to fit format 'yyyy-MM-dd'T'HH:mm:ss.SSS'Z'', parsing fails (leniency? null); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Failed to parse Date value '2017-08-14T12:59': Can not parse date "2017-08-14T12:59.000Z": while it seems to fit format 'yyyy-MM-dd'T'HH:mm:ss.SSS'Z'', parsing fails (leniency? null) (through reference chain:
In my POJO i tried like this:
#JsonDeserialize(using= CustomDateDeserialize.class)
private Date inputDateTime;
and I've implemented Deserializer like this :
private SimpleDateFormat dateFormat = new SimpleDateFormat(
"yyyy-MM-dd HH:mm");
#Override
public Date deserialize(JsonParser paramJsonParser,
DeserializationContext paramDeserializationContext)
throws IOException, JsonProcessingException {
String str = paramJsonParser.getText().trim();
try {
return dateFormat.parse(str);
} catch (ParseException e) {
}
return paramDeserializationContext.parseDate(str);
}
What else I miss here? Any help appreciated.
you need to modify format in your deserializer.
SimpleDateFormat dateFormat = new SimpleDateFormat(
"yyyy-MM-dd'T'HH:mm");
any way simpledatetimeformat is not thread safe. if you java8 use DateTimeFormat.
Why don't you try Instant library
#Field("your_db_id_name")
private Instant inputDateTime;
public void setInputDateTime(Instant inputDateTime) {
this.inputDateTime = inputDateTime;
}
public void getInputDateTime() {
return inputDateTime;
}
You can set Filed by using Instant.now()
You may find this as duplicate question, still I'm not getting what is wrong with following code.
This code I found in several WebSites and in Spring Documentation , any ty-po here in my code ?
I want persist object in file (.ser) , which contains several 'Date' fields, and show details again in my jsp,
The formart requred to me is dd/MM/yyyy i.e. 31/12/1999.
It shows in the format "Wed Jun 01 00:00:00 IST 2016" before persisting in my controller.
I have used CustomDateEditor as follows:
SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
dateFormat.setLenient(false);
webDataBinder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
in above code I have used two parameters to registerCustomEditor() , is this ok ?
or how to do for multiple fields ,
webDataBinder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
should I have to use this above line of code for every field.
I have tried for this one also but no success
webDataBinder.registerCustomEditor(Date.class, new PropertyEditorSupport(){
#Override
public void setAsText(String text) throws IllegalArgumentException {
System.out.println(text+" setAsText");
try {
if( text != "")
//setValue(text);
setValue(new SimpleDateFormat("dd/MM/yyyy").parse(value));
} catch (Exception e) {
setValue(null);
}
}
#Override
public String getAsText() {
System.out.println(getValue()+" getValue");
return new SimpleDateFormat("dd/mm/yyyy").format((Date)getValue());
}
});
In my JSP , I used date picker as:
$('#id-scheme-start-date').datepicker({
dateFormat: 'dd/mm/yy',
});
I have another choice to use JSTL tag in JSP but I do not want to use this at several position.
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);