14. Ejemplo 11 – Conversión y validación de fechas
La nueva aplicación presenta la introducción de fechas:
![]() |
- en [1], el formulario de introducción
- en [2], la respuesta devuelta
La aplicación funciona de manera similar a la introducción de números enteros, por lo que solo comentaremos los puntos que difieren.
14.1. El proyecto NetBeans
El proyecto NetBeans es el siguiente:
![]() |
- en [1], las vistas de la aplicación
- [Accueil.jsp]: la página de inicio
- [FormDate.jsp]: el formulario de entrada
- [ConfirmationFormDate.jsp]: la página de confirmación
- en [2], el archivo de mensajes [messages.properties] y el archivo de configuración principal de Struts
- en [3]:
- [FormDate.java]: la acción que muestra y procesa el formulario
- [FormDate-validation.xml]: las reglas de validación de la acción [FormDate]. Este archivo delega estas validaciones al modelo.
- [FormDateModel]: el modelo de la acción [FormDate]
- [ FormDateModel-validation.xml]: las reglas de validación del modelo
- [FormDateModel.properties]: el archivo de mensajes del modelo
- [example.xml]: archivo de configuración secundario de Struts
14.2. La configuración del proyecto
El proyecto se configura principalmente mediante el siguiente archivo [example.xml]:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<package name="example" namespace="/example" extends="struts-default">
<action name="Accueil">
<result name="success">/example/Accueil.jsp</result>
</action>
<action name="FormDate" class="example.FormDate">
<result name="input">/example/FormDate.jsp</result>
<result name="cancel" type="redirect">/example/Accueil.jsp</result>
<result name="success">/example/ConfirmationFormDate.jsp</result>
</action>
</package>
</struts>
Es similar al que se ha estudiado para la introducción de números enteros.
14.3. Los archivos de mensajes
El archivo [messages.properties] es el siguiente:
Accueil.titre=Accueil
Accueil.message=Struts 2 - Conversions et validations
Accueil.FormDate=Saisie de dates
Form.titre=Conversions et validations
FormDate.message=Struts 2 - Conversion et validation de dates
FormDate.conseil=Tapez les dates au format JJ/MM/AAAA comme dans 12/10/2008
Form.submitText=Valider
Form.cancelText=Annuler
Form.clearModel=Raz mod\u00e8le
Confirmation.titre=Confirmation
Confirmation.message=Confirmation des valeurs saisies
Confirmation.champ=champ
Confirmation.valeur=valeur
Confirmation.lien=Formulaire de test
xwork.default.invalid.fieldvalue=Valeur invalide pour le champ "{0}".
El archivo [FormDateModel.properties] es el siguiente:
date.format={0,date,dd/MM/yyyy}
date1.prompt=1-Tapez une date au format JJ/MM/AAAA
date1.error=Date erron\u00E9e
date2.prompt=2-Tapez une date au format JJ/MM/AAAA
date2.error=Date erron\u00E9e
date3.prompt=3-Tapez une date >=18/05/2000
date3.error=Date erron\u00E9e
date4.prompt=4-Tapez une date <=20/06/2001
date4.error=Date erron\u00E9e
date5.prompt=5-Tapez une date dans l''intervalle [18/05/2000,20/06/2001]
date5.error=Date errron\u00E9e
date6.prompt=6-Tapez une date dans l''intervalle [18/05/2000,20/06/2001]
date6.error=Date errron\u00E9e
La línea 1 desempeña un papel importante. Volveremos sobre ella más adelante.
14.4. El formulario de entrada
La vista [FormDate.jsp] es la siguiente:
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title><s:text name="Form.titre"/></title>
<s:head/>
</head>
<body background="<s:url value="/ressources/standard.jpg"/>">
<h2><s:text name="FormDate.message"/></h2>
<h4><s:text name="FormDate.conseil"/></h4>
<s:form name="formulaire" action="FormDate">
<s:textfield name="date1" key="date1.prompt"/>
<s:textfield name="date2" key="date2.prompt" value="%{#parameters['date2']!=null ? #parameters['date2'] : date2==null ? '' :getText('date.format',{date2})}"/>
<s:textfield name="date3" key="date3.prompt" value="%{#parameters['date3']!=null ? #parameters['date3'] : date3==null ? '' :getText('date.format',{date3})}"/>
<s:textfield name="date4" key="date4.prompt" value="%{#parameters['date4']!=null ? #parameters['date4'] : date4==null ? '' :getText('date.format',{date4})}"/>
<s:textfield name="date5" key="date5.prompt" value="%{#parameters['date5']!=null ? #parameters['date5'] : date5==null ? '' :getText('date.format',{date5})}"/>
<s:textfield name="date6" key="date6.prompt"/>
<s:submit key="Form.submitText" method="execute"/>
</s:form>
<br/>
<s:url id="url" action="FormDate" method="cancel"/>
<s:a href="%{url}"><s:text name="Form.cancelText"/></s:a>
<br/>
<s:url id="url" action="FormDate" method="clearModel"/>
<s:a href="%{url}"><s:text name="Form.clearModel"/></s:a>
</body>
</html>
Las líneas 13 a 18 son los seis campos de introducción de fechas. Los campos date2 a date5 tienen un atributo complejo value, el mismo que para la introducción de números reales. Dado que se han encontrado las mismas dificultades, se han resuelto de la misma manera.
14.5. La vista de confirmación
La vista de confirmación [ConfirmationFormDate.jsp] es la siguiente:

