1

从昨天开始,我一直在尝试使用 JodaTime 解析一个简单的日期,但到目前为止我一直在失败.....

这是我要解析的(随机)日期:2017-Sept-14(即使大写的 S 也不会改变任何东西......)

这是我的代码

 DateTimeFormatter dateTimeFormat = DateTimeFormat.forPattern("yyyy-MM-dd");
 // The variable 'parsed' is a dynamic string. For now I set it to 2017-sept-14 
 DateTime dateTime = dateTimeFormat.parseDateTime(parsed);
 Log.d(TAG, "Parsed date = "+ dateTime.toString());

这是我的例外:

java.lang.IllegalArgumentException: Invalid format: "2017-sept-14" is malformed at "sept-14" at org.joda.time.format.DateTimeFormatter.parseDateTime(DateTimeFormatter.java:945)

我在这里想念什么?

更新: 实际上我从我的文本字段中得到的是上面的形式,即日期-月-日(月份长 3 或 4 个字符,具体取决于月份....)所以当我有 2017 年时我想得到什么作为输出-sept-14 只是 2017-09-14

4

4 回答 4

1

Joda-Time 接受短格式(在大多数语言中,通常为 3 个字母)或长格式(全名)的月份名称。您的输入似乎是英文且包含 4 个字母,这是不受支持的。

如果可以操纵输入,您可以删除多余的字符并确保月份名称仅包含 3 个字母。

我还使用 ajava.util.Locale来指定月份名称为英文。如果不指定区域设置,则使用系统默认设置,并且不保证始终为英语,因此最好指定一个。

我还将它解析为 a LocalDate,因为它的toString()方法已经产生了你想要的输出:

String input = "2017-Sept-14";
input = input.replace("Sept", "Sep");
DateTimeFormatter dateTimeFormat = DateTimeFormat.forPattern("yyyy-MMM-dd").withLocale(Locale.ENGLISH);
LocalDate dateTime = dateTimeFormat.parseLocalDate(input);
System.out.println(dateTime);

输出是:

2017-09-14

我假设语言环境是英语,但在爱沙尼亚语言环境中,九月的短月份名称是“sept”,所以你也可以这样做:

String input = "2017-Sept-14";
input = input.toLowerCase(); // et_EE locale accepts only "sept"
DateTimeFormatter dateTimeFormat = DateTimeFormat.forPattern("yyyy-MMM-dd")
    .withLocale(new Locale("et", "EE"));
LocalDate dateTime = dateTimeFormat.parseLocalDate(input);
System.out.println(dateTime);

或者您可以尝试使用系统的默认设置(根据您SimpleDateFormat在法语语言环境下的注释,因此上面的代码也有可能也可以工作)。


Java 新的日期/时间 API

Joda-Time 处于维护模式,正在被新的 API 取代,所以我不建议用它开始一个新项目。即使在joda 的网站上,它也说:“请注意,Joda-Time 被认为是一个基本上‘完成’的项目。没有计划进行重大改进。如果使用 Java SE 8,请迁移到 java.time (JSR-310)。” .

如果您不能(或不想)从 Joda-Time 迁移到新 API,则可以忽略此部分。

在 Android 中,您可以使用ThreeTen Backport,它是 Java 8 新日期/时间类的一个很好的后向端口。要使其工作,您还需要ThreeTenABP(更多关于如何使用它的信息)。

您可以创建格式化程序,设置语言环境并将其解析为LocalDate

import org.threeten.bp.LocalDate;
import org.threeten.bp.format.DateTimeFormatter;
import org.threeten.bp.format.DateTimeFormatterBuilder;

DateTimeFormatter f = new DateTimeFormatterBuilder()
    // case insensitive (so it accepts Sept, sept, and so on)
    .parseCaseInsensitive()
    // pattern
    .appendPattern("yyyy-MMM-dd")
    // set locale
    .toFormatter(new Locale("et", "EE"));
System.out.println(LocalDate.parse("2017-Sept-14", f));

输出是:

2017-09-14

或者只是尝试使用系统的默认语言环境(只需toFormatter()不带参数调用,它将使用系统默认设置)。


或者,您可以创建自定义月份名称的地图并在格式化程序中使用它。唯一的细节是你必须用所有月份的值来填充它。我输入Sept了九月,您可以相应地填写其他月份:

