如何在Java 8中用阿拉伯-印度教数字解析字符串日期时间和时区?



我想解析字符串datetime&阿拉伯-印度数字时区,所以我写了一个这样的代码:

String dateTime = "٢٠٢١-١١-٠٨T٠٢:٢١:٠٨+٠٢:٠٠";
char zeroDigit = '٠';
Locale locale = Locale.forLanguageTag("ar");
DateTimeFormatter pattern = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX")
.withLocale(locale)
.withDecimalStyle(DecimalStyle.of(locale).withZeroDigit(zeroDigit));
ZonedDateTime parsedDateTime = ZonedDateTime.parse(dateTime, pattern);
assert parsedDateTime != null;

但我收到了一个例外:

java.time.format.DateTimeParseException:无法在索引19 中分析文本"٢٠">

我检查了很多关于Stackoverflow的问题,但我仍然不明白我做错了什么。

当时区不使用阿拉伯-印度数字时,它可以与dateTime = "٢٠٢١-١١-٠٨T٠٢:٢١:٠٨+02:00"配合使用。

您的dateTime字符串是错误的,被误解了。它显然试图遵守ISO8601格式,但失败了。因为ISO 8601格式使用US-ASCII数字。

如果只有数字符合ISO8601,java.time的类(InstantOffsetDateTimeZonedDateTime(将在没有任何格式化程序的情况下解析字符串。在绝大多数情况下,我会采取你的方式:尝试按原样解析字符串。在这种情况下不会。对我来说,在解析之前更正字符串更有意义。

String dateTime = "٢٠٢١-١١-٠٨T٠٢:٢١:٠٨+٠٢:٠٠";
char[] dateTimeChars = dateTime.toCharArray();
for (int index = 0; index < dateTimeChars.length; index++) {
if (Character.isDigit(dateTimeChars[index])) {
int digitValue = Character.getNumericValue(dateTimeChars[index]);
dateTimeChars[index] = Character.forDigit(digitValue, 10);
}
}

OffsetDateTime odt = OffsetDateTime.parse(CharBuffer.wrap(dateTimeChars));

System.out.println(odt);

输出:

2021-11-08T02:21:08+02:00

编辑:当然,如果你能教育字符串的发布者使用US-ASCII数字,那就更好了。

编辑:我知道我链接到下面的维基百科文章说:

表示必须用阿拉伯数字组合书写以及特定的计算机字符(例如"-"、":"、"T"、"W"、"Z"(在标准中被赋予特定含义的…

这是造成混乱的一个原因。链接到的文章阿拉伯数字说:

阿拉伯数字是十位数字:0、1、2、3、4、5、6、7、8和9。

编辑:如何转换每个数字:Character.getNumericValue()从代表一个数字的char转换为等于该数字代表的数字的int,因此'٠'转换为0,'٢'转换为2,等等。它适用于所有数字字符(不仅仅是阿拉伯语和ASCII字符(。Character.forDigit()执行某种相反的转换,只总是到US ASCII,所以0到'0',2到'2',等等。

编辑:感谢@Holger在本文中引起我对CharBuffer的关注。CharBuffer实现了CharSequence,这是java.time的parse方法所需要的类型,因此避免了我们将char数组转换回String

链接

  • 维基百科文章:ISO 8601
  • 维基百科文章:阿拉伯数字

错误消息指出问题出现在输入字符串的索引19处。

字符19是输入字符串中的+字符。这意味着偏移量(在模式中由XXX表示(无法解析。

问题不在于+本身。问题是,像+05:00这样的时区偏移永远不会本地化。

文档中没有提到这一点,所以我不得不去DateTimeFormatterBuilder的源代码进行验证

在这个类里面是这个内部类:

static final class OffsetIdPrinterParser implements DateTimePrinterParser {

在该类中,我们可以找到一个对私有parseHour、parseMinute和parseSeconds方法进行调用的解析方法。

这些方法中的每一个都委托给一个私有的parseDigits方法。在这种方法中,我们可以看到只考虑ASCII数字:

char ch1 = parseText.charAt(pos++);
char ch2 = parseText.charAt(pos++);
if (ch1 < '0' || ch1 > '9' || ch2 < '0' || ch2 > '9') {
return false;
}

因此,这里的答案是,时区偏移量必须由ASCII数字组成,而不考虑区域设置。

最新更新