Su código es el siguiente:
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title><s:text name="Confirmation.titre"/></title>
<s:head/>
</head>
<body background="<s:url value="/ressources/standard.jpg"/>">
<h2><s:text name="Confirmation.message"/></h2>
<table border="1">
<tr>
<th><s:text name="Confirmation.champ"/></th>
<th><s:text name="Confirmation.valeur"/></th>
</tr>
<tr>
<td><s:text name="date1.prompt"/></td>
<td><s:text name="date1"/></td>
</tr>
<tr>
<td><s:text name="date2.prompt"/></td>
<td>
<s:text name="date.format">
<s:param value="date2"/>
</s:text>
</td>
</tr>
<tr>
<td><s:text name="date3.prompt"/></td>
<td>
<s:text name="date.format">
<s:param value="date3"/>
</s:text>
</td>
</tr>
<tr>
<td><s:text name="date4.prompt"/></td>
<td>
<s:text name="date.format">
<s:param value="date4"/>
</s:text>
</td>
</tr>
<tr>
<td><s:text name="date5.prompt"/></td>
<td>
<s:text name="date.format">
<s:param value="date5"/>
</s:text>
</td>
</tr>
<tr>
<td><s:text name="date6.prompt"/></td>
<td><s:text name="date6"/></td>
</tr>
</table>
<br/>
<s:url id="url" action="FormDate!input"/>
<s:a href="%{url}"><s:text name="Confirmation.lien"/></s:a>
</body>
</html>
La vista [ConfirmationFormDate.jsp] se limita a mostrar la plantilla [FormDateModel]. Las líneas 47-49 muestran la visualización de una fecha. Queremos que esta visualización tenga en cuenta un formato de fecha. Para ello, utilizamos la etiqueta <s:text> que habíamos utilizado hasta ahora para la internacionalización de la aplicación.
Aquí, la clave de mensaje utilizada es date.format. Esta clave se encuentra en el archivo [FormDateModel.properties]:
date.format={0,date,dd/MM/yyyy}
El valor asociado a la clave no es aquí un mensaje, sino un formato de visualización:
- 0 es un parámetro que representa el valor que se va a mostrar. En este caso, será el campo date5 del modelo.
- date representa una fecha. Anteriormente, teníamos number para un número.
- dd/MM/yyyy representa el formato de la fecha. Aquí lo queremos en el formato dd/mm/aaaa. El formato Java adecuado es dd/MM/yyyy (d: día, M: mes, y: año)
La etiqueta
<s:text name="date.format">
<s:param value="date5"/>
</s:text>
muestra el mensaje {0, fecha, dd/MM/yyyy}, donde el parámetro date5 (línea 2) sustituirá al parámetro 0 del formato. De este modo, la plantilla date5 se mostrará en el formato dd/mm/aaaa.
14.6. La plantilla [FormDateModel]
Los campos date1 a date6 del formulario [FormDate.jsp] se insertan en la siguiente plantilla [FormDateModel]:
package example;
import java.util.Date;
public class FormDateModel {
// fabricante sin parámetros
public FormDateModel() {
}
// campos
private String date1;
private Date date2 = new Date();
private Date date3 = new Date();
private Date date4;
private Date date5;
private String date6;
// borrar modelo
public void clearModel() {
date1 = null;
date2 = null;
date3 = null;
date4 = null;
date5 = null;
date6 = null;
}
// getters y setters
...
}
- los campos date1 y date6 son de tipo String
- los demás campos son de tipo Date
14.7. Validación del modelo
La validación del modelo se controla mediante dos archivos: [FormDate-validation.xml] y [FormDateModel-validation.xml].
El archivo [FormDate-validation.xml] delega las validaciones a [FormDateModel-validation.xml]:
<!--
<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//
EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">
-->
<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//
EN" "http://localhost:8084/ejemplo-10/ejemplo/xwork-validator-1.0.2.dtd">
<validators>
<field name="model" >
<field-validator type="visitor">
<param name="appendPrefix">false</param>
<message/>
</field-validator>
</field>
</validators>
Ya nos hemos encontrado con este archivo.
El archivo [FormDateModel-validation.xml] contiene las siguientes reglas de validación:
<!--
<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//
EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">
-->
<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//
EN" "http://localhost:8084/ejemplo-11/ejemplo/xwork-validator-1.0.2.dtd">
<validators>
<field name="date1" >
<field-validator type="requiredstring" short-circuit="true">
<message key="date1.error"/>
</field-validator>
<field-validator type="regex" short-circuit="true">
<param name="expression">^\d{2}/\d{2}/\d{4}$</param>
<param name="trim">true</param>
<message key="date1.error"/>
</field-validator>
</field>
<field name="date2" >
<field-validator type="required" short-circuit="true">
<message key="date2.error"/>
</field-validator>
<field-validator type="conversion" short-circuit="true">
<message key="date2.error"/>
</field-validator>
</field>
<field name="date3" >
<field-validator type="required" short-circuit="true">
<message key="date2.error"/>
</field-validator>
<field-validator type="conversion" short-circuit="true">
<message key="date3.error"/>
</field-validator>
<field-validator type="date" short-circuit="true">
<param name="min">18/05/2000</param>
<message key="date3.error"/>
</field-validator>
</field>
<field name="date4" >
<field-validator type="required" short-circuit="true">
<message key="date2.error"/>
</field-validator>
<field-validator type="conversion" short-circuit="true">
<message key="date4.error"/>
</field-validator>
<field-validator type="date" short-circuit="true">
<param name="max">20/06/2001</param>
<message key="date4.error"/>
</field-validator>
</field>
<field name="date5" >
<field-validator type="required" short-circuit="true">
<message key="date2.error"/>
</field-validator>
<field-validator type="conversion" short-circuit="true">
<message key="date5.error"/>
</field-validator>
<field-validator type="date" short-circuit="true">
<param name="min">18/05/2000</param>
<param name="max">20/06/2001</param>
<message key="date5.error"/>
</field-validator>
</field>
<field name="date6" >
<field-validator type="requiredstring" short-circuit="true">
<message key="date6.error"/>
</field-validator>
<field-validator type="regex" short-circuit="true">
<param name="expression">^\d{2}/\d{2}/\d{4}$</param>
<param name="trim">true</param>
<message key="date6.error"/>
</field-validator>
</field>
</validators>
- La regla de las líneas 11-20 comprueba que el campo de entrada date1 siga el patrón de una expresión regular que representa una fecha con el formato dd/mm/aaaa. Cabe señalar que se comprueba que la cadena tiene el formato de una fecha, pero no se comprueba que sea una fecha válida. Así, 29/02/2011 tiene el formato de una fecha, pero no es una fecha válida.
- La regla de las líneas 22-29 comprueba que el campo de entrada date2 sea una fecha válida.
- La regla de las líneas 31-42 comprueba que el campo de entrada date3 sea una fecha válida >=18/05/2000
- La regla de las líneas 44-55 comprueba que el campo de entrada date4 sea una fecha válida <=20/06/2001.
- La regla de las líneas 57-69 comprueba que el campo de entrada date5 sea una fecha válida <=20/06/2001 y >=18/05/2000.
- La regla de las líneas 71-80 comprueba que el campo date6 tenga el formato de fecha dd/mm/aaaa.
Una vez que el interceptor de validación ha procesado el archivo [FormDateModel-validation.xml], este ejecuta el método validate de la acción [FormDate], si existe. Lo presentaremos junto con el resto de la acción.
14.8. La acción [FormDate]
La acción [FormDate] es la siguiente:
package example;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import org.apache.struts2.interceptor.SessionAware;
import org.apache.struts2.interceptor.validation.SkipValidation;
public class FormDate extends ActionSupport implements ModelDriven, SessionAware {
// constructor sin parámetros
public FormDate() {
}
// modelo de la acción
public Object getModel() {
if (session.get("model") == null) {
session.put("model", new FormDateModel());
}
return session.get("model");
}
@SkipValidation
public String clearModel() {
// borrado del modelo
((FormDateModel) getModel()).clearModel();
// resultado
return INPUT;
}
public String cancel() {
// se limpia el modelo
((FormDateModel) getModel()).clearModel();
// resultado
return "cancel";
}
// SessionAware
Map<String, Object> session;
public void setSession(Map<String, Object> session) {
this.session = session;
}
// validación
@Override
public void validate() {
// formato de las fechas
SimpleDateFormat formateurDate = new SimpleDateFormat("dd/MM/yyyy");
formateurDate.setLenient(false);
// ¿Es válida la fecha 1 introducida?
if (getFieldErrors().get("date1") == null) {
// verificación de la validez de la fecha
try {
formateurDate.parse(((FormDateModel) getModel()).getDate1());
} catch (Exception e) {
addFieldError("date1", getText("date1.error"));
}
}
// ¿Es válida la fecha 6 introducida?
if (getFieldErrors().get("date6") == null) {
Date d = null;
try {
// verificación de la validez de la fecha
d = formateurDate.parse(((FormDateModel) getModel()).getDate6());
// verificación de límites
if (d.after(formateurDate.parse("20/06/2001")) || d.before(formateurDate.parse("18/05/2000"))) {
addFieldError("date6", getText("date6.error"));
return;
}
} catch (Exception e) {
addFieldError("date6", getText("date6.error"));
return;
}
}
}
}
La acción [FormDate] se basa en el mismo modelo que la acción [FormInt]. Solo comentaremos el método validate. Recordamos que el método validate se ejecuta tras el procesamiento del archivo de validación [FormDateModel-validation.xml] y antes de la ejecución del método execute.
- El método validate completa la validación de las fechas de date1 y date6. El archivo de validación [FormDateModel-validation.xml] había comprobado que las cadenas introducidas tuvieran el formato de una fecha. Ahora se comprueba que se trate efectivamente de fechas válidas. Cabe recordar también que date1 y date6 eran los dos únicos campos del modelo que eran de tipo String.
- línea 50: se crea un formato de fecha dd/mm/aaaa para verificar que las cadenas de este tipo son efectivamente fechas válidas.
- línea 51: setLenient(false) permite evitar que la fecha no válida 32/01/2012 se interprete como la fecha válida 01/02/2012.
- línea 53: solo se valida date1 si las validaciones anteriores no han dado errores en este campo.
- línea 56: se comprueba que date1 sea una fecha válida en el formato dd/mm/aaaa.
- línea 58: si no es así, se asocia un mensaje de error al campo date1.
- línea 62: solo se valida date6 si este campo ha superado las validaciones anteriores.
- línea 66: se comprueba que date6 sea una fecha válida
- línea 68: si date6 es <18/05/2000 o >20/06/2001, entonces date6 es incorrecto.
14.9. Últimos detalles
El formulario anterior presenta fallos, como muestra el siguiente ejemplo:
![]() |
- en [1], se introduce una fecha no válida
- en [2], se ha aceptado. Solo se han tenido en cuenta los primeros caracteres dd/mm/aaaa.
Los campos date2 a date5, que están vinculados a modelos del tipo Date, presentan todos este problema. Una vez más, quizá se me haya pasado algo por alto en la documentación. Los campos date1 y date6, que están vinculados a plantillas de tipo String, no presentan este problema.


