我一直试图用昨天的JodaTime来解析一个简单的约会,到目前为止我一直在失败......
这是我正在尝试解析的(随机)日期: 2017年9月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年9月14日时,我希望得到的输出只是2017-09-14
答案 0 :(得分:1)
Joda-Time 并不提供简单的方法。唯一(复杂)的方法是定义自己的DateTimeParser实现。您可能会从我那里找到另一个old 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,您可以用Time4A替换Time4J,并用实现ChronoCondition
- 接口的匿名类替换给定的lambda表达式。
顺便说一句,每个库都可以使用固定月份名称的查找表。对于Joda,请参阅我上面提到的旧帖子作为链接。对于Java-8或三重后端,请参阅Hugo的答案。对于SimpleDateFormat
,请参阅如何设置DateFormatSymbols
的合适实例。对于Time4J,请参阅builder-API(类似于Java-8)。
关于SimpleDateFormat
的最后一句话。即使它现在似乎对你有用(只是因为宽松的解析适用于旧的Java和Time4J,但有趣的是不适用于java.time
- API),我仍然不会相信它在任何情况下,这个旧的解析器类也不是线程安全的。
答案 1 :(得分:0)
如果您想保留模式yyyy-MM-dd
,则需要一个两位数的月份(09)。否则,请将您的模式更改为yyyy-MMMM-dd
。
答案 2 :(得分: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();
}
答案 3 :(得分:0)
Joda-Time以短格式接受月份名称(在大多数语言中,通常使用3个字母)或长格式(使用全名)。您的输入似乎是英文和4个字母,但不支持。
如果可以操作输入,您可以删除多余的字符,并确保月份名称只包含3个字母。
我还使用java.util.Locale
指定月份名称是英文。如果您没有指定区域设置,它将使用系统默认值,并且不保证始终为英语,因此最好指定一个。
我还将其解析为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年9月14日
我假设语言环境是英语,但在爱沙尼亚语中,9月的短月名称是“9月”,所以你也可以这样做:
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
与法语区域设置一起使用的评论,因此上面的代码也可能有效。)
Joda-Time处于维护模式,正在被新的API取代,因此我不建议使用它来启动新项目。即使在joda's website中它也说:“请注意,Joda-Time被认为是一个很大程度上”完成“的项目。没有计划大的增强。如果使用Java SE 8,请迁移到java.time(JSR) -310)。“即可。
如果您不能(或不想)从Joda-Time迁移到新API,则可以忽略此部分。
在Android中,您可以使用ThreeTen Backport,这是Java 8新日期/时间类的绝佳后端。为了使其有效,您还需要ThreeTenABP(更多关于如何使用它here)。
您可以创建格式化程序,设置区域设置并将其解析为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年9月14日
或者只是尝试使用系统的默认语言环境(只需调用toFormatter()
不带参数,它将使用系统默认值。)
或者,您可以创建自定义月份名称的地图,并在格式化程序中使用它。唯一的细节是你必须用所有月份的值填充它。我在9月份放了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));