14. Beispiel 11 – Umwandlung und Validierung von Datumsangaben
Die neue Anwendung ermöglicht die Eingabe von Datumsangaben:
![]() |
- in [1], das Eingabeformular
- in [2], die zurückgegebene Antwort
Die Anwendung funktioniert ähnlich wie bei der Eingabe von ganzen Zahlen, daher werden wir nur auf die Punkte eingehen, die sich unterscheiden.
14.1. Das NetBeans-Projekt
Das NetBeans-Projekt sieht wie folgt aus:
![]() |
- in [1], die Ansichten der Anwendung
- [Accueil.jsp]: die Startseite
- [FormDate.jsp]: das Eingabeformular
- [ConfirmationFormDate.jsp]: Die Bestätigungsseite
- in [2], die Meldungsdatei [messages.properties] und die Hauptkonfigurationsdatei von Struts
- in [3]:
- [FormDate.java]: die Aktion, die das Formular anzeigt und verarbeitet
- [FormDate-validation.xml]: die Validierungsregeln für die Aktion [FormDate]. Diese Datei delegiert diese Validierungen an das Modell.
- [FormDateModel]: das Modell der Aktion [FormDate]
- [ FormDateModel-validation.xml]: die Validierungsregeln des Modells
- [FormDateModel.properties]: die Meldungsdatei des Modells
- [example.xml]: Sekundäre Struts-Konfigurationsdatei
14.2. Die Projektkonfiguration
Das Projekt wird hauptsächlich über die folgende Datei „[example.xml]“ konfiguriert:
<?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>
Sie entspricht derjenigen, die bereits für die Eingabe von ganzen Zahlen behandelt wurde.
14.3. Die Meldungsdateien
Die Datei [messages.properties] lautet wie folgt:
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}".
Die Datei [FormDateModel.properties] lautet wie folgt:
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
Zeile 1 spielt eine wichtige Rolle. Wir werden darauf zurückkommen.
14.4. Das Eingabeformular
Die Ansicht [FormDate.jsp] sieht wie folgt aus:
<%@ 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>
Die Zeilen 13 bis 18 sind die sechs Felder zur Datums-Eingabe. Die Felder date2 bis date5 haben ein komplexes Attribut value, das gleiche wie bei der Eingabe von reellen Zahlen. Da dieselben Schwierigkeiten auftraten, wurden sie auf dieselbe Weise gelöst.
14.5. Die Bestätigungsansicht
Die Bestätigungsansicht [ConfirmationFormDate.jsp] sieht wie folgt aus:

