Question

J'essaie d'analyser certaines dates qui sortent d'un document. Il semblerait que les utilisateurs aient entré ces dates dans un format similaire mais pas exact.

Voici les formats:

9/09
9/2009
09/2009
9/1/2009
9-1-2009 

Quelle est la meilleure façon d'essayer d'analyser tout cela? Ceux-ci semblent être les plus courants, mais je suppose que ce qui me raccroche, c'est que si j'ai un modèle de "m / yyyy" qui ne saisira pas toujours "mm / yyyy" imbriqué d'une manière la moins restrictive à la manière la plus restrictive? Il semble que cela prendra certainement beaucoup de duplication de code pour bien faire les choses.

Était-ce utile?

La solution

Vous devrez utiliser un autre SimpleDateFormat objet pour chaque modèle différent. Cela dit, vous n'avez pas besoin de beaucoup de différents, grâce à ça:

Numéro: Pour le formatage, le nombre de lettres de motif est le nombre minimum de chiffres et les nombres plus courts sont nuls à ce montant. Pour l'analyse, le nombre de lettres de modèle est ignoré à moins qu'il ne soit nécessaire pour séparer deux champs adjacents.

Vous aurez donc besoin de ces formats:

  • "M/y" (Cela couvre 9/09, 9/2009, et 09/2009)
  • "M/d/y" (Cela couvre 9/1/2009)
  • "M-d-y" (Cela couvre 9-1-2009)

Donc, mon conseil serait d'écrire une méthode qui fonctionne comme ça (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;
}

Autres conseils

Qu'en est-il de définir plusieurs modèles? Ils pourraient provenir d'un fichier de configuration contenant des modèles 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 sachez que vous rencontrerez des problèmes si vous l'utiliserez pour différencier les dates du format y/M/d et d/M/y. Par exemple, un formateur a initialisé avec y/M/d acceptera une date comme 01/01/2009 Et vous rendre une date qui n'est clairement pas ce que vous vouliez. J'ai résolu le problème comme suit, mais j'ai un temps limité et je ne suis pas satisfait de la solution pour 2 raisons principales:

  1. Il viole l'une des Quidelines de Josh Bloch, en particulier «n'utilisez pas d'exceptions pour gérer le flux du programme».
  2. Je peux voir le getDateFormat() La méthode devient un peu un cauchemar si vous en avez besoin pour gérer de nombreux autres formats de date.

Si je devais faire quelque chose qui pourrait gérer beaucoup, beaucoup de formats de date différents et que je devais être très performant, alors je pense que j'utiliserais l'approche de la création d'une énumération qui a lié chaque date d'expansion différente à son format. Puis utiliser MyEnum.values() pour traverser l'énumération et tester avec if(myEnum.getPattern().matches(date)) plutôt que d'attraper une conception 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 incluent également des formats de temps:

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;
    }


}

Dans Apache Commons Lang, Dateutils Classe Nous avons une méthode appelée Parsedate. Nous pouvons l'utiliser pour analyser la date.

Une autre bibliothèque Joda-temps a également la méthode pour analyse la date.

Cette solution vérifie tous les formats possibles avant de lancer une exception. Cette solution est plus pratique si vous essayez de tester pour plusieurs formats de date.

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.");

}

Voici l'exemple complet (avec la méthode principale) qui peut être ajouté en tant que classe d'utilité dans votre projet. Tout le format mentionné dans SimpleFormate L'API est prise 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");
    }

}

Si vous travaillez dans Java 1.8, vous pouvez tirer parti du 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 la publication: Java 8 date équivalente à DateTimeFormatterBuilder de Joda avec plusieurs formats d'analyse?

Pour la réponse moderne, j'ignore l'exigence d'utilisation 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, elle est désormais obsolète. Le remplacement, DateTimeFormatter, est sorti en 2014. L'idée suivante 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);
}

Cela analyse chacune des chaînes d'entrée de la question en un mois 2009-09. Il est important d'essayer la première année à deux chiffres depuis "M/y" pourrait également analyser 9/09, mais dans 0009-09 Au lieu.

Une limitation du code ci-dessus est qu'elle ignore le jour de mois des chaînes qui en ont une, comme 9/1/2009. Peut-être que ça va tant que la plupart des formats n'ont qu'un mois et un an. Pour le ramasser, nous devons essayer LocalDate.parse() plutôt que YearMonth.parse() pour les formats qui incluent d Dans la chaîne de motif. Cela peut sûrement être fait.

Implémenté la même chose dans Scala, veuillez vous aider à se 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) )
  }
}

}

En utilisant DateTimeFormatter, il peut être réalisé comme ci-dessous:


import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAccessor;
import java.util.Date;
import java.util.TimeZone;

public class DateTimeFormatTest {

    public static void main(String[] args) {

        String pattern = "[yyyy-MM-dd[['T'][ ]HH:mm:ss[.SSSSSSSz][.SSS[XXX][X]]]]";
        String timeSample = "2018-05-04T13:49:01.7047141Z";
        SimpleDateFormat simpleDateFormatter = new SimpleDateFormat("dd/MM/yy HH:mm:ss");
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
        TemporalAccessor accessor = formatter.parse(timeSample);
        ZonedDateTime zTime = LocalDateTime.from(accessor).atZone(ZoneOffset.UTC);

        Date date=new Date(zTime.toEpochSecond()*1000);
        simpleDateFormatter.setTimeZone(TimeZone.getTimeZone(ZoneOffset.UTC));
        System.out.println(simpleDateFormatter.format(date));       
    }
}

Faire attention à String pattern, c'est la combinaison de plusieurs modèles. Ouvert [ et fermer ] Bracets, vous pouvez mentionner tout type de motifs.

J'avais des formats de dattes multiples en JSON et je extrait le CSV avec un format universel. J'ai regardé plusieurs endroits, j'ai essayé de différentes manières, mais à la fin, je suis capable de me convertir avec le code simple suivant.

private String getDate(String anyDateFormattedString) {
    @SuppressWarnings("deprecation")
    Date date = new Date(anyDateFormattedString);
    SimpleDateFormat dateFormat = new SimpleDateFormat(yourDesiredDateFormat);
        String convertedDate = dateFormat.format(date);
    return convertedDate;
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top