Pergunta

Estou tentando analisar algumas datas que estão saindo de um documento. Parece que os usuários inseriram essas datas em um formato semelhante, mas não exato.

Aqui estão os formatos:

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

Qual é a melhor maneira de tentar analisar tudo isso? Estes parecem ser os mais comuns, mas acho que o que está me enforcando é que, se eu tiver um padrão de "m/aaa", não é sempre capturado antes de "mm/yyyy", eu tenho que configurar meus blocos de tentativa/captura aninhado de maneira menos restritiva à maneira mais restritiva? Parece que certamente levará muita duplicação de código para acertar isso.

Foi útil?

Solução

Você precisará usar um diferente SimpleDateFormat objeto para cada padrão diferente. Dito isto, você não precisa de tantos diferentes, graças a isto:

Número: Para a formatação, o número de letras de padrão é o número mínimo de dígitos e os números mais curtos são acolchoados com zero para esse valor. Para análise, o número de letras de padrão é ignorado, a menos que seja necessário para separar dois campos adjacentes.

Então, você precisará desses formatos:

  • "M/y" (Isso cobre 9/09, 9/2009, e 09/2009)
  • "M/d/y" (Isso cobre 9/1/2009)
  • "M-d-y" (Isso cobre 9-1-2009)

Então, meu conselho seria escrever um método que funcione algo assim (não testado):

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

Outras dicas

Que tal definir vários padrões? Eles podem vir de um arquivo de configuração que contém padrões conhecidos, codificados com força como:

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;

A abordagem de Matt acima está bem, mas esteja ciente de que você terá problemas se usá -la para diferenciar entre datas do formato y/M/d e d/M/y. Por exemplo, um formatador inicializado com y/M/d vai aceitar uma data como 01/01/2009 e devolver uma data que claramente não é o que você queria. Corrigi o problema da seguinte maneira, mas tenho tempo limitado e não estou feliz com a solução por dois motivos principais:

  1. Ele viola uma das quidelinas de Josh Bloch, especificamente "não use exceções para lidar com o fluxo do programa".
  2. Eu posso ver o getDateFormat() Método tornando -se um pesadelo, se você precisar de lidar com muitos outros formatos de data.

Se eu tivesse que fazer algo que pudesse lidar com muitos e muitos formatos de data e precisava ser altamente executado, acho que usaria a abordagem de criar uma enumeração que vinculasse cada data regex diferente ao seu formato. Em seguida, use MyEnum.values() Para fazer um loop através da enumeração e testar com if(myEnum.getPattern().matches(date)) em vez de pegar uma dataForMatexception.

Isto é, dito, o seguinte pode lidar com datas dos formatos 'y/M/d' 'y-M-d' 'y M d' 'd/M/y' 'd-M-y' 'd M y' E todas as outras variações daqueles que incluem formatos de tempo também:

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


}

No Apache Commons Lang, DateUtils Classe, temos um método chamado Parsedate. Podemos usar isso para analisar a data.

Além disso, outra biblioteca Joda-time também tem o método para analisar a data.

Esta solução verifica todos os formatos possíveis antes de lançar uma exceção. Esta solução é mais conveniente se você estiver tentando testar vários formatos de data.

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

}

Aqui está o exemplo completo (com o método principal) que pode ser adicionado como uma classe de utilitário em seu projeto. Todo o formato mencionado em SimpledateFormate A API é suportada no método abaixo.

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

}

Se estiver trabalhando no Java 1.8, você pode aproveitar o 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;
    }
}

Veja Post: Java 8 Data equivalente aos DateTimeFormatterBuilder de Joda com vários formatos de analisador?

Para a resposta moderna, estou ignorando o requisito de usar SimpleDateFormat. Embora usar essa aula para análise fosse uma boa idéia em 2010, quando essa pergunta foi feita, agora está desatualizada há muito tempo. A substituição, DateTimeFormatter, saiu em 2014. A idéia a seguir é praticamente a mesma da resposta aceita.

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

Isso analisa cada uma das seqüências de entrada da questão em um ano de ano de 2009-09. É importante experimentar o ano de dois dígitos primeiro desde "M/y" também poderia analisar 9/09, mas em 0009-09 em vez de.

Uma limitação do código acima é que ele ignora o dia do mês das cordas que têm uma, como 9/1/2009. Talvez esteja tudo bem, desde que a maioria dos formatos tenha apenas mês e ano. Para buscá -lo, teríamos que tentar LocalDate.parse() Em vez disso YearMonth.parse() Para os formatos que incluem d na sequência de padrões. Certamente isso pode ser feito.

Implementou o mesmo em Scala, ajude -se a converter para Java, a lógica e as funções usadas permanecem as mesmas.

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

}

Usando o DATETIMEformatter, ele pode ser alcançado como abaixo:


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

Preste atenção em String pattern, esta é a combinação de vários padrões. Em aberto [ e fechar ] Suportes quadrados Você pode mencionar qualquer tipo de padrões.

Eu estava tendo vários formatos de data no JSON e estava extraindo CSV com formato universal. Eu parecia vários lugares, tentei maneiras diferentes, mas no final sou capaz de converter com o seguinte código simples.

private String getDate(String anyDateFormattedString) {
    @SuppressWarnings("deprecation")
    Date date = new Date(anyDateFormattedString);
    SimpleDateFormat dateFormat = new SimpleDateFormat(yourDesiredDateFormat);
        String convertedDate = dateFormat.format(date);
    return convertedDate;
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top