Comment analyser des dates en formats alternatifs et / ou substituts en utilisant SimpleDateFormat
j'essaie d'analyser certaines dates qui sortent d'un document. Il semblerait que les utilisateurs aient saisi ces dates dans un format similaire mais non exact.
voici les formats:
9/09
9/2009
09/2009
9/1/2009
9-1-2009
Quelle est la meilleure façon d'essayer de les analyser tous ces? Ceux-ci semblent être le plus commun, mais je suppose que ce qui est accroché à moi est que si j'ai un modèle de "M/aaaa" l'habitude de toujours attraper avant "MM/aaaa" Dois-je configurer mon essayer / attraper les blocs imbriqués de la manière la moins restrictive à la plus restrictive? il semble que cela va certainement prendre beaucoup de duplication de code pour obtenir ce droit.
9 réponses
vous devrez utiliser un objet SimpleDateFormat
différent pour chaque motif différent. Cela dit, vous n'avez pas besoin que beaucoup de différents, merci à ce :
nombre: pour le formatage, le nombre de lettres de dessin est le nombre minimum de chiffres, et les numéros plus courts sont zéro-rembourrés à ce montant. Pour l'analyse, le nombre de lettres de modèle est ignoré à moins qu'il ne soit nécessaire de séparer deux les champs adjacents.
donc, vous aurez besoin de ces formats:
-
"M/y"
(qui couvre9/09
,9/2009
, et09/2009
) -
"M/d/y"
(qui couvre9/1/2009
) -
"M-d-y"
(qui couvre9-1-2009
)
Donc, mon conseil serait d'écrire une méthode qui fonctionne quelque chose comme ceci ( non testé ):
// ...
List<String> formatStrings = Arrays.asList("M/y", "M/d/y", "M-d-y");
// ...
Date tryParse(String dateString)
{
for (String formatString : formatStrings)
{
try
{
return new SimpleDateFormat(formatString).parse(dateString);
}
catch (ParseException e) {}
}
return null;
}
qu'en est-il de la définition de plusieurs motifs? Ils peuvent provenir d'un fichier de configuration contenant des motifs connus, codé en dur il se lit comme:
List<SimpleDateFormat> knownPatterns = new ArrayList<SimpleDateFormat>();
knownPatterns.add(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"));
knownPatterns.add(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm.ss'Z'"));
knownPatterns.add(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"));
knownPatterns.add(new SimpleDateFormat("yyyy-MM-dd' 'HH:mm:ss"));
knownPatterns.add(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX"));
for (SimpleDateFormat pattern : knownPatterns) {
try {
// Take a try
return new Date(pattern.parse(candidate).getTime());
} catch (ParseException pe) {
// Loop on
}
}
System.err.println("No known Date format found: " + candidate);
return null;
L'approche de Matt ci-dessus est bien, mais soyez conscient que vous allez rencontrer des problèmes si vous l'utilisez pour différencier les dates du format y/M/d
et d/M/y
. Par exemple, un formatter initialisé avec y/M/d
acceptera une date comme 01/01/2009
et vous donnera une date qui n'est clairement pas ce que vous vouliez. J'ai réglé la question comme suit, mais j'ai peu de temps et je ne suis pas satisfait de la solution pour deux raisons principales:
- il viole L'une des lignes directrices de Josh Bloch, spécifiquement "n'utilisez pas d'exceptions pour gérer le flux de programmes".
- je peux voir la méthode
getDateFormat()
devenir un peu un cauchemar si vous en aviez besoin pour gérer beaucoup d'autres formats de date.
si je devais faire quelque chose qui pourrait gérer des lots et des lots de différents formats de date et avait besoin d'être très performant, alors je pense que j'utiliserais l'approche de la création d'un enum qui relie chaque date différente regex à son format. Ensuite, utilisez MyEnum.values()
pour boucler l'enum et tester avec if(myEnum.getPattern().matches(date))
plutôt que d'attraper une dateformatexception.
Anway, cela étant dit, ce qui suit peut gérer les dates des formats 'y/M/d' 'y-M-d' 'y M d' 'd/M/y' 'd-M-y' 'd M y'
et toutes les autres variations de ceux qui comprennent des formats de temps aussi bien:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateUtil {
private static final String[] timeFormats = {"HH:mm:ss","HH:mm"};
private static final String[] dateSeparators = {"/","-"," "};
private static final String DMY_FORMAT = "dd{sep}MM{sep}yyyy";
private static final String YMD_FORMAT = "yyyy{sep}MM{sep}dd";
private static final String ymd_template = "\d{4}{sep}\d{2}{sep}\d{2}.*";
private static final String dmy_template = "\d{2}{sep}\d{2}{sep}\d{4}.*";
public static Date stringToDate(String input){
Date date = null;
String dateFormat = getDateFormat(input);
if(dateFormat == null){
throw new IllegalArgumentException("Date is not in an accepted format " + input);
}
for(String sep : dateSeparators){
String actualDateFormat = patternForSeparator(dateFormat, sep);
//try first with the time
for(String time : timeFormats){
date = tryParse(input,actualDateFormat + " " + time);
if(date != null){
return date;
}
}
//didn't work, try without the time formats
date = tryParse(input,actualDateFormat);
if(date != null){
return date;
}
}
return date;
}
private static String getDateFormat(String date){
for(String sep : dateSeparators){
String ymdPattern = patternForSeparator(ymd_template, sep);
String dmyPattern = patternForSeparator(dmy_template, sep);
if(date.matches(ymdPattern)){
return YMD_FORMAT;
}
if(date.matches(dmyPattern)){
return DMY_FORMAT;
}
}
return null;
}
private static String patternForSeparator(String template, String sep){
return template.replace("{sep}", sep);
}
private static Date tryParse(String input, String pattern){
try{
return new SimpleDateFormat(pattern).parse(input);
}
catch (ParseException e) {}
return null;
}
}
cette solution vérifie tous les formats possibles avant de lancer une exception. Cette solution est plus pratique si vous essayez de tester des formats de date multiples.
Date extractTimestampInput(String strDate){
final List<String> dateFormats = Arrays.asList("yyyy-MM-dd HH:mm:ss.SSS", "yyyy-MM-dd");
for(String format: dateFormats){
SimpleDateFormat sdf = new SimpleDateFormat(format);
try{
return sdf.parse(strDate);
} catch (ParseException e) {
//intentionally empty
}
}
throw new IllegalArgumentException("Invalid input for date. Given '"+strDate+"', expecting format yyyy-MM-dd HH:mm:ss.SSS or yyyy-MM-dd.");
}
si vous travaillez en Java 1.8, Vous pouvez utiliser le DateTimeFormatterBuilder
public static boolean isTimeStampValid(String inputString)
{
DateTimeFormatterBuilder dateTimeFormatterBuilder = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ofPattern("" + "[yyyy-MM-dd'T'HH:mm:ss.SSSZ]" + "[yyyy-MM-dd]"));
DateTimeFormatter dateTimeFormatter = dateTimeFormatterBuilder.toFormatter();
try {
dateTimeFormatter.parse(inputString);
return true;
} catch (DateTimeParseException e) {
return false;
}
}
Voir le post: Java 8 Date équivalente à Joda est DateTimeFormatterBuilder avec plusieurs analyseur de formats?
pour la réponse moderne, j'ignore l'exigence d'utiliser SimpleDateFormat
. Bien que l'utilisation de cette classe pour l'analyse était une bonne idée en 2010 lorsque cette question a été posée, il est maintenant dépassé depuis longtemps. Le remplacement, DateTimeFormatter
, est sorti en 2014. L'idée dans ce qui suit est à peu près la même que dans la réponse acceptée.
private static DateTimeFormatter[] parseFormatters = Stream.of("M/yy", "M/y", "M/d/y", "M-d-y")
.map(DateTimeFormatter::ofPattern)
.toArray(DateTimeFormatter[]::new);
public static YearMonth parseYearMonth(String input) {
for (DateTimeFormatter formatter : parseFormatters) {
try {
return YearMonth.parse(input, formatter);
} catch (DateTimeParseException dtpe) {
// ignore, try next format
}
}
throw new IllegalArgumentException("Could not parse " + input);
}
ceci divise chacune des chaînes de saisie de la question en une année-mois de 2009-09
. Il est important de tenter l' deux chiffres année première depuis "M/y"
pourrait également Parser 9/09
, mais dans 0009-09
à la place.
une limitation du code ci-dessus est qu'il ignore le jour du mois des chaînes qui ont un, comme 9/1/2009
. Peut-être que c'est OK aussi longtemps que la plupart des formats ont seulement le mois et l'année. Pour le récupérer, nous devrions essayer LocalDate.parse()
plutôt que YearMonth.parse()
pour les formats qui incluent d
dans la chaîne de motifs. Il peut certainement être fait.
Voici l'exemple complet (avec la méthode principale) qui peut être ajouté comme classe d'utilité dans votre projet. Tous les formats mentionnés dans L'API SimpleDateFormate sont pris en charge dans la méthode ci-dessous.
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.commons.lang.time.DateUtils;
public class DateUtility {
public static Date parseDate(String inputDate) {
Date outputDate = null;
String[] possibleDateFormats =
{
"yyyy.MM.dd G 'at' HH:mm:ss z",
"EEE, MMM d, ''yy",
"h:mm a",
"hh 'o''clock' a, zzzz",
"K:mm a, z",
"yyyyy.MMMMM.dd GGG hh:mm aaa",
"EEE, d MMM yyyy HH:mm:ss Z",
"yyMMddHHmmssZ",
"yyyy-MM-dd'T'HH:mm:ss.SSSZ",
"yyyy-MM-dd'T'HH:mm:ss.SSSXXX",
"YYYY-'W'ww-u",
"EEE, dd MMM yyyy HH:mm:ss z",
"EEE, dd MMM yyyy HH:mm zzzz",
"yyyy-MM-dd'T'HH:mm:ssZ",
"yyyy-MM-dd'T'HH:mm:ss.SSSzzzz",
"yyyy-MM-dd'T'HH:mm:sszzzz",
"yyyy-MM-dd'T'HH:mm:ss z",
"yyyy-MM-dd'T'HH:mm:ssz",
"yyyy-MM-dd'T'HH:mm:ss",
"yyyy-MM-dd'T'HHmmss.SSSz",
"yyyy-MM-dd",
"yyyyMMdd",
"dd/MM/yy",
"dd/MM/yyyy"
};
try {
outputDate = DateUtils.parseDate(inputDate, possibleDateFormats);
System.out.println("inputDate ==> " + inputDate + ", outputDate ==> " + outputDate);
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return outputDate;
}
public static String formatDate(Date date, String requiredDateFormat) {
SimpleDateFormat df = new SimpleDateFormat(requiredDateFormat);
String outputDateFormatted = df.format(date);
return outputDateFormatted;
}
public static void main(String[] args) {
DateUtility.parseDate("20181118");
DateUtility.parseDate("2018-11-18");
DateUtility.parseDate("18/11/18");
DateUtility.parseDate("18/11/2018");
DateUtility.parseDate("2018.11.18 AD at 12:08:56 PDT");
System.out.println("");
DateUtility.parseDate("Wed, Nov 18, '18");
DateUtility.parseDate("12:08 PM");
DateUtility.parseDate("12 o'clock PM, Pacific Daylight Time");
DateUtility.parseDate("0:08 PM, PDT");
DateUtility.parseDate("02018.Nov.18 AD 12:08 PM");
System.out.println("");
DateUtility.parseDate("Wed, 18 Nov 2018 12:08:56 -0700");
DateUtility.parseDate("181118120856-0700");
DateUtility.parseDate("2018-11-18T12:08:56.235-0700");
DateUtility.parseDate("2018-11-18T12:08:56.235-07:00");
DateUtility.parseDate("2018-W27-3");
}
}
mis en œuvre la même chose en scala, s'il vous plaît aidez-vous à convertir en Java, la logique de base et les fonctions utilisées restent les mêmes.
import java.text.SimpleDateFormat
import org.apache.commons.lang.time.DateUtils
object MultiDataFormat {
def main(args: Array[String]) {
val dates =Array("2015-10-31","26/12/2015","19-10-2016")
val possibleDateFormats:Array[String] = Array("yyyy-MM-dd","dd/MM/yyyy","dd-MM-yyyy")
val sdf = new SimpleDateFormat("yyyy-MM-dd") //change it as per the requirement
for (date<-dates) {
val outputDate = DateUtils.parseDateStrictly(date, possibleDateFormats)
System.out.println("inputDate ==> " + date + ", outputDate ==> " +outputDate + " " + sdf.format(outputDate) )
}
}
}