如何格式化输入中的日期字符串可以是多种字符串格式

how to format date string from input could be of multiple string formats

可以输入日期字符串

"2020-01-25T21:59:27Z"

"Sat Jan 25 20:06:07 +0000 2020"

Long

并希望显示的内容类似于

Jan 25, 2020

如何获取所需的格式化日期字符串?

更新:

@Ole V.V 提供了一个很好的建议,只是不能用android lib case.

但我想这三种情况没有单一的格式,所以必须一一尝试。例如对于 ISO8601 做这样的事情:

    return try { 
            val dateStr = "Sat Jan 25 20:06:07 +0000 2020". //ISO8601
            val format = SimpleDateFormat("EEE MMM dd HH:mm:ss z yyyy", Locale.getDefault())
            val dsipFormat = SimpleDateFormat("MMM dd yyyy", Locale.getDefault()) // for display result
            val date = format.parse(dateStr) // parse it to date
            dsipFormat.format(date) // returning the display result
            } catch (e: Exception) {
                Log.e("+++", "+++ error: $e")
                ""
            }

有没有更好的方法?

java.time 和 ThreeTenABP

我的解决方案是为三种可能的输入格式构建三个格式化程序,然后为每个输入依次尝试格式化程序。对于这个想法的简单演示:

    DateTimeFormatter[] inputFormatters = {
            DateTimeFormatter.ISO_INSTANT,
            DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss xx yyyy", Locale.ROOT),
            new DateTimeFormatterBuilder()
                    .appendValue(ChronoField.INSTANT_SECONDS)
                    .appendValue(ChronoField.MILLI_OF_SECOND, 3)
                    .toFormatter()
    };
    DateTimeFormatter displayFormatter
            = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)
                    .withLocale(Locale.US);

    for (String inputString : new String[] {
            "2020-01-25T21:59:27Z",
            "Sat Jan 25 20:06:07 +0000 2020",
            "1566777888999"
    }) {
        // try the formatters in turn and see which one works
        for (DateTimeFormatter formatter : inputFormatters) {
            try {
                ZonedDateTime dateTime = formatter.parse(inputString, Instant.FROM)
                        .atZone(ZoneId.systemDefault());
                System.out.format("%-30s was parsed to %s%n",
                        inputString, dateTime.format(displayFormatter));
                break;
            } catch (DateTimeParseException ignore) {
                // Ignore, try next format
            }
        }
    }

在我的时区 (Europe/Copenhagen) 此代码段的输出是:

2020-01-25T21:59:27Z           was parsed to Jan 25, 2020
Sat Jan 25 20:06:07 +0000 2020 was parsed to Jan 25, 2020
1566777888999                  was parsed to Aug 26, 2019

由于所有时区的日期都不同,因此输出会因时区而异。

我推荐 java.time,现代 Java 日期和时间 API。我看到您标记了问题 simpledateformat,但是 SimpleDateFormat class 是出了名的麻烦且早已过时,因此我建议不要使用它。我正在利用这样一个事实,即您的第一种格式是标准 ISO 8601,并且 java.time 有一个内置的格式化程序,DateTimeFormatter.ISO_INSTANT.

我的第三个输入格式化程序,用于长值的格式化程序,将最后三个字符视为秒的毫秒数,将其之前的所有字符视为自纪元以来的秒数。最终结果是它解析自纪元以来的毫秒数。构建此格式化程序需要 DateTimeFormatterBuilder

无库解决方案

我承认我讨厌写这个。我真的希望你能避免臭名昭著的麻烦 SimpleDateFormat class 及其早已过时的亲信,如 Date。因为我知道你的是一个无库应用程序,所以 Joda-Time 和 ThreeTenABP 似乎都不可能。对不起。在这种情况下,由于 SimpleDateFormat 无法解析 long,我的方法是尝试字符串以确定格式并根据它选择我的解析方式。

    DateFormat inputIso = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");
    // This format resembles the output from Date.toString
    DateFormat inputDatelike
            = new SimpleDateFormat("EEE MMM dd HH:mm:ss ZZZ yyyy", Locale.ROOT);
    DateFormat displayFormat
            = DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.US);
    displayFormat.setTimeZone(TimeZone.getDefault());

    for (String inputString : new String[] {
            "2020-01-25T21:59:27Z",
            "Sat Jan 25 20:06:07 +0000 2020",
            "1566777888999"
    }) {
        Date parsedDate;
        if (Character.isDigit(inputString.charAt(0))) {
            if (inputString.contains("-")) {
                parsedDate = inputIso.parse(inputString);
            } else {
                // long number of millis
                parsedDate = new Date(Long.parseLong(inputString));
            }
        } else {
            parsedDate = inputDatelike.parse(inputString);
        }
        System.out.format("%-30s was parsed to %s%n",
                inputString, displayFormat.format(parsedDate));
    }

输出与之前完全相同:

2020-01-25T21:59:27Z           was parsed to Jan 25, 2020
Sat Jan 25 20:06:07 +0000 2020 was parsed to Jan 25, 2020
1566777888999                  was parsed to Aug 26, 2019

请注意,此处的无效输入可能会导致 NumberFormatExceptionParseException,因此请注意两者。并且只有在无法避免时才采用此解决方案。

displayFormat.setTimeZone(TimeZone.getDefault());行在技术上是多余的,但它明确表示输出取决于时区,也许更重要的是,它告诉你如果你想输出在一个时区需要修改代码的地方不同的时区。

问题:java.time 不需要 Android API 26 级吗?

java.time 在新旧 Android 设备上都能很好地工作。它只需要至少 Java 6.

  • 在 Java 8 及更高版本和较新的 Android 设备上(从 API 级别 26),现代 API 是内置的。仅在这种情况下使用方法引用 Instant::from 而不是常量 Instant.FROM.
  • In-Android Java 6 和 7 获得 ThreeTen Backport,现代 classes 的 backport(ThreeTen 用于 JSR 310;请参阅底部的链接) .
  • 在(较旧的)Android 使用 ThreeTen Backport 的 Android 版本。它叫做 ThreeTenABP。并确保使用子包从 org.threeten.bp 导入日期和时间 classes。

链接