【问题标题】:Java time parse "Jun 26th 2021, 04:30:15 pm NY"Java 时间解析“2021 年 6 月 26 日,纽约时间下午 4:30:15”
【发布时间】:2021-09-09 10:19:13
【问题描述】:

我有一个如下所示的字符串:

String str = "Jun 26th 2021, 04:30:15 pm NY";

我想将其转换为ZonedDateTime,为此我使用DateTimeFormatterBuilder

DateTimeFormatter formatter = new DateTimeFormatterBuilder()
        .parseCaseInsensitive()
        .appendPattern("MMM dd'th' uuuu, h:mm:ss a z")
        .toFormatter(Locale.US);
ZonedDateTime result = ZonedDateTime.parse(str, formatter);

但是解析器对这种格式不满意我收到了这个错误:

java.time.format.DateTimeParseException: Text 'Jun 26th 2021, 04:30:15 pm NY' could not be parsed at index 27

NY 似乎没有被z 覆盖,请对此错误有任何想法,还有什么技巧可以在解析器中避免'th'

【问题讨论】:

  • 嗯,纽约不是公认的时区。 Java 不知道 NY 是什么意思。除了 NY,还有哪些其他自定义时区名称?
  • 查看this answer 了解如何处理th 部分。

标签: java date-format java-time zoneddatetime


【解决方案1】:

DateTimeFormatter#parse(CharSequence, ParsePosition) 随时为您服务。

请注意,NY 不是时区的名称。时区的命名约定是 Region/City 例如Europe/Paris。您可以使用ZoneId#getAvailableZoneIds 获取时区名称列表。

此外,对于带有序数的日期,例如26日,你可以建立一个Map,如下代码所示。

演示:

import java.text.ParsePosition;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

public class Main {
    public static void main(String[] args) {
        String strDateTime ="Jun 26th 2021, 04:30:15 pm NY";
    
        DateTimeFormatter dtf = new DateTimeFormatterBuilder()
                .parseCaseInsensitive()
                .appendPattern("[MMMM][MMM] ") // caters for both full name and 3-letter abbv.
                .appendText(ChronoField.DAY_OF_MONTH, ordinalMap())
                .appendPattern(" u, h:m:s a")
                .toFormatter(Locale.ENGLISH);

        LocalDateTime ldt = LocalDateTime.from(dtf.parse(strDateTime, new ParsePosition(0)));
        ZonedDateTime zdt = ldt.atZone(ZoneId.of("America/New_York"));
        System.out.println(zdt);
    }
    static Map<Long, String> ordinalMap() {
        String[] suffix = { "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th" };
        Map<Long, String> map = new HashMap<>();
        
        for (int i = 1; i <= 31; i++) 
            map.put((long)i, String.valueOf(i) + suffix[(i > 3 && i < 21) ? 0 : (i % 10)]);
        
        return map;
    }
}

输出:

2021-06-26T16:30:15-04:00[America/New_York]

ONLINE DEMO

Trail: Date Time 了解有关现代日期时间 API 的更多信息。

礼貌:构建Map 的逻辑基于this excellent answer

【讨论】:

  • 还值得注意的是,将来当 OP 确实需要处理更多时区时,他们可以读取解析结束的位置,从该索引中获取子字符串,并在 Map&lt;String, ZoneId&gt; 中查找.
【解决方案2】:

正如 Sweeper 所强调的,纽约不是 Java 认可的时区。为了尽量减少对代码的更改,您可以使用 DateTimeFormatter 格式化程序的 DateTimeFormatter#withZone 方法返回具有新覆盖区域的格式化程序副本,如下所示:

String str = "Jun 26th 2021, 04:30:15 pm"; //<-- erased NY
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
        .parseCaseInsensitive()
        .appendPattern("MMM dd'th' uuuu, h:mm:ss a")
        .toFormatter(Locale.US);
ZonedDateTime result = ZonedDateTime.parse(str, formatter.withZone(ZoneId.of("America/New_York")));
System.out.println(result); //<-- 2021-06-26T16:30:15-04:00[America/New_York]

【讨论】:

  • 也可以使用.toFormatter().withZone(ZoneId.of("America/New_York")).withLocale(Locale.US);
  • @Pekka 你是对的,我这样写只是为了把你的代码修改到最后。
  • @ArvindKumarAvinash,我赞成您解决 th 问题的模式,我看到您使用 Locale.English 而不是 Locale.US 。英语不是我的第一语言,这个选择有什么特别的原因吗?
  • @dariosicily - 谢谢。 Locale.ENGLISH 仅指定日期时间字符串所在的语言。如您所知,Locale 有国家和语言。当我们只处理英语时,Locale.ENGLISHLocale.ROOT 就足够了。更多示例请查看Never use SimpleDateFormat or DateTimeFormatter without a Locale
  • @ArvindKumarAvinash 感谢您的解释和您的时间。
猜你喜欢
  • 2018-01-24
  • 1970-01-01
  • 1970-01-01
  • 2020-07-28
  • 2021-09-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多