// map of custom names for month
Map<Long, String> monthNames = new HashMap<>();
// put the names used in your input
monthNames.put(1L, "Jan");
monthNames.put(2L, "Feb");
monthNames.put(3L, "Mar");
monthNames.put(4L, "Apr");
monthNames.put(5L, "May");
monthNames.put(6L, "Jun");
monthNames.put(7L, "Jul");
monthNames.put(8L, "Aug");
monthNames.put(9L, "Sept");
monthNames.put(10L, "Oct");
monthNames.put(11L, "Nov");
monthNames.put(12L, "Dec");

DateTimeFormatter fmt = new DateTimeFormatterBuilder()
    // case insensitive (so it accepts Sept, sept, and so on)
    .parseCaseInsensitive()
    // year
    .appendPattern("yyyy-")
    // month, using custom names
    .appendText(ChronoField.MONTH_OF_YEAR, monthNames)
    // day
    .appendPattern("-dd")
    // create formatter
    .toFormatter();

String input = "2017-Sept-14";
System.out.println(LocalDate.parse(input, fmt));
于 2017-09-14T15:47:09.910 回答
1

Joda-Time没有提供简单的方法。唯一(复杂)的方法是定义您自己的DateTimeParser实现。您可能会在我的另一篇旧 SO-post中找到一些灵感,然后执行以下操作:

DateTimeParser monthParser = ...; // left as exercise to you

DateTimeFormatter joda = 
  new DateTimeFormatterBuilder()
  .appendPattern("yyyy-")
  .append(monthParser)
  .append("-dd")
  .toFormatter();
LocalDate ld = joda.parseLocalDate("2017-sept-14");

我不确定为什么 Hugo 建议基于地图查找自定义名称的答案对您不起作用。也许月份名称是可变的而不是固定的(对于同一个月)。也许您只是想检查月份名称是否以相同的前缀开头。如果是这样,那么我的 lib Time4J可能会对您有所帮助,因为它管理更多的格式化/解析属性来控制解析,请参见以下简短示例(如果存在,它还管理其他尾随点):

String s = "2017-sept.-14";
ChronoFormatter<PlainDate> f =
  ChronoFormatter
    .setUp(PlainDate.class, new java.util.Locale("fr", "FR"))
    .addPattern("uuuu-MMM", PatternType.CLDR)
    .skipUnknown(c -> c == '.', 1)
    .addPattern("-dd", PatternType.CLDR)
    .build()
    .with(net.time4j.format.Attributes.PARSE_CASE_INSENSITIVE, true)
    .with(net.time4j.format.Attributes.PARSE_PARTIAL_COMPARE, true);
System.out.println(f.parse(s)); // 2017-09-14

对于 Android,您可以将 Time4J 替换为 Time4A,并将给定的 lambda 表达式替换为实现 -interface 的匿名类ChronoCondition

顺便说一句,每个库都可以查找固定月份名称的查找表。对于 Joda,请参阅上面提到的我较早的 SO-post 作为链接。对于 Java-8 或 threeten-backport,请参阅 Hugo 的答案。查看SimpleDateFormat如何设置合适的DateFormatSymbols. 对于 Time4J,请参阅builder-API(类似于 Java-8)。

最后一句话SimpleDateFormat。即使它现在似乎对你有用(只是因为宽松的解析适用于旧的 Java 和 Time4J,但有趣的是不适用于 -API java.time),我仍然不会在任何情况下都相信它,而且这个旧的解析器类不是线程安全的, 也。

于 2017-09-14T17:31:30.913 回答
0

所以我不知道这是否是正确的方法,但这对我有用(我最终使用了 SimpleDateFormat ...):

SimpleDateFormat sdfSource = new SimpleDateFormat("yyyy-MMM-dd");
SimpleDateFormat sdfDestination = new SimpleDateFormat("yyyy-MM-dd");
try {
      Date date = sdfSource.parse(parsed);
      String strOutput = sdfDestination.format(date);
      Log.d(TAG, "Parsed date = "+ strOutput);
    } catch (ParseException e) {
        e.printStackTrace();
    }
于 2017-09-14T14:49:54.323 回答
0

如果要保留该模式,则需要一个两位数的月份 (09) yyyy-MM-dd。否则将您的模式更改为yyyy-MMMM-dd.

于 2017-09-14T13:12:26.733 回答