【问题标题】:How to parse dates in multiple formats using SimpleDateFormat如何使用 SimpleDateFormat 解析多种格式的日期
【发布时间】:2011-04-30 18:53:02
【问题描述】:

我正在尝试解析来自文档的一些日期。用户似乎以类似但不准确的格式输入了这些日期。

格式如下:

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

尝试解析所有这些的最佳方法是什么?这些似乎是最常见的,但我想让我感到困惑的是,如果我有一个“M/yyyy”模式不会总是在“MM/yyyy”之前捕获我是否必须设置我的 try/catch 块以最少限制到最多限制的方式嵌套?似乎肯定需要大量的代码重复才能做到这一点。

【问题讨论】:

  • 嗯,你可以只列出一个模式,然后一个简单的循环继续尝试,直到你找到一个有效的。
  • 然后简单地打破循环?

标签: java parsing datetime simpledateformat


【解决方案1】:

您需要为每个不同的模式使用不同的SimpleDateFormat 对象。也就是说,你不需要那么多不同的,thanks to this:

数字:对于格式化,模式字母的数量是最小位数,较短的数字在此数量上补零。解析时,模式字母的数量会被忽略,除非需要分隔两个相邻的字段。

所以,你需要这些格式:

  • "M/y"(包括 9/09、9/2009 和 09/2009)
  • "M/d/y"(包括9/1/2009)
  • "M-d-y"(包括9-1-2009)

所以,我的建议是编写一个像这样工作的方法(未经测试):

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

【讨论】:

  • 这里的许多答案都提供了某种形式的解决方案,但不幸的是它不起作用。如果要解析的字符串与格式#3 匹配,SimpleDateFormat 很可能会成功解析格式#1,但您会得到错误的日期。
【解决方案2】:

如果只定义多个模式呢?它们可能来自包含已知模式的配置文件,硬编码如下:

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;

【讨论】:

  • 嘿,我正在尝试查看所有这些模式 "yyyy-MM-dd'T'HH:mm:ss'Z'" 你能指出这些模式的示例或文档吗@xybrek
  • 很棒的解决方案。非常感谢分享。!
【解决方案3】:

上述Matt 的方法很好,但请注意,如果您使用它来区分y/M/d 和d/M/y 格式的日期,您会遇到问题。例如,使用y/M/d 初始化的格式化程序将接受01/01/2009 之类的日期,并返回一个显然不是您想要的日期。我按如下方式解决了这个问题,但我的时间有限,我对解决方案不满意主要有两个原因:

  1. 它违反了 Josh Bloch 的准则之一,特别是“不要使用异常来处理程序流”。
  2. 如果您需要 getDateFormat() 方法来处理许多其他日期格式,我可以看到它变得有点像噩梦。

如果我必须制作一些可以处理大量不同日期格式并且需要高性能的东西,那么我想我会使用创建一个枚举的方法,将每个不同的日期正则表达式链接到它的格式。然后使用MyEnum.values() 循环遍历枚举并使用if(myEnum.getPattern().matches(date)) 进行测试,而不是捕获日期格式异常。

话虽如此,以下可以处理格式'y/M/d' 'y-M-d' 'y M d' 'd/M/y' 'd-M-y' 'd M y' 的日期以及包括时间格式的所有其他变体:

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


}

