PrimeFaces calendar accepts invalid dates as input - java

The problem I am having is with the PrimesFaces 3.4.1 calendar. When using the popup date picker activated either through the button or on input field focus you can only select valid dates which work fine, happy days!
The issues comes when you manually add a date into the input field, if you add an invalid date the PrimeFaces calendar component takes its best guess at converting this into a valid date and then sending it, meaning that back-end validation is a no go. Some interesting translations below:
30/02/2012 becomes 2/6/2014
322/05/2012 becomes 5/10/2038
01/14/2012 becomes 4/1/2012
To recreate this madness have a look at the PrimeFaces Calendar Showcase.
I have seen solution around using the readOnlyInput='true' attribute but that only seems to prevent letters being entered in the field not number or slashes. Below is one instance of the calendar I have implemented:
<p:calendar id="fldDateOfBirth"
value="#{pc_CreateUser.user.dateOfBirth}"
binding="#{pc_CreateUser.dobComp}"
navigator="true"
pattern="dd/MM/yyyy"
maxlength="10"
yearRange="-100"
validator="#{pc_CreateUser.validateDOB}"
title="#{msg.user_date_format_default_tip}"
converterMessage="#{msg.user_error_dob_invalid}"
readOnlyInput="true"
showOn="button" />
Solution wise I am open to any suggestions:
Is this a common issues in PrimeFaces? Is there a trick I can use to
fix it?
Could I use JavaScript to validate the date before it's sent or to
block all user input entirely?
Anything else I haven't thought of!
Thanks in advance, this has been causing me issues for weeks!

The <p:calendar> uses under the covers SimpleDateFormat which in turn uses by default lenient parsing, causing the overflowed values to roll over into the next date metric level. E.g. 32 January would become 1 February, etc.
In plain Java terms, this can be turned off by DateFormat#setLenient(), passing false. See also among others this question: validating a date using dateformat.
In JSF terms, you basically need to provide a custom converter which uses a non-lenient DateFormat. Fortunately, standard JSF already provides such one out the box in flavor of <f:convertDateTime>, so you could just make use of it directly.
<p:calendar ...>
<f:convertDateTime pattern="dd/MM/yyyy" />
</p:calendar>

In faces-config.xml add this
<converter>
<converter-id>localDateConverter</converter-id>
<converter-class>com.utility.LocalDateConverter</converter-class>
</converter>
In the above class i.e LocaldateConverter add this below code
/**
* #param facesContext .
* #param uiComponent .
* #param input .
* #return Object .
*/
#Override
public Object getAsObject(final FacesContext facesContext, final UIComponent uiComponent, final String input) {
if (StringUtils.isBlank(input)) {
return null;
}
final String componentPattern = (String) uiComponent.getAttributes().get("datePattern");
final String patternToUse = componentPattern != null ? componentPattern : CommonConstants.OUTPUT_DATE_FORMAT;
try {
final DateFormat fmt = new SimpleDateFormat(patternToUse);
Date convertedDate = new java.sql.Date(fmt.parse(input).getTime());
return convertedDate;
} catch (Exception e) {
throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Invalid Date Format", null));
}
}
/**
* #param facesContext .
* #param uiComponent .
* #param obj .
* #return String .
*/
#Override
public String getAsString(final FacesContext facesContext, final UIComponent uiComponent, final Object obj) {
if (obj==null) {
return null;
}
final Date date = (Date) obj;
return date.toString();
}

Related

Create ISO 8601 date String with GWT

