如何在Java中获取给定的日期字符串格式(模式)?

47
我想获取给定日期字符串的格式。
例如:我有一个字符串,如2011-09-27T07:04:21.97-05:00,该字符串的日期格式为yyyy-MM-dd'T'HH:mm:ss.SSS
在这里,当我将字符串(2011-09-27T07:04:21.97-05:00)传递给一个方法时,我想找出这个日期格式,该方法将返回格式(yyyy-MM-dd'T'HH:mm:ss.SSS),然后我将根据我的要求格式化给定的日期字符串(如yy-mm--dd或mm/dd/yyyy)。
请问如何实现?

@Tomasz Nurkiewicz,不是的。这是一个从一种格式转换到另一种格式的日期转换问题。 - Buhake Sindi
哈哈,我浪费了时间滚动这个问题的JavaScript答案..没用...它不是重复的问题..请取消重复标记。 - Vikas Chowdhury
@MingweiSamuel 三年半后,它重新开放了。 - Mikael
10个回答

59
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class NewClass {

    private static final String[] formats = { 
                "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'HH:mm:ss.SSS'Z'",
                "yyyy-MM-dd'T'HH:mm:ss.SSSZ", "yyyy-MM-dd HH:mm:ss", 
                "MM/dd/yyyy HH:mm:ss",        "MM/dd/yyyy'T'HH:mm:ss.SSS'Z'", 
                "MM/dd/yyyy'T'HH:mm:ss.SSSZ", "MM/dd/yyyy'T'HH:mm:ss.SSS", 
                "MM/dd/yyyy'T'HH:mm:ssZ",     "MM/dd/yyyy'T'HH:mm:ss", 
                "yyyy:MM:dd HH:mm:ss",        "yyyyMMdd", };

        /*
         * @param args
         */
    public static void main(String[] args) {
        String yyyyMMdd = "20110917";   
        parse(yyyyMMdd);
    }

    public static void parse(String d) {
        if (d != null) {
            for (String parse : formats) {
                SimpleDateFormat sdf = new SimpleDateFormat(parse);
                try {
                    sdf.parse(d);
                    System.out.println("Printing the value of " + parse);
                } catch (ParseException e) {

                }
            }
        }
    }
}

1
上述解决方案可以确保解析日期,但无法提供日期格式。SimpleDateFormat的toPattern和toLocalizedPattern将给出确切的格式。 - Vikas Chowdhury
如果有人正在使用此解决方案,只需在打印值后返回解析字符串即可。 - Narasimha
永远不要在格式模式中硬编码 Z 作为文字字面量。它是一个偏移量,需要解析为这样,否则会得到错误的结果。2020年的一个评论是:我们不应该再使用 DateSimpleDateFormat。这些类设计很差,而且已经过时了。 - Ole V.V.

10

你可以像这样做,我不知道是否好用,但可以试试:

首先创建SimpleDateFormat对象。

SimpleDateFormt sdf = new SimpleDateFormat("yyyy-MM-dd 'T' HH:mm:ss.SSS");

现在当检查日期是否可以按照此格式解析时,根据您的格式进行修改。

try{
     Date date = sdf.parse(yourdate);
     sdf.applyPattern("yy-mm--dd or mm/dd/yyyy");
     String dateformat = sdf.format(date);
}catch(Exception ex) { // here forgot the exact exception class Parse exception was used
    // do something here
}

更新的帖子:

在Java中从未知的日期字符串格式返回日期格式

如何在不知道日期格式的情况下将字符串转换为日期?

在Java中解析任何日期


2
我如何在不知道给定字符串格式的情况下指定SimpleDateFormat的格式? - sathish
我如何在不知道给定字符串格式的情况下指定SimpleDateFormat的格式为“yyyy-mm-dd'T'HH:mm:ss.SSS”?如果我的字符串类似于2011-09-28 12:00:22,那么我应该如何使用上述SimpleDateFormat对象解析它? - sathish

3
我认为您应该尝试使用一些预定义模式来解析输入字符串。您需要的是有效的模式。请记住,有些模式非常棘手。
在欧洲,01.12.12表示2012年12月1日,而在美国表示2012年1月12日。它也可能表示2001年12月12日。

2

