Je trouve curieux que le moyen le plus évident de créer des objets Date
en Java soit obsolète et semble avoir été "substitué" par un calendrier moins évident.
Comment vérifiez-vous qu'une date, donnée sous la forme d'une combinaison de jour, mois et année, est une date valide?
Par exemple, 2008-02-31 (comme dans aaaa-mm-jj) serait une date invalide.
La méthode actuelle consiste à utiliser la classe calendar. Il a la méthode setLenient qui validera la date, les levées et les exceptions si elles sont hors limites, comme dans votre exemple.
Oublié d'ajouter: Si vous obtenez une instance de calendrier et définissez l'heure à l'aide de votre date, voici comment vous obtenez la validation.
Calendar cal = Calendar.getInstance();
cal.setLenient(false);
cal.setTime(yourDate);
try {
cal.getTime();
}
catch (Exception e) {
System.out.println("Invalid date");
}
La clé est df.setLenient (false); C'est plus que suffisant pour les cas simples. Si vous recherchez une bibliothèque plus robuste (je doute) et/ou alternative comme joda-time, regardez la réponse de l'utilisateur "tardate"
final static String DATE_FORMAT = "dd-MM-yyyy";
public static boolean isDateValid(String date)
{
try {
DateFormat df = new SimpleDateFormat(DATE_FORMAT);
df.setLenient(false);
df.parse(date);
return true;
} catch (ParseException e) {
return false;
}
}
Comme l'a montré @Maglob, l'approche de base consiste à tester la conversion de chaîne en date à l'aide de SimpleDateFormat.parse . Cela capturera les combinaisons jour/mois non valides, comme 2008-02-31.
Cependant, dans la pratique, cela suffit rarement, car SimpleDateFormat.parse est extrêmement libéral. Vous pourriez être concerné par deux comportements:
Caractères non valides dans la chaîne de date Étonnamment, 2008-02-2x "passera" comme une date valide avec locale format = "aaaa-MM-jj" par exemple. Même quand isLenient == false.
Années: 2, 3 ou 4 chiffres? Vous pouvez également vouloir appliquer des années à 4 chiffres plutôt que de permettre le comportement SimpleDateFormat par défaut (qui interprétera différemment "12-02-31" selon que votre format est "aaaa-MM-jj" ou "aa -MM-dd ")
Ainsi, une chaîne complète de test de date pourrait ressembler à ceci: une combinaison de correspondance regex, puis une conversion de date forcée. Le truc avec l'expression rationnelle est de le rendre convivial pour les paramètres régionaux.
Date parseDate(String maybeDate, String format, boolean lenient) {
Date date = null;
// test date string matches format structure using regex
// - weed out illegal characters and enforce 4-digit year
// - create the regex based on the local format string
String reFormat = Pattern.compile("d+|M+").matcher(Matcher.quoteReplacement(format)).replaceAll("\\\\d{1,2}");
reFormat = Pattern.compile("y+").matcher(reFormat).replaceAll("\\\\d{4}");
if ( Pattern.compile(reFormat).matcher(maybeDate).matches() ) {
// date string matches format structure,
// - now test it can be converted to a valid date
SimpleDateFormat sdf = (SimpleDateFormat)DateFormat.getDateInstance();
sdf.applyPattern(format);
sdf.setLenient(lenient);
try { date = sdf.parse(maybeDate); } catch (ParseException e) { }
}
return date;
}
// used like this:
Date date = parseDate( "21/5/2009", "d/M/yyyy", false);
Notez que l'expression régulière suppose que la chaîne de format ne contient que des caractères de jour, de mois, d'année et de séparation. En dehors de cela, le format peut être dans n'importe quel format de lieu: "j/MM/aa", "aaaa-MM-jj", etc. La chaîne de format pour l'environnement local actuel pourrait être obtenue comme suit:
Locale locale = Locale.getDefault();
SimpleDateFormat sdf = (SimpleDateFormat)DateFormat.getDateInstance(DateFormat.SHORT, locale );
String format = sdf.toPattern();
J'ai entendu parler de joda time récemment et j'ai pensé comparer. Deux points:
C'est assez simple à utiliser:
import org.joda.time.format.*;
import org.joda.time.DateTime;
org.joda.time.DateTime parseDate(String maybeDate, String format) {
org.joda.time.DateTime date = null;
try {
DateTimeFormatter fmt = DateTimeFormat.forPattern(format);
date = fmt.parseDateTime(maybeDate);
} catch (Exception e) { }
return date;
}
Vous pouvez utiliser SimpleDateFormat
Par exemple, quelque chose comme:
boolean isLegalDate(String s) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
sdf.setLenient(false);
return sdf.parse(s, new ParsePosition(0)) != null;
}
Avec les API Date et heure (/ Java.time classes) intégrées à Java 8 et ultérieur, vous pouvez utiliser la classe LocalDate
.
public static boolean isDateValid(int year, int month, int day) {
boolean dateIsValid = true;
try {
LocalDate.of(year, month, day);
} catch (DateTimeException e) {
dateIsValid = false;
}
return dateIsValid;
}
Utilisez le mode strict sur Java.time.DateTimeFormatter
pour analyser un LocalDate
. Piège pour le DateTimeParseException
.
LocalDate.parse( // Represent a date-only value, without time-of-day and without time zone.
"31/02/2000" , // Input string.
DateTimeFormatter // Define a formatting pattern to match your input string.
.ofPattern ( "dd/MM/uuuu" )
.withResolverStyle ( ResolverStyle.STRICT ) // Specify leniency in tolerating questionable inputs.
)
Après l'analyse, vous pouvez rechercher une valeur raisonnable. Par exemple, une date de naissance dans les cent dernières années.
birthDate.isAfter( LocalDate.now().minusYears( 100 ) )
Évitez d’utiliser les anciennes classes de date et d’heure problématiques livrées avec les premières versions de Java. Maintenant supplanté par les classes Java.time .
LocalDate
& DateTimeFormatter
& ResolverStyle
La classe LocalDate
représente une valeur de date uniquement sans heure et sans fuseau horaire.
String input = "31/02/2000";
DateTimeFormatter f = DateTimeFormatter.ofPattern ( "dd/MM/uuuu" );
try {
LocalDate ld = LocalDate.parse ( input , f );
System.out.println ( "ld: " + ld );
} catch ( DateTimeParseException e ) {
System.out.println ( "ERROR: " + e );
}
La classe Java.time.DateTimeFormatter
peut être configurée pour analyser les chaînes avec l'un des trois modes de clémence définis dans l'en-tête ResolverStyle
. Nous insérons une ligne dans le code ci-dessus pour essayer chacun des modes.
f = f.withResolverStyle ( ResolverStyle.LENIENT );
Les resultats:
ResolverStyle.LENIENT
ResolverStyle.SMART
ResolverStyle.STRICT
Nous pouvons voir qu'en mode ResolverStyle.LENIENT
, la date non valide est avancée d'un nombre de jours équivalent. En mode ResolverStyle.SMART
(par défaut), une décision logique est prise pour conserver la date dans le mois et aller avec le dernier jour possible du mois, le 29 février d'une année bissextile, car il n'y a pas de 31e jour. dans ce mois. Le mode ResolverStyle.STRICT
lève une exception en se plaignant de l'absence d'une telle date.
Tous les trois sont raisonnables en fonction de votre problème d’entreprise et de vos règles. On dirait que dans votre cas, vous voulez que le mode strict rejette la date non valide plutôt que de l’ajuster.
Le cadre Java.time est intégré à Java 8 et versions ultérieures. Ces classes supplantent les anciennes classes gênantes héritées _ date-heure telles que Java.util.Date
, Calendar
_, & SimpleDateFormat
.
Le projet Joda-Time , désormais en mode { mode maintenance }, conseille la migration vers les classes Java.time .
Pour en savoir plus, consultez le Oracle Tutorial . Et recherchez Stack Overflow pour de nombreux exemples et explications. La spécification est JSR 310 .
Vous pouvez échanger des objets Java.time directement avec votre base de données. Utilisez un pilote JDBC compatible avec JDBC 4.2 ou une version ultérieure. Pas besoin de chaînes, pas besoin de Java.sql.*
classes.
Où obtenir les classes Java.time?
Le projet ThreeTen-Extra étend Java.time avec des classes supplémentaires. Ce projet est un terrain d’essai pour d’éventuels ajouts à Java.time. Vous pouvez y trouver des classes utiles telles que Interval
_, YearWeek
, YearQuarter
_ et plus _.
Une autre solution stricte utilisant la bibliothèque standard consiste à effectuer les opérations suivantes:
1) Créez un SimpleDateFormat strict en utilisant votre modèle
2) Essayer d'analyser la valeur entrée par l'utilisateur en utilisant l'objet format
3) En cas de succès, reformatez la Date résultant de (2) en utilisant le même format de date (à partir de (1))
4) Comparez la date reformatée à la valeur d'origine saisie par l'utilisateur. Si elles sont égales, la valeur entrée correspond strictement à votre modèle.
De cette façon, vous n'avez pas besoin de créer des expressions régulières complexes - dans mon cas, je devais prendre en charge toute la syntaxe de motif de SimpleDateFormat, plutôt que de me limiter à certains types tels que quelques jours, mois et années.
En me basant sur la réponse de @Pangea pour résoudre le problème signalé par @ceklock , j'ai ajouté une méthode permettant de vérifier que la variable dateString
ne contient aucun caractère non valide.
Voici comment je fais:
private boolean isDateCorrect(String dateString) {
try {
Date date = mDateFormatter.parse(dateString);
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
return matchesOurDatePattern(dateString); //added my method
}
catch (ParseException e) {
return false;
}
}
/**
* This will check if the provided string matches our date format
* @param dateString
* @return true if the passed string matches format 2014-1-15 (YYYY-MM-dd)
*/
private boolean matchesDatePattern(String dateString) {
return dateString.matches("^\\d+\\-\\d+\\-\\d+");
}
Je vous suggère d'utiliser la classe org.Apache.commons.validator.GenericValidator
d'Apache.
GenericValidator.isDate(String value, String datePattern, boolean strict);
Remarque: strict - Pour avoir une correspondance exacte du datePattern.
Je pense que le plus simple consiste simplement à convertir une chaîne en un objet de date et à la reconvertir en une chaîne. La chaîne de date donnée est correcte si les deux chaînes correspondent encore.
public boolean isDateValid(String dateString, String pattern)
{
try
{
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
if (sdf.format(sdf.parse(dateString)).equals(dateString))
return true;
}
catch (ParseException pe) {}
return false;
}
En supposant que les deux sont des chaînes (sinon, elles seraient déjà des dates valides), voici un moyen:
package cruft;
import Java.text.DateFormat;
import Java.text.ParseException;
import Java.text.SimpleDateFormat;
import Java.util.Date;
public class DateValidator
{
private static final DateFormat DEFAULT_FORMATTER;
static
{
DEFAULT_FORMATTER = new SimpleDateFormat("dd-MM-yyyy");
DEFAULT_FORMATTER.setLenient(false);
}
public static void main(String[] args)
{
for (String dateString : args)
{
try
{
System.out.println("arg: " + dateString + " date: " + convertDateString(dateString));
}
catch (ParseException e)
{
System.out.println("could not parse " + dateString);
}
}
}
public static Date convertDateString(String dateString) throws ParseException
{
return DEFAULT_FORMATTER.parse(dateString);
}
}
Voici la sortie que je reçois:
Java cruft.DateValidator 32-11-2010 31-02-2010 04-01-2011
could not parse 32-11-2010
could not parse 31-02-2010
arg: 04-01-2011 date: Tue Jan 04 00:00:00 EST 2011
Process finished with exit code 0
Comme vous pouvez le constater, les deux cas sont bien traités.
Cela fonctionne très bien pour moi. Approche suggérée ci-dessus par Ben.
private static boolean isDateValid(String s) {
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
try {
Date d = asDate(s);
if (sdf.format(d).equals(s)) {
return true;
} else {
return false;
}
} catch (ParseException e) {
return false;
}
}
Deux commentaires sur l'utilisation de SimpleDateFormat.
il devrait être déclaré comme une instance statique si déclaré en tant qu'accès statique doit être synchronisé car il n'est pas thread-safe
Il est préférable que IME instancie une instance pour chaque analyse d'une date.
Les méthodes ci-dessus d’analyse de date sont Nice, je viens d’ajouter une nouvelle vérification dans les méthodes existantes qui vérifient la date convertie avec la date originale à l’aide de formater, de sorte que cela fonctionne pour presque chaque cas vérifié. par exemple. 29/02/2013 est invalide date . Fonction donnée analyser la date en fonction des formats de date acceptables actuels. Il renvoie true si la date n'est pas analysée correctement.
public final boolean validateDateFormat(final String date) {
String[] formatStrings = {"MM/dd/yyyy"};
boolean isInvalidFormat = false;
Date dateObj;
for (String formatString : formatStrings) {
try {
SimpleDateFormat sdf = (SimpleDateFormat) DateFormat.getDateInstance();
sdf.applyPattern(formatString);
sdf.setLenient(false);
dateObj = sdf.parse(date);
System.out.println(dateObj);
if (date.equals(sdf.format(dateObj))) {
isInvalidFormat = false;
break;
}
} catch (ParseException e) {
isInvalidFormat = true;
}
}
return isInvalidFormat;
}
ressemble à SimpleDateFormat ne vérifie pas le modèle strictement même après setLenient (false); La méthode est appliquée dessus, donc j’ai utilisé la méthode ci-dessous pour valider si la date entrée est une date valide ou non selon le modèle fourni.
import Java.time.format.DateTimeFormatter;
import Java.time.format.DateTimeParseException;
public boolean isValidFormat(String dateString, String pattern) {
boolean valid = true;
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
try {
formatter.parse(dateString);
} catch (DateTimeParseException e) {
valid = false;
}
return valid;
}
public static String detectDateFormat(String inputDate, String requiredFormat) {
String tempDate = inputDate.replace("/", "").replace("-", "").replace(" ", "");
String dateFormat;
if (tempDate.matches("([0-12]{2})([0-31]{2})([0-9]{4})")) {
dateFormat = "MMddyyyy";
} else if (tempDate.matches("([0-31]{2})([0-12]{2})([0-9]{4})")) {
dateFormat = "ddMMyyyy";
} else if (tempDate.matches("([0-9]{4})([0-12]{2})([0-31]{2})")) {
dateFormat = "yyyyMMdd";
} else if (tempDate.matches("([0-9]{4})([0-31]{2})([0-12]{2})")) {
dateFormat = "yyyyddMM";
} else if (tempDate.matches("([0-31]{2})([a-z]{3})([0-9]{4})")) {
dateFormat = "ddMMMyyyy";
} else if (tempDate.matches("([a-z]{3})([0-31]{2})([0-9]{4})")) {
dateFormat = "MMMddyyyy";
} else if (tempDate.matches("([0-9]{4})([a-z]{3})([0-31]{2})")) {
dateFormat = "yyyyMMMdd";
} else if (tempDate.matches("([0-9]{4})([0-31]{2})([a-z]{3})")) {
dateFormat = "yyyyddMMM";
} else {
return "Pattern Not Added";
//add your required regex
}
try {
String formattedDate = new SimpleDateFormat(requiredFormat, Locale.ENGLISH).format(new SimpleDateFormat(dateFormat).parse(tempDate));
return formattedDate;
} catch (Exception e) {
//
return "";
}
}
Voici je vérifierais le format de date:
public static boolean checkFormat(String dateTimeString) {
return dateTimeString.matches("^\\d{4}-\\d{2}-\\d{2}") || dateTimeString.matches("^\\d{4}-\\d{2}-\\d{2}\\s\\d{2}:\\d{2}:\\d{2}")
|| dateTimeString.matches("^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}") || dateTimeString
.matches("^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z") ||
dateTimeString.matches("^\\d{4}-\\d{2}-\\d{2}\\s\\d{2}:\\d{2}:\\d{2}Z");
}
Voici ce que j'ai fait pour l'environnement Node sans aucune bibliothèque externe:
Date.prototype.yyyymmdd = function() {
var yyyy = this.getFullYear().toString();
var mm = (this.getMonth()+1).toString(); // getMonth() is zero-based
var dd = this.getDate().toString();
return zeroPad([yyyy, mm, dd].join('-'));
};
function zeroPad(date_string) {
var dt = date_string.split('-');
return dt[0] + '-' + (dt[1][1]?dt[1]:"0"+dt[1][0]) + '-' + (dt[2][1]?dt[2]:"0"+dt[2][0]);
}
function isDateCorrect(in_string) {
if (!matchesDatePattern) return false;
in_string = zeroPad(in_string);
try {
var idate = new Date(in_string);
var out_string = idate.yyyymmdd();
return in_string == out_string;
} catch(err) {
return false;
}
function matchesDatePattern(date_string) {
var dateFormat = /[0-9]+-[0-9]+-[0-9]+/;
return dateFormat.test(date_string);
}
}
Et voici comment l'utiliser:
isDateCorrect('2014-02-23')
true
// to return valid days of month, according to month and year
int returnDaysofMonth(int month, int year) {
int daysInMonth;
boolean leapYear;
leapYear = checkLeap(year);
if (month == 4 || month == 6 || month == 9 || month == 11)
daysInMonth = 30;
else if (month == 2)
daysInMonth = (leapYear) ? 29 : 28;
else
daysInMonth = 31;
return daysInMonth;
}
// to check a year is leap or not
private boolean checkLeap(int year) {
Calendar cal = Calendar.getInstance();
cal.set(Calendar.YEAR, year);
return cal.getActualMaximum(Calendar.DAY_OF_YEAR) > 365;
}
setLenient sur false si vous aimez une validation stricte
public boolean isThisDateValid(String dateToValidate, String dateFromat){
if(dateToValidate == null){
return false;
}
SimpleDateFormat sdf = new SimpleDateFormat(dateFromat);
sdf.setLenient(false);
try {
//if not valid, it will throw ParseException
Date date = sdf.parse(dateToValidate);
System.out.println(date);
} catch (ParseException e) {
e.printStackTrace();
return false;
}
return true;
}