Here's my code:
public static String getStringFormat(Date inputDate, String timeZone){
String strFormat = null;
try{
final TimeZone computedTimeZone = TimeZone.createTimeZone(TimeZoneInfo.buildTimeZoneData(timeZone));
DateTimeFormat dateTimeFormat = DateTimeFormat.getFormat(DateTimeFormat.PredefinedFormat.ISO_8601);
strFormat = dateTimeFormat.format(inputDate, computedTimeZone);
Date d = new Date(strFormat);
strFormat = dateTimeFormat.format(d, TimeZone.createTimeZone(0));
String[] s = strFormat.split("\\+");
strFormat = s[0];
}catch(Exception e){
Console.log(e.getMessage());
}
return strFormat;
}
For input, new Date() and Etc/GMT+3 this function returns null. What could be wrong?
Error
Error: NullPointerException: undefined
at NBF_g$.QBF_g$ [as createError_0_g$] (NullPointerException.java:40)
at NBF_g$.ub_g$ [as initializeBackingError_0_g$] (Throwable.java:113)
at NBF_g$.bb_g$ (Throwable.java:61)
at NBF_g$.Ib_g$ (Exception.java:25)
at NBF_g$.avq_g$ (RuntimeException.java:25)
at NBF_g$.gfs_g$ (JsException.java:34)
at new NBF_g$ (NullPointerException.java:27)
at new wou_g$ (JSONString.java:43)
The method TimeZoneInfo.buildTimeZoneData(String tzJSON) doesn't accept the name of the zone, but needs a JSON string full of the details of how that zone works. It turns out that the browser doesn't come to you with all of the details of how all time zones work, so your app has to already be prepared to handle them.
GWT ships with all of the timezones (though they are currently a little out of date, and should be updated in this next release), but you have to tell the compiler which ones you want, or it will compile them out. The full list of all possible timezones and their offsets, etc is not small, so I would encourage you to limit the list.
These are stored in the constants interface TimeZoneConstants. Here is how you might use it:
TimeZoneConstants constants = GWT.create(TimeZoneConstants.class);
// This is the shorthand for TimeZone.createTimeZone(TimeZoneInfo.buildTimeZoneData(...))
TimeZone computedTimeZone = TimeZone.createTimeZone(constants.americaAdak());
//...
If you want to use the timezone string instead, say, passed from the server, you could build a map of the possible timezones that are supported. Be aware though that the full map is very large (200KB just for the timezones in the "America/..." group).
computedTimeZone = TimeZone.createTimeZone(constants.americaAdak());
zones.put(computedTimeZone.getID(), computedTimeZone);
computedTimeZone = TimeZone.createTimeZone(constants.americaAnchorage());
zones.put(computedTimeZone.getID(), computedTimeZone);
computedTimeZone = TimeZone.createTimeZone(constants.americaAnguilla());
zones.put(computedTimeZone.getID(), computedTimeZone);
//...
Then you can read out a specific item from the map as needed:
String tzName = Window.prompt("Enter a timezone name", "America/Chicago");
DateTimeFormat dateTimeFormat = DateTimeFormat.getFormat(DateTimeFormat.PredefinedFormat.ISO_8601);
String strFormat = dateTimeFormat.format(inputDate, zones.get(tzName));
//...
In your comment, you clarified the question, that you only need to deal with offsets, not the full TimeZone string format, i.e. Etc/GMT+3, meaning "Offset of +3 hours from GMT". This is easier to handle - simply parse out the +3 into a number, and use the TimeZone.createTimeZone(int timeZoneOffsetInMinutes) method. This will not understand daylight savings time, but that wouldn't be possible without the full name of the timezone or list of offsets, etc (which gets to why that JSON is so large).
//TODO, implement parse(), don't forget about negative numbers
int offsetInHours = parse(timeZone);
TimeZone computedTimeZone = TimeZone.createTimeZone(60 * offsetInHours);
//...

SmartGwt: add days to date