【讨论】:

    【解决方案4】:

    如果在 Java 1.8 中工作,您可以利用 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;
        }
    }
    

    见帖子:Java 8 Date equivalent to Joda's DateTimeFormatterBuilder with multiple parser formats?

    【讨论】:

      【解决方案5】:

      在 Apache commons lang,DateUtils 类中,我们有一个名为 parseDate 的方法。我们可以用它来解析日期。

      另外一个库Joda-time也有parse日期的方法。

      【讨论】:

        【解决方案6】:

        这是一个完整的例子(带有 main 方法),它可以作为一个实用程序类添加到您的项目中。以下方法支持SimpleDateFormate API 中提到的所有格式。

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

        【讨论】:

          【解决方案7】:

          此解决方案在引发异常之前检查所有可能的格式。如果您尝试测试多种日期格式,此解决方案会更方便。

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

          【讨论】:

            【解决方案8】:

            对于现代答案,我忽略了使用SimpleDateFormat 的要求。虽然在 2010 年提出这个问题时使用这个类进行解析是一个好主意,但现在它已经过时了。替代品 DateTimeFormatter 于 2014 年问世。下面的想法与接受的答案几乎相同。

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

            这会将问题中的每个输入字符串解析为2009-09 的年月。首先尝试两位数的年份很重要,因为"M/y" 也可以解析9/09,但会解析为0009-09。

            上述代码的一个限制是它忽略了字符串中的日期,例如9/1/2009。也许只要大多数格式只有月份和年份就可以了。要获取它,我们必须尝试 LocalDate.parse() 而不是 YearMonth.parse() 用于在模式字符串中包含 d 的格式。当然可以。

            【讨论】:

              【解决方案9】:

              我使用正则表达式更简单地解决了这个问题

              fun parseTime(time: String?): Long {
                  val longRegex = "\\d{4}+-\\d{2}+-\\d{2}+\\w\\d{2}:\\d{2}:\\d{2}.\\d{3}[Z]\$"
                  val shortRegex = "\\d{4}+-\\d{2}+-\\d{2}+\\w\\d{2}:\\d{2}:\\d{2}Z\$"
              
                  val longDateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.sssXXX")
                  val shortDateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX")
              
                  return when {
                      Pattern.matches(longRegex, time) -> longDateFormat.parse(time).time
                      Pattern.matches(shortRegex, time) -> shortDateFormat.parse(time).time
                      else -> throw InvalidParamsException(INVALID_TIME_MESSAGE, null)
                  }
              }
              

              【讨论】:

                【解决方案10】:

                最佳和简单的 Java 8 答案(来自 https://stackoverflow.com/a/59546290/2131040)

                final DateTimeFormatterBuilder dtfb = new DateTimeFormatterBuilder();
                dtfb.appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSSSSS"))
                .appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSSSS"))
                .appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSSS"))
                .appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSS"))
                .appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSS"))
                .appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSS"))
                    .appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"))
                    .appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SS"))
                    .appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.S"))
                    .parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
                    .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
                    .parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0);
                

                【讨论】:

                  【解决方案11】:

                  在scala中实现相同,请帮助自己转换为Java,使用的核心逻辑和功能保持不变。

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

                  }

                  【讨论】:

                    【解决方案12】:

                    使用 DateTimeFormatter 可以实现如下:

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

                    注意String pattern,这是多种模式的组合。在打开的[ 和关闭的] 方括号中,您可以提及任何类型的模式。

                    【讨论】:

                    • 当您可以使用现代 Java 日期和时间 API java.time 时,您没有理由还应该使用设计不佳且早已过时的 SimpleDateFormat、Date 和 @ 987654327@。混合旧与现代只会增加复杂性和混乱。
                    • 对于为 DateFormatter 和 ZonedDateTime 开发的 Android 开发人员也值得一提,您需要在版本 > 26 上运行您的应用程序
                    【解决方案13】:

                    我将多种日期格式转换为 json,并以通用格式提取 csv。我查看了多个地方,尝试了不同的方法,但最后我可以使用以下简单代码进行转换。

                    private String getDate(String anyDateFormattedString) {
                        @SuppressWarnings("deprecation")
                        Date date = new Date(anyDateFormattedString);
                        SimpleDateFormat dateFormat = new SimpleDateFormat(yourDesiredDateFormat);
                            String convertedDate = dateFormat.format(date);
                        return convertedDate;
                    }
                    

                    【讨论】:

                    • 我在尝试使用问题中的第一个样本9/09 时得到IllegalArgumentException。此外,您使用的构造函数也因某种原因被弃用,它跨时区工作不可靠,所以我们不应该使用它。
                    猜你喜欢
                    • 2018-02-07
                    • 2013-01-09
                    • 2020-09-14
                    • 1970-01-01
                    • 1970-01-01
                    • 2012-11-09
                    • 2020-07-04
                    • 1970-01-01
                    相关资源
                    最近更新 更多