更漂亮的Multiple DateTimeFormatter?

问题描述

我有多个字符串日期要转换为 OffsetDateTime 并且我通过多次尝试和捕获来做到这一点,我想我不会有其他 DateTimeFormatter 要写。那么,如何让它更漂亮?

代码

public static OffsetDateTime convertStringDatetoOffsetDate(String dateStr){
        DateTimeFormatter f = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX").withLocale( Locale.US );
        DateTimeFormatter f2 = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss").withZone(ZoneId.of("Europe/Paris"));
        DateTimeFormatter f3 = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX").withZone(ZoneId.of("Europe/Paris"));
        DateTimeFormatter f4 = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSS").withZone(ZoneId.of("Europe/Paris"));
        DateTimeFormatter f5 = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS").withZone(ZoneId.of("Europe/Paris"));
        DateTimeFormatter f6 = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSSXXX").withZone(ZoneId.of("Europe/Paris"));
        OffsetDateTime myDate = null;
        try{
            myDate = zoneddatetime.parse(dateStr,f).toOffsetDateTime();
        } catch(DateTimeParseException e){
            try{
                myDate = zoneddatetime.parse(dateStr,f2).toOffsetDateTime();
            } catch (DateTimeParseException ex) {
                try{
                    myDate = zoneddatetime.parse(dateStr,f3).toOffsetDateTime();
                } catch (DateTimeParseException exc) {
                    try{
                        myDate = zoneddatetime.parse(dateStr,f4).toOffsetDateTime();
                    }  catch (DateTimeParseException exce) {
                        try{
                            myDate = zoneddatetime.parse(dateStr,f5).toOffsetDateTime();
                        } catch(DateTimeParseException excep){
                            myDate = zoneddatetime.parse(dateStr,f6).toOffsetDateTime();
                        }
                    }
                }
            }
        }

        return myDate;
    }

解决方法

public static OffsetDateTime convertStringDateToOffsetDate(String dateStr){
    DateTimeFormatter f = DateTimeFormatter.ofPattern("yyyy-MM-dd['T'][ ][HH:mm:ss][.][SSSSSS][SSSSS][SSSS][SSS][XXX][XX][X]").withZone(ZoneId.of("Europe/Paris"));
    return ZonedDateTime.parse(dateStr,f).toOffsetDateTime();
}

这应该可以处理您的所有模式。不需要多个格式化程序或正则表达式。

,

您可以使用 [] 语法将部分格式字符串声明为可选。这可能只是让您找到一个解决所有问题的单一模式。但是,在这种设置中,一种模式具有美国语言环境而其他模式没有,该部分将不适合单个格式字符串。因此,您可以减少您拥有的格式字符串的数量,但可能不会减少到一个。

然后,使用一个列表和一个辅助方法,来实现干净的代码:

private static final List<DateTimeFormatter> FORMATS = List.of(
    DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX").withLocale( Locale.US ),DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss[.][SSSSSS][XXX]").withZone(ZoneId.of("Europe/Paris")));

public static OffsetDateTime parse(String dateStr) throws DateTimeParseException {
  DateTimeParseException ex = null;

  for (var format : FORMATS) try {
      return ZonedDateTime.parse(dateStr,format).toOffsetDateTime();
  } catch (DateTimeParseException e) {
    ex = e;
  }
  throw ex;
}
,

这是我的尝试。

private static final DateTimeFormatter PARSER = new DateTimeFormatterBuilder()
        .append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
        .optionalStart()
        .appendOffsetId()
        .optionalEnd()
        .toFormatter(Locale.ROOT);

private static final ZoneId DEFAULT_ZONE = ZoneId.of("Europe/Paris");

public static OffsetDateTime convertStringDateToOffsetDate(String dateStr) {
    TemporalAccessor parsed
            = PARSER.parseBest(dateStr,OffsetDateTime::from,LocalDateTime::from);
    if (parsed instanceof OffsetDateTime) {
        return (OffsetDateTime) parsed;
    } else {
        return ((LocalDateTime) parsed).atZone(DEFAULT_ZONE).toOffsetDateTime();
    }
}

尝试一下:

    String[] testStrings = {
            "2021-01-01T12:34:56.789-07:00","2021-02-01T12:34:56","2021-03-01T12:34:56-06:00","2021-04-01T12:34:56.987654","2021-05-01T12:34:56.789","2021-06-01T12:34:56.987654-05:00"
    };
    
    for (String testString : testStrings) {
        System.out.format("%-32s -> %s%n",testString,convertStringDateToOffsetDate(testString));
    }

输出:

2021-01-01T12:34:56.789-07:00    -> 2021-01-01T12:34:56.789-07:00
2021-02-01T12:34:56              -> 2021-02-01T12:34:56+01:00
2021-03-01T12:34:56-06:00        -> 2021-03-01T12:34:56-06:00
2021-04-01T12:34:56.987654       -> 2021-04-01T12:34:56.987654+02:00
2021-05-01T12:34:56.789          -> 2021-05-01T12:34:56.789+02:00
2021-06-01T12:34:56.987654-05:00 -> 2021-06-01T12:34:56.987654-05:00

你注意到:

  • 它可以处理您问题中的所有 6 种格式。
  • 对于其中具有 UTC 偏移量的字符串,将保留偏移量。对于没有的字符串,假定巴黎的正确偏移量(2 月 +01:00,4 月和 5 月 +02:00)。

我认为它有以下优点:

  • 我只需要一个格式化程序。
  • 我根本没有编写任何格式模式字符串,只是从内置部件组装了我的格式化程序。