How do you add days to a date in SmartGwt. I found this question and found that I can use CalendarUtil.addDaysToDate(dateToAddTo, daysToAddToDateAsInteger)); but addDaysToDate() is static void. What is the point of a method that can "add days to a date" if it does not return anything?
How do I use this method? I want to do something like this.
Date newDate = dateToAddTo + daysToAddToDate;
Here is a simplified version of my code.
if (listGridRecord.getAttribute("expirationDays") != null) {
CalendarUtil.addDaysToDate((Date) endDate.getValue(), Integer.parseInt(listGridRecord.getAttribute("expirationDays")));
listGridRecord.setAttribute("expirationDate", (Date) endDate.getValue());
} else {
listGridRecord.setAttributeAsJavaObject("expirationDate", null);
}
Here is a link to the javadocs
This method changes the object that is passed as parameter.
Date date = new Date();
long checkBeforeChange = date.getTime();
CalendarUtil.addDaysToDate(date, 1);
long checkAfterChange = date.getTime();
if(checkBeforeChange != checkAfterChange)
Window.alert("It works ;)");
Your code should be something like that:
if (listGridRecord.getAttribute("expirationDays") != null) {
Date tmpDate = endDate.getValue();
CalendarUtil.addDaysToDate(tmpDate, Integer.parseInt(listGridRecord.getAttribute("expirationDays")));
listGridRecord.setAttribute("expirationDate", tmpDate);
} else {
listGridRecord.setAttributeAsJavaObject("expirationDate", null);
}
When doing (Date) endDate.getValue() you get a copy of Date object thus you don't see any change.
I figured out what I was doing wrong with the help of #Adam. I created a new Date variable called expireationDate and set it to (Date) endDate.getValue(); after this I used it to do the calculation.
if (listGridRecord.getAttribute("expirationDays") != null) {
Date expirationDate = (Date) endDate.getValue();
CalendarUtil.addDaysToDate(expirationDate, Integer.parseInt(listGridRecord.getAttribute("expirationDays")));
listGridRecord.setAttribute("expirationDate", expirationDate);
} else {
listGridRecord.setAttributeAsJavaObject("expirationDate", null);
}
First of all, you can wrap all those utility methods you need in your own utility class (private constructor), where e.g. MyDateUtilsClassName.addDays(Date, int) will return new instance, leave parameter unmodified.
When it comes to Date manipulation, in Gwt you can use standard java.util.Date methods, even those deprecated ones like setMinutes, setHours etc. Even when you see com.google.gwt.user.datepicker.client.CalendarUtil method, they are used there.
If it's not server side, but client side, you should not care much about #Deprecated on those methods. Gwt compiles them to javascript anyway. You should be aware of java.util.Calendar. As far as I remember, it is not supported at all.

Java.util.date - printing its object not value