2
如果我理解正确,您想通过使用 DateFormat.parse() 将任意格式的字符串(即您不知道其格式的字符串)解析为日期?那么您需要处理如何处理 01-02-03(是2003年1月2日?还是2003年2月1日等等)等问题。
您应该至少了解一些有关预期格式的信息,比如输入格式的几个预定义选择。

1
这里有一个通用解决方案,可以在不提前知道日期模式且不调用所有格式的SimpleDateFormat解析方法的情况下确定模式。通过使用正则表达式,您可以从日期字符串值中获取任何日期模式。
package com.utility.utils.modelmapper.datetime;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class DateParser {
    private static final Map<String, String> DATE_FORMAT_REGEXPS = new HashMap<String, String>() {
        {
            put("^\\d{8}$", "yyyyMMdd");
            put("^\\d{12}$", "yyyyMMddHHmm");
            put("^\\d{8}\\s\\d{4}$", "yyyyMMdd HHmm");
            put("^\\d{14}$", "yyyyMMddHHmmss");
            put("^\\d{8}\\s\\d{6}$", "yyyyMMdd HHmmss");
            put("^\\d{1,2}-\\d{1,2}-\\d{4}$", "dd-MM-yyyy");
            put("^\\d{4}-\\d{1,2}-\\d{1,2}$", "yyyy-MM-dd");
            put("^\\d{1,2}/\\d{1,2}/\\d{4}$", "MM/dd/yyyy");
            put("^\\d{4}/\\d{1,2}/\\d{1,2}$", "yyyy/MM/dd");
            put("^\\d{1,2}\\s[a-z]{3}\\s\\d{4}$", "dd MMM yyyy");
            put("^\\d{1,2}\\s[a-z]{4,}\\s\\d{4}$", "dd MMMM yyyy");
            put("^\\d{1,2}-\\d{1,2}-\\d{4}\\s\\d{1,2}:\\d{2}$", "dd-MM-yyyy HH:mm");
            put("^\\d{4}-\\d{1,2}-\\d{1,2}\\s\\d{1,2}:\\d{2}$", "yyyy-MM-dd HH:mm");
            put("^\\d{1,2}/\\d{1,2}/\\d{4}\\s\\d{1,2}:\\d{2}$", "MM/dd/yyyy HH:mm");
            put("^\\d{4}/\\d{1,2}/\\d{1,2}\\s\\d{1,2}:\\d{2}$", "yyyy/MM/dd HH:mm");
            put("^\\d{1,2}\\s[a-z]{3}\\s\\d{4}\\s\\d{1,2}:\\d{2}$", "dd MMM yyyy HH:mm");
            put("^\\d{1,2}\\s[a-z]{4,}\\s\\d{4}\\s\\d{1,2}:\\d{2}$", "dd MMMM yyyy HH:mm");
            put("^\\d{1,2}-\\d{1,2}-\\d{4}\\s\\d{1,2}:\\d{2}:\\d{2}$", "dd-MM-yyyy HH:mm:ss");
            put("^\\d{4}-\\d{1,2}-\\d{1,2}\\s\\d{1,2}:\\d{2}:\\d{2}$", "yyyy-MM-dd HH:mm:ss");
            put("^\\d{1,2}/\\d{1,2}/\\d{4}\\s\\d{1,2}:\\d{2}:\\d{2}$", "MM/dd/yyyy HH:mm:ss");
            put("^\\d{4}/\\d{1,2}/\\d{1,2}\\s\\d{1,2}:\\d{2}:\\d{2}$", "yyyy/MM/dd HH:mm:ss");
            put("^\\d{1,2}\\s[a-z]{3}\\s\\d{4}\\s\\d{1,2}:\\d{2}:\\d{2}$", "dd MMM yyyy HH:mm:ss");
            put("^\\d{1,2}\\s[a-z]{4,}\\s\\d{4}\\s\\d{1,2}:\\d{2}:\\d{2}$", "dd MMMM yyyy HH:mm:ss");
            put("^\\d{4}-\\d{1,2}-\\d{1,2}T\\d{1,2}:\\d{2}:\\d{2}\\.\\d{2}[-+]\\d{2}:\\d{2}$", "yyyy-MM-dd'T'HH:mm:ss.SSS");
        }
    };

    /**
     * To Determine the pattern by the string date value
     * 
     * @param dateString
     * @return The matching SimpleDateFormat pattern, or null if format is unknown.
     */
    public static String determineDateFormat(String dateString) {
        for (String regexp : DATE_FORMAT_REGEXPS.keySet()) {
            if (dateString.matches(regexp) || dateString.toLowerCase().matches(regexp)) {
                return DATE_FORMAT_REGEXPS.get(regexp);
            }
        }
        return null;
    }

    public static void main(String[] args) {
        parse("2011-09-27T07:04:21.97-05:00"); //here is your value
        parse("20110917");
        parse("01/02/2018");
        parse("02-01-2018 06:07:59");
        parse("02 January 2018");
    }

    public static void parse(String value) {
        if (value != null) {
            String format = determineDateFormat(value);
            if (format != null) {
                SimpleDateFormat sdf = new SimpleDateFormat(format);
                try {
                    Date date = sdf.parse(value);
                    System.out.println(String.format("Format : %s | Value : %s | Parsed Date : %s", value, date, format));
                } catch (ParseException e) {
                    // Failed the execution
                }
            }
        }
    }
}