Ihr Code lautet wie folgt:
<%@ 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>
Die Ansicht [ConfirmationFormDate.jsp] zeigt lediglich die Vorlage [FormDateModel] an. Die Zeilen 47–49 zeigen die Anzeige eines Datums. Diese Anzeige soll ein Datumsformat berücksichtigen. Dazu verwenden wir das Tag <s:text>, das wir bisher für die Internationalisierung der Anwendung genutzt haben.
Hier lautet der verwendete Meldungsschlüssel date.format. Dieser Schlüssel ist in der Datei [FormDateModel.properties] zu finden:
date.format={0,date,dd/MM/yyyy}
Der dem Schlüssel zugeordnete Wert ist hier keine Meldung, sondern ein Anzeigeformat:
- 0 ist ein Parameter, der den anzuzeigenden Wert darstellt. In diesem Fall ist dies das Feld date5 der Vorlage.
- „date“ steht für ein Datum. Zuvor hatten wir „number“ für eine Zahl.
- dd/MM/yyyy steht für das Datumsformat. Hier soll es in der Form tt/mm/jjjj angezeigt werden. Das entsprechende Java-Format lautet dd/MM/yyyy (d: Tag, M: Monat, y: Jahr)
Das Tag
<s:text name="date.format">
<s:param value="date5"/>
</s:text>
löst die Anzeige der Meldung {0, date, dd/MM/yyyy} aus, wobei der Parameter date5 (Zeile 2) den Parameter 0 des Formats ersetzt. Somit wird die Vorlage date5 im Format tt/mm/jjjj angezeigt.
14.6. Die Vorlage [FormDateModel]
Die Felder date1 bis date6 des Formulars [FormDate.jsp] werden in die folgende Vorlage [FormDateModel] eingefügt:
package example;
import java.util.Date;
public class FormDateModel {
// Konstruktor ohne Parameter
public FormDateModel() {
}
// Felder
private String date1;
private Date date2 = new Date();
private Date date3 = new Date();
private Date date4;
private Date date5;
private String date6;
// Modell zurücksetzen
public void clearModel() {
date1 = null;
date2 = null;
date3 = null;
date4 = null;
date5 = null;
date6 = null;
}
// Getter und Setter
...
}
- Die Felder date1 und date6 sind vom Typ String
- Die übrigen Felder haben den Typ Date
14.7. Die Validierung des Modells
Die Validierung des Modells wird durch zwei Dateien gesteuert: [FormDate-validation.xml] und [FormDateModel-validation.xml].
Die Datei [FormDate-validation.xml] delegiert die Validierungen an [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/Beispiel-10/Beispiel/xwork-validator-1.0.2.dtd">
<validators>
<field name="model" >
<field-validator type="visitor">
<param name="appendPrefix">false</param>
<message/>
</field-validator>
</field>
</validators>
Diese Datei ist uns bereits begegnet.
Die Datei [FormDateModel-validation.xml] enthält die folgenden Validierungsregeln:
<!--
<!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/Beispiel-11/Beispiel/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>
- Die Regel in den Zeilen 11–20 überprüft, ob das Eingabefeld date1 dem Muster eines regulären Ausdrucks entspricht, der ein Datum im Format TT/MM/JJJJ darstellt. Es ist zu beachten, dass zwar überprüft wird, ob die Zeichenfolge die Form eines Datums hat, jedoch nicht, ob es sich um ein gültiges Datum handelt. So hat beispielsweise 29/02/2011 die Form eines Datums, ist aber kein gültiges Datum.
- Die Regel in den Zeilen 22–29 überprüft, ob das Eingabefeld date2 ein gültiges Datum ist.
- Die Regel in den Zeilen 31–42 prüft, ob das Eingabefeld date3 ein gültiges Datum >= 18.05.2000 ist.
- Die Regel in den Zeilen 44–55 prüft, ob das Eingabefeld date4 ein gültiges Datum <= 20.06.2001 ist.
- Die Regel in den Zeilen 57–69 prüft, ob das Eingabefeld date5 ein gültiges Datum <= 20.06.2001 und >= 18.05.2000 ist.
- Die Regel in den Zeilen 71–80 überprüft, ob das Feld date6 das Datumsformat TT/MM/JJJJ hat.
Sobald die Datei [FormDateModel-validation.xml] vom Validierungs-Interceptor verarbeitet wurde, lässt dieser die Methode validate der Aktion [FormDate] ausführen, sofern diese vorhanden ist. Wir werden sie zusammen mit der gesamten Aktion vorstellen.
14.8. Die Aktion [FormDate]
Die Aktion [FormDate] lautet wie folgt:
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 {
// Konstruktor ohne Parameter
public FormDate() {
}
// Aktionsvorlage
public Object getModel() {
if (session.get("model") == null) {
session.put("model", new FormDateModel());
}
return session.get("model");
}
@SkipValidation
public String clearModel() {
// Löschung des Modells
((FormDateModel) getModel()).clearModel();
// Ergebnis
return INPUT;
}
public String cancel() {
// Das Modell wird bereinigt
((FormDateModel) getModel()).clearModel();
// Ergebnis
return "cancel";
}
// SessionAware
Map<String, Object> session;
public void setSession(Map<String, Object> session) {
this.session = session;
}
// Validierung
@Override
public void validate() {
// Datumsformatierung
SimpleDateFormat formateurDate = new SimpleDateFormat("dd/MM/yyyy");
formateurDate.setLenient(false);
// Ist die Eingabe von Datum1 gültig?
if (getFieldErrors().get("date1") == null) {
// Überprüfung der Gültigkeit des Datums
try {
formateurDate.parse(((FormDateModel) getModel()).getDate1());
} catch (Exception e) {
addFieldError("date1", getText("date1.error"));
}
}
// Ist die Eingabe von Datum 6 gültig?
if (getFieldErrors().get("date6") == null) {
Date d = null;
try {
// Überprüfung der Gültigkeit des Datums
d = formateurDate.parse(((FormDateModel) getModel()).getDate6());
// Überprüfung der Grenzwerte
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;
}
}
}
}
Die Aktion [FormDate] ist nach dem gleichen Muster aufgebaut wie die Aktion [FormInt]. Wir werden nur die Methode validate erläutern. Zur Erinnerung: Die Methode validate wird nach der Verarbeitung der Validierungsdatei [FormDateModel-validation.xml] und vor der Ausführung der Methode execute ausgeführt.
- Die Methode validate vervollständigt die Validierung der Datumsangaben aus den Methoden date1 und date6. Die Validierungsdatei [FormDateModel-validation.xml] hatte zuvor überprüft, ob die eingegebenen Zeichenfolgen das Format eines Datums hatten. Nun wird überprüft, ob es sich tatsächlich um gültige Datumsangaben handelt. Es sei auch daran erinnert, dass date1 und date6 die einzigen beiden Felder des Modells waren, die vom Typ String waren.
- Zeile 50: Es wird ein Datumsformat „TT/MM/JJJJ“ erstellt, um zu überprüfen, ob Zeichenfolgen dieses Typs tatsächlich gültige Datumsangaben sind.
- Zeile 51: setLenient(false) verhindert, dass das ungültige Datum 32/01/2012 als gültiges Datum 01/02/2012 interpretiert wird.
- Zeile 53: date1 wird nur validiert, wenn bei den vorherigen Validierungen keine Fehler in diesem Feld aufgetreten sind.
- Zeile 56: Es wird überprüft, ob date1 ein gültiges Datum im Format TT/MM/JJJJ ist.
- Zeile 58: Ist dies nicht der Fall, wird dem Feld date1 eine Fehlermeldung zugeordnet.
- Zeile 62: date6 wird nur validiert, wenn dieses Feld die vorherigen Validierungen bestanden hat.
- Zeile 66: Es wird überprüft, ob date6 ein gültiges Datum ist
- Zeile 68: Wenn date6 <18.05.2000 oder date6>20.06.2001 ist, dann ist date6 falsch.
14.9. Letzte Details
Das vorherige Formular weist Schwachstellen auf, wie das folgende Beispiel zeigt:
![]() |
- Bei [1] wird ein ungültiges Datum
- in [2] wurde es akzeptiert. Es wurden nur die ersten Zeichen tt/mm/jjjj berücksichtigt.
Die Felder date2 bis date5, die mit Vorlagen vom Typ Date verknüpft sind, weisen alle dieses Problem auf. Auch hier habe ich vielleicht etwas in der Dokumentation übersehen. Die Felder date1 und date6, die mit Vorlagen vom Typ String verknüpft sind, weisen dieses Problem nicht auf.