Hello i am using struts2 but my textfield is getting Object from Java.util.date instead of its value
Javascript
start = moment(start).format();
alert(start);
2014-10-31T00:00:00+00:00
but when i try to use value of start in java object , it prints following object
java.util.GregorianCalendar[time=1416382200000,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Ulaanbaatar",offset=28800000,dstSavings=0,useDaylight=false,transitions=48,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2014,MONTH=10,WEEK_OF_YEAR=47,WEEK_OF_MONTH=4,DAY_OF_MONTH=19,DAY_OF_YEAR=323,DAY_OF_WEEK=4,DAY_OF_WEEK_IN_MONTH=3,AM_PM=1,HOUR=3,HOUR_OF_DAY=15,MINUTE=30,SECOND=0,MILLISECOND=0,ZONE_OFFSET=28800000,DST_OFFSET=0]
how can i get 2014-10-31T00:00:00+00:00 in java (struts2 ) object
Struts2 Getter and Setter
#Column(name="EVENT_START")
public Calendar getOrder_employee_start() {
return order_employee_start;
}
public void setOrder_employee_start(Calendar order_employee_start) {
order_employee_start.getTime();
this.order_employee_start = order_employee_start;
}
Are you using <s:date> tag to receive the date value on your jsp?
If not you should either return a String value which you could use directly in your textboxes.
SimpleDateFormat can be used to get the desired result.
For instance-
Calendar cal = Calendar.getInstance();
SimpleDateFormat sdFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
Then call sdFormat.format(cal.getTime())
So, in your case you can do something like sdFormat.format(order_employee_start.getTime())

Javabean with a single date field But need to search on it with date range

I have a Javabean with a date_observed field. Now I am trying to create a form where a user can search for entries between a start and end date.
Should I create another Javabean that extends this bean to have a start and end date field so that I can populate these field from request parameter?
I'd like to cleanly pass a bean to my Dao for SQL string generation and also have a way to do form validation if they enter incorrect date format.
Typically I would do
public void processDate_observed(HttpServletRequest request, Comment comment) {
String _date = FormUtil.getFieldValue(request, FIELD_DATE_OBSERVED);
if (!"".equals(_date) && _date != null) {
try {
Date date = DateUtil.parse(_date);
com.setDate_observed(date);
} catch (ParseException e) {
setError(FIELD_DATE_OBSERVED, e.getMessage());
}
}
}
But my Comment Javabean does not have fields for start_date and end_date
And for dao.search(comment)
public List<Evaluatee> search(Comment comment) {
SQL = ".... where date_observed > ? AND date_observed <= ?
ps.setDate(1, comment.EXTENDED FIELD???)
ps.setDate(2, comment.EXTENDED FIELD???)
...
}
What is the best practice here? Creat a whole new Javabean, extend my original bean or pass along the two date fields to Dao? But then how do you pass form validation back to form if I don't have the dates in a bean?
Is a good practice that in your DAO class you have a serch method with a start and end date as parameters.
public List<Evaluatee> search(String startDate, String endDate) {
///CODE GOES HERE
}
It's not necessary that the DAOs needs to have a Comment object as an argument, the rule is that for each table of the data base need to be a class with the same fields as in the table.

jersey-client throws NPE in ClientResponse.getLastModified()

I use jersey-client to consume a REST service.
I need both the requested Entity and the Last-Modified header.
So I do the following:
ClientResponse response = webResource.get(ClientResponse.class);
Person person = response.getEntity(Person.class);
That works. I get a response and I can marshal the Entity (wich is XML) into my POJO.
When I debug and take a look into the Headers of the response, then I see that there is a Last-Modified header set.
But when I try to retrieve the date via
response.getLastModified();
I get a NPE somewhere in URLConnectionClientHandler.
Has anyone a clue what I do wrong?
edit: as requested the trace
java.lang.NullPointerException: null
at com.sun.jersey.api.client.ClientResponse.getLastModified(ClientResponse.java:647) ~[jersey-client-1.12.jar:1.12]
at a.o.u.user.dao.impl.uds.PersonenUdsClient.getPerson(PersonenUdsClient.java:103) ~[um-user-2.5.0-Beta1-SNAPSHOT.jar:na]
at a.o.u.user.dao.impl.UserDaoUdsImpl.mergeWithUdsUser(UserDaoUdsImpl.java:282) ~[um-user-2.5.0-Beta1-SNAPSHOT.jar:na]
at a.o.u.user.dao.impl.UserDaoUdsImpl.getUserWithEmail(UserDaoUdsImpl.java:124) ~[um-user-2.5.0-Beta1-SNAPSHOT.jar:na]
at ...
edit: as npe suggested I digged into the code. I think I found the problem. Beside jersey-client I also have cxf in the classpath. Both jersey and cxf provide a class called RuntimeDelegateImpl. But CXFs version does not feature a DateHeaderDelegate. I think the wrong version (CXFs) of RuntimeDelegateImpl is taken.
By now I have not found how I can explicitely set the RuntimeDelegateImpl to use.
Use the source, Luke
The implementation of ClientResponse#getLastModified() for version 1.12 looks like this:
/*639*/ /**
/*640*/ * Get the last modified date.
/*641*/ *
/*642*/ * #return the last modified date, otherwise <code>null</code> if not present.
/*643*/ */
/*644*/ public Date getLastModified() {
/*645*/ String d = getHeaders().getFirst("Last-Modified");
/*646*/
/*647*/ return (d != null) ? dateDelegate.fromString(d) : null;
/*648*/ }
You get a NullPointerException in line 647, so it appears, that dateDelegate is null. Now, the dateDelegate object is initialized in line 321, like this:
/*321*/ protected static final HeaderDelegate<Date> dateDelegate =
/*322*/ RuntimeDelegate.getInstance().createHeaderDelegate(Date.class);
Now, the field is final, so it cannot be changed after this initialization - which means dateDelegate is null from the beginning - and that means, you have some kind of configuration issue and the delegate is not created.
Further, delegates are created in the AbstractRuntimeDelegate class (source for 1.12 here), like this:
/* 88*/ map.put(Date.class, _createHeaderDelegate(Date.class));
This rabbit hole goes deeper and deeper, so I'm going to stop here, but you know the way.
And last, but not least - debugger is your friend, my friend ;-)
To make it a wrap.
The problem is that I have both jersey-client and cxf in the classpath. Both RuntimeDelegateImpl. But CXFs version does not feature a DateHeaderDelegate. The wrong version (CXFs) of RuntimeDelegateImpl is taken.
I solved the issue by retrieving the Last-Modified Header 'manually':
private Date getLastModified(ClientResponse response){
String lastModifiedString = response.getHeaders().getFirst(
"Last-Modified");
if (StringUtils.isEmpty(lastModifiedString)) {
LOG.warn("Expect to get Last-Modified header when retrieving a Person by pnr "
+ "but there is none.");
return null;
} else {
try {
// format is Thu, 21 Jun 2012 08:00:42 GMT
return new SimpleDateFormat(
"EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US)
.parse(lastModifiedString);
} catch (ParseException e) {
LOG.error("Could not parse Last-Modified date "
+ e.getMessage());
return null;
}
}
}
Thanx to npe for the hints.

Categories

Resources