我用来解析的 DateTimeFormatter.parseBest 方法将首先尝试创建一个 OffsetDateTime,如果不成功,它将求助于创建并返回一个 LocalDateTime。在后一种情况下,我需要转换它。我的解决方案的缺点是我需要通过 TemporalAccessor,这是一个我认为是低级的接口,我们通常不应该在应用程序代码中使用。

内置的 DateTimeFOrmatter.ISO_LOCAL_DATE_TIME 已经处理了秒数最多 9 位小数的存在和不存在。因此,通过在我的格式化程序中重用它,我已经处理了没有小数和 3 位和 6 位小数的情况。

对没有偏移量的字符串使用欧洲/巴黎时区的要求的一个挑战是,虽然 DateTimeFormatter 可以有许多默认值,但它不能有默认时区。 withZone 方法为我们提供了一个带有覆盖区域的格式化程序,但这是另外一回事。该格式化程序将根据格式化或解析的结果强制覆盖区域。虽然从你的问题中并不清楚,但我假设你不想要这个。

编辑:格式化程序需要语言环境吗?我使用 .toFormatter(Locale.ROOT) 从构建器构建格式化程序。从技术上讲,在这种情况下不需要语言环境,因为我的格式化程序不包含任何依赖语言环境的部分,并且在此答案的第一个版本中,我已将语言环境排除在外(调用 no-arg toFormatter 方法反而)。不过,我倾向于同意 Arvind Kumar Avinash 的评论:

只是小小的吹毛求疵:请始终使用带有日期时间的 Locale 解析/格式化类型……因为它是一个对 Locale 敏感的类型。它 可能与这里处理的日期时间字符串无关 解决方案,但我们应该坚持下去,就好像这是一条规则。

这可能只是我自大并假设读者能够确定格式化程序中没有对区域设置敏感的部分。提供语言环境是更好的习惯(否则至少在评论中说明为什么没有)。

,

您可以在循环中使用单个 try-catch,其中异常将被忽略。

import 'dart:convert';

AddContactModel addContactModelFromJson(String str) => AddContactModel.fromJson(json.decode(str));

String addContactModelToJson(AddContactModel data) => json.encode(data.toJson());

class AddContactModel {
  AddContactModel({
    this.contact,});

  Contact contact;

  factory AddContactModel.fromJson(Map<String,dynamic> json) => AddContactModel(
    contact: Contact.fromJson(json["contact"]),);

  Map<String,dynamic> toJson() => {
    "contact": contact.toJson(),};
}

class Contact {
  Contact({
    this.isPublished,this.dateAdded,this.dateModified,this.createdBy,this.createdByUser,this.modifiedBy,this.modifiedByUser,this.id,this.points,this.color,this.fields,this.lastActive,this.owner,this.ipAddresses,this.tags,this.utmtags,this.stage,this.dateIdentified,this.preferredProfileImage,this.doNotContact,this.frequencyRules,});

  bool isPublished;
  DateTime dateAdded;
  dynamic dateModified;
  int createdBy;
  String createdByUser;
  dynamic modifiedBy;
  dynamic modifiedByUser;
  int id;
  int points;
  dynamic color;
  Fields fields;
  dynamic lastActive;
  dynamic owner;
  List<dynamic> ipAddresses;
  List<dynamic> tags;
  dynamic utmtags;
  dynamic stage;
  dynamic dateIdentified;
  dynamic preferredProfileImage;
  List<dynamic> doNotContact;
  List<dynamic> frequencyRules;

  factory Contact.fromJson(Map<String,dynamic> json) => Contact(
    isPublished: json["isPublished"],dateAdded: DateTime.parse(json["dateAdded"]),dateModified: json["dateModified"],createdBy: json["createdBy"],createdByUser: json["createdByUser"],modifiedBy: json["modifiedBy"],modifiedByUser: json["modifiedByUser"],id: json["id"],points: json["points"],color: json["color"],fields: Fields.fromJson(json["fields"]),lastActive: json["lastActive"],owner: json["owner"],ipAddresses: List<dynamic>.from(json["ipAddresses"].map((x) => x)),tags: List<dynamic>.from(json["tags"].map((x) => x)),utmtags: json["utmtags"],stage: json["stage"],dateIdentified: json["dateIdentified"],preferredProfileImage: json["preferredProfileImage"],doNotContact: List<dynamic>.from(json["doNotContact"].map((x) => x)),frequencyRules: List<dynamic>.from(json["frequencyRules"].map((x) => x)),dynamic> toJson() => {
    "isPublished": isPublished,"dateAdded": dateAdded.toIso8601String(),"dateModified": dateModified,"createdBy": createdBy,"createdByUser": createdByUser,"modifiedBy": modifiedBy,"modifiedByUser": modifiedByUser,"id": id,"points": points,"color": color,"fields": fields.toJson(),"lastActive": lastActive,"owner": owner,"ipAddresses": List<dynamic>.from(ipAddresses.map((x) => x)),"tags": List<dynamic>.from(tags.map((x) => x)),"utmtags": utmtags,"stage": stage,"dateIdentified": dateIdentified,"preferredProfileImage": preferredProfileImage,"doNotContact": List<dynamic>.from(doNotContact.map((x) => x)),"frequencyRules": List<dynamic>.from(frequencyRules.map((x) => x)),};
}

class Fields {
  Fields({
    this.core,this.social,this.personal,this.professional,this.all,});

  All core;
  Social social;
  List<dynamic> personal;
  List<dynamic> professional;
  All all;

//Not the full code

但请记住,异常的性能很差(将堆栈跟踪写入变量需要时间),所以也许杜德克先生对使用正则表达式的评论可能是更好的答案。