该类的控制台输出:

Format : 2011-09-27T07:04:21.97-05:00 | Value : Tue Sep 27 07:04:21 LINT 2011 | Parsed Date : yyyy-MM-dd'T'HH:mm:ss.SSS
Format : 20110917 | Value : Sat Sep 17 00:00:00 LINT 2011 | Parsed Date : yyyyMMdd
Format : 01/02/2018 | Value : Tue Jan 02 00:00:00 LINT 2018 | Parsed Date : MM/dd/yyyy
Format : 02-01-2018 06:07:59 | Value : Tue Jan 02 06:07:59 LINT 2018 | Parsed Date : dd-MM-yyyy HH:mm:ss
Format : 02 January 2018 | Value : Tue Jan 02 00:00:00 LINT 2018 | Parsed Date : dd MMMM yyyy

也许我错过了一些日期时间模式,但是为了正确匹配,需要在映射中添加正确的正则表达式模式。

请不要教年轻人使用早已过时且臭名昭著的SimpleDateFormat类。至少不要将其作为第一选择。而且不要毫无保留地使用它。今天,我们有更好的选择——java.time,这是现代Java日期和时间API,以及它的DateTimeFormatter。否则,这是一个创造性的解决方案。 - Ole V.V.
1
这个示例并不是为了展示 SimpleDateFormat 如何工作,而是看问题所在——它是关于获取日期格式的。我只是使用 SimpleDateFormat 来展示它实际上获取了正确的格式。在获取到正确的格式后,可以使用任何你喜欢的方式。 - Vinit Solanki
我猜这将是一个单独的话题,讨论是否使用“双大括号反模式”。 - Vinit Solanki
你显然有权发表自己的观点。我的观点是,当你发布一个包含两个不良实践的答案时,却没有提到它们是不良实践,你很可能会误导读者,而不是帮助他们。 - Ole V.V.
显示剩余2条评论

1

您可以尝试使用dateparser

它可以自动识别任何字符串,并将其正确快速地解析为日期日历本地日期时间偏移日期时间1us~1.5us)。

它不基于任何自然语言分析器SimpleDateFormat或者regex.Pattern

使用它,您无需准备任何适当的模式,如yyyy-MM-dd'T'HH:mm:ss'Z'MM/dd/yyyy HH:mm:ss等:

Date date = DateParserUtils.parseDate("2015-04-29T10:15:00.500+0000");
Calendar calendar = DateParserUtils.parseCalendar("2015-04-29T10:15:00.500Z");
LocalDateTime dateTime = DateParserUtils.parseDateTime("2015-04-29 10:15:00.500 +00:00");

它比循环尝试多个SimpleDateFormat具有更好的性能。

请享用。


0
你需要将初始日期字符串转换为日期对象,并将该转换后的日期对象格式化为所需的字符串。

0

java.time及其预定义格式化程序

我们不能为任何日期时间格式都这样做。有成千上万种格式,我们不可能都知道(明天会有人发明新的),有些看起来非常相似,我们无法确定我们得到了哪个。

我建议对于大多数目的,您需要解析字符串,但不需要知道格式模式。在很多情况下,包括您问题中的示例2011-09-27T07:04:21.97-05:00,我们不需要指定模式(您的字符串与DateTimeFormatter.ISO_OFFSET_DATE_TIME匹配)。

自从Java 8于2014年发布以来(即使仍在使用Java 6或7),请使用现代Java日期和时间API java.time 处理日期和时间。

我正在定义一个格式化程序数组,用于处理我们想要处理的格式。请替换为您自己的设置。

private static final DateTimeFormatter[] formatters = {
        DateTimeFormatter.ISO_OFFSET_DATE_TIME,
        DateTimeFormatter.RFC_1123_DATE_TIME,
        new DateTimeFormatterBuilder().append(DateTimeFormatter.ISO_LOCAL_DATE)
                .parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
                .parseDefaulting(ChronoField.OFFSET_SECONDS, 0)
                .toFormatter(),
        DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG).withLocale(Locale.US),
        DateTimeFormatter.ofPattern("MM/dd/uuuu HH:mm")
                .withZone(ZoneId.of("America/Los_Angeles"))
};

以下方法会依次尝试各种格式化方式,直到找到可用的为止:

private static OffsetDateTime parse(String dateTimeString) {
    for (DateTimeFormatter formatter : formatters) {
        try {
            return ZonedDateTime.parse(dateTimeString, formatter)
                    .toOffsetDateTime();
        } catch (DateTimeParseException dtpe) {
            // Ignore, try next formatter
        }
    }
    throw new IllegalArgumentException("String " + dateTimeString + " could not be parsed");
} 

让我们尝试一些不同的字符串:

    String[] dateTimeStrings = {
            "2011-09-27T07:04:21.97-05:00",
            "20110917",
            "2012-07-04",
            "12/27/2014 23:45",
            "Mon, 12 Nov 2018 01:32:10 GMT",
            "July 29, 2015 at 10:19:36 AM EDT",
    };
    
    for (String dts : dateTimeStrings) {
        try {
            System.out.format("%32s -> %s%n", dts, parse(dts));
        } catch (IllegalArgumentException iae) {
            System.out.format("%32s -> %s%n", dts, iae);
        }
    }

输出为:

    2011-09-27T07:04:21.97-05:00 -> 2011-09-27T07:04:21.970-05:00
                        20110917 -> java.lang.IllegalArgumentException: String 20110917 could not be parsed
                      2012-07-04 -> 2012-07-04T00:00Z
                12/27/2014 23:45 -> 2014-12-27T23:45-08:00
   Mon, 12 Nov 2018 01:32:10 GMT -> 2018-11-12T01:32:10Z
July 29, 2015 at 10:19:36 AM EDT -> 2015-07-29T10:19:36-04:00

其他选项

解析多种格式的日期和时间的技术包括:

  • 尝试检测字符串的格式并根据其使用适当的格式化程序。如果您只有几种格式,这是最合适的方法,尽管 Vinit Solanki 的答案展示了一个详细的版本,适用于相当多的格式。
  • 在格式模式字符串中使用可选部分。例如 [uuuu][uu] 将解析四位或两位年份(2021 或只有 21)。
  • 按照我上面的代码一样尝试多个格式化程序。如果您确实需要知道模式,请使用模式数组而不是格式化程序数组。
  • 要求字符串供应商提供格式模式字符串。这可能并不总是像听起来那样简单。

注意歧义。经典例子是两种格式MM-dd-yyyydd-MM-yyyy。如果我们得到一个字符串03-09-2020,就无法确定它是3月9日还是9月3日。更糟糕的是,02-05-07可能是yy-MM-dddd-MM-yyMM-dd-yy甚至更多的可能性。因此,请确保不要包含可能将同一字符串解析为不同结果的两个(或更多)格式化程序。

链接


-3
HH:mm:ss.SSS => ([0-2]{1,}[0-9]{1,})(:)([0-5]{1,}[0-9]{1,})(:)([0-5]{1,}[0-9]{1,})(.)([0-9]{1,3})

yyyy-mm-dd => ([0-9]{4})(-)([0-1]{1,}[0-9]{1,})(-)([0-3]{1,}[0-9]{1,})

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接