编辑:
我打开了一个错误,并已被Oracle确认。您可以在此处遵循分辨率:https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8216414
我正在与LDAP存储库连接,该存储库存储具有以下时间和时区的人的生日:
我找不到一种使用相同的模式解析和格式化出生日期的方法。
以下代码适合格式化,但不适用于解析:
LocalDate date = LocalDate.of(2018, 12, 27);
String pattern = "yyyyMMdd'000000+0000'";
DateTimeFormatter birthdateFormat = DateTimeFormatter.ofPattern(pattern);
// Outputs correctly 20181227000000+0000
date.format(birthdateFormat);
// Throw a DatetimeParseException at index 0
date = LocalDate.parse("20181227000000+0000", birthdateFormat);
以下代码可很好地用于解析,但不能用于格式
LocalDate date = LocalDate.of(2018, 12, 27);
String pattern = "yyyyMMddkkmmssxx";
DateTimeFormatter birthdateFormat = DateTimeFormatter.ofPattern(pattern);
// Throws a UnsupportedTemporalTypeException for ClockHourOfDay not supported
// Anyway I would have an unwanted string with non zero hour, minute, second, timezone
date.format(birthdateFormat);
// Parse correctly the date to 27-12-2018
date = LocalDate.parse("20181227000000+0000", birthdateFormat);
哪种模式既可以满足解析又可以满足格式要求?
我被迫使用2种不同的模式吗?
我问是因为模式是在属性文件中配置的。我只想在此属性文件中配置1个模式。我想外部化该模式,因为LDAP不是我的项目的一部分,它是共享资源,并且我不能保证格式不能更改。
答案 0 :(得分:2)
由于您的LDAP字符串具有区域格式...+0000
,因此建议您使用ZonedDateTime
或OffsetDateTime
。
此模式yyyyMMddHHmmssZZZ
可以解析和格式化。
LocalDate date = LocalDate.of(2018, 12, 27);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmssZZZ");
格式化
首先将您的LocalDate
转换为ZonedDateTime
/ OffsetDateTime
:
ZonedDateTime zonedDateTime = date.atStartOfDay(ZoneOffset.UTC);
// or
OffsetDateTime offsetDateTime = date.atStartOfDay().atOffset(ZoneOffset.UTC);
然后将其格式化:
// Both output correctly 20181227000000+0000
System.out.println(zonedDateTime.format(formatter));
// or
System.out.println(offsetDateTime.format(formatter));
解析
首先解析ZonedDateTime
/ OffsetDateTime
:
// Both parse correctly
ZonedDateTime zonedDateTime = ZonedDateTime.parse("20181227000000+0000", formatter);
// or
OffsetDateTime offsetDateTime = OffsetDateTime.parse("20181227000000+0000", formatter);
拥有ZonedDateTime
/ OffsetDateTime
后,您可以像这样简单地检索LocalDate
:
LocalDate date = LocalDate.from(zonedDateTime);
// or
LocalDate date = LocalDate.from(offsetDateTime);
更新
解析和格式都可以简化为单行代码:
LocalDate date = LocalDate.from(formatter.parse(ldapString));
String ldapString = OffsetDateTime.of(date, LocalTime.MIN, ZoneOffset.UTC).format(formatter);
如果您仍然对上面的代码不满意,则可以将逻辑提取到实用程序方法中:
public LocalDate parseLocalDate(String ldapString) {
return LocalDate.from(formatter.parse(ldapString));
}
public String formatLocalDate(LocalDate date) {
return OffsetDateTime.of(date, LocalTime.MIN, ZoneOffset.UTC)
.format(formatter);
}
答案 1 :(得分:1)
愚蠢的简单解决方案:
String s1 = "20181227000000+0000";
DateTimeFormatter yyyyMMdd = DateTimeFormatter.ofPattern("yyyyMMdd");
LocalDate date = LocalDate.parse(s1.substring(0, 8), yyyyMMdd);
System.out.println("date = " + date);
String s2 = date.format(yyyyMMdd) + "000000+0000";
System.out.println("s2 = " + s2);
System.out.println(s1.equals(s2));
答案 2 :(得分:1)
我建议:
LocalDate date = LocalDate.of(2018, Month.DECEMBER, 27);
String pattern = "yyyyMMddHHmmssxx";
DateTimeFormatter birthdateFormat = DateTimeFormatter.ofPattern(pattern);
// Outputs 20181227000000+0000
String formatted = date.atStartOfDay(ZoneOffset.UTC).format(birthdateFormat);
System.out.println(formatted);
// Parses to 2018-12-27T00:00Z
OffsetDateTime odt = OffsetDateTime.parse("20181227000000+0000", birthdateFormat);
System.out.println(odt);
// Validate
if (! odt.toLocalTime().equals(LocalTime.MIN)) {
System.out.println("Unexpected time of day: " + odt);
}
if (! odt.getOffset().equals(ZoneOffset.UTC)) {
System.out.println("Unexpected time zone offset: " + odt);
}
// Converts to 2018-12-27
date = odt.toLocalDate();
System.out.println(date);
LDAP字符串代表日期,时间和UTC偏移量。好的解决方案是尊重这一点,并在格式化(将一天中的时间设置为00:00并将偏移量设置为0)并解析回所有内容时生成所有这些内容(最多也可以验证它们,以防出现任何意外情况)。如果您知道怎么做,LocalDate
和OffsetDateTime
之间的转换就很简单。
编辑3:允许配置模式
...模式已在属性文件中配置...我要配置1 模式仅在此属性文件中。
...我不能保证格式不能更改。
要考虑到模式有一天可能不包含一天中的时间和/或没有UTC偏移量的可能性,请在上面的代码中使用以下格式化程序:
DateTimeFormatter birthdateFormat = new DateTimeFormatterBuilder()
.appendPattern(pattern)
.parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
.toFormatter()
.withZone(ZoneOffset.UTC);
这定义默认的一天中的时间(午夜)和默认的偏移量(0)。只要在LDAP字符串中定义了时间和偏移量,就不会使用默认值。
如果您觉得它太复杂了,那么使用两种配置的格式(一种用于格式化和一种用于解析)可能是您的最佳解决方案(最不烦人的解决方案)。
修改:避免类型转换
我认为以上是不错的解决方案。但是,如果您坚持要避免使用LocalDate
从ZonedDateTime
到atStartOfDay
以及使用OffsetDateTime
从toLocalDate
到 DateTimeFormatter birthdateFormat = new DateTimeFormatterBuilder()
.appendValue(ChronoField.YEAR, 4, 4, SignStyle.NEVER)
.appendValue(ChronoField.MONTH_OF_YEAR, 2, 2, SignStyle.NEVER)
.appendValue(ChronoField.DAY_OF_MONTH, 2, 2, SignStyle.NEVER)
.appendLiteral("000000+0000")
.toFormatter();
// Outputs 20181227000000+0000
String formatted = date.format(birthdateFormat);
System.out.println(formatted);
// Parses into 2018-12-27
date = LocalDate.parse("20181227000000+0000", birthdateFormat);
System.out.println(date);
的转换,则可以通过以下技巧实现: / p>
yyyyMMdd'000000+0000'
我正在指定每个字段的确切宽度,以便格式化程序在解析时可以知道在字符串中将其分隔的位置。
编辑2:这是解析错误吗?
我会立即期望# Database
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'mydb',
'USER': 'myuser',
'PASSWORD': 'mypassword',
'HOST': 'localhost',
'PORT': '',
'OPTIONS': {
'sslmode': 'disable'
}
}
}
可以同时进行格式化和解析。您可以尝试向Oracle提交错误,然后看看他们怎么说,尽管我不太乐观。