提问人:Algimantas 提问时间:8/28/2023 最后编辑:Algimantas 更新时间:9/3/2023 访问量:126
如何使 LocalDateTime.parse 拒绝 24:00 时间
How to make LocalDateTime.parse reject 24:00 time
问:
我有一个表单,用户可以从键盘输入日期和时间,当输入的时间部分为 24:00 时,LocalDateTime 接受输入,将其转换为第二天。
我的代码
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
val INPUT_DATETIME_PATTERN = "yyyyMMddHHmm"
val formatter = DateTimeFormatter.ofPattern(INPUT_DATETIME_PATTERN)
val datetime = "202308102400"
val localdatetime = LocalDateTime.parse(datetime, formatter)
println(datetime)
println(localdatetime.format(formatter))
预期结果:
LocalDateTime.parse 引发异常。
根据文档,HH 代表一天中的小时数 (0-23),因此 2400 无效。输入字符串202308102401也会引发异常。
实际结果:
打印202308110000。在 Android Studio 和 Kotlin playground 中签入了划痕(链接)
答:
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
val INPUT_DATETIME_PATTERN = "yyyyMMddHHmm"
val formatter = DateTimeFormatter.ofPattern(INPUT_DATETIME_PATTERN)
val datetime = "202308102400"
val adjustedDatetime = datetime.replaceRange(8..9, "00")
val localdatetime = LocalDateTime.parse(adjustedDatetime, formatter)
println(datetime)
println(localdatetime.format(formatter))
此代码在分析日期时间字符串之前将时间部分从“2400”调整为“0000”,并生成预期的输出
评论
(在 Java 语法中,而不是 Kotlin)
TL的;博士
若要拒绝此类输入,请指定严格的解析器样式。
DateTimeFormatter
.ofPattern( "uuuuMMddHHmm" )
.withResolverStyle( ResolverStyle.STRICT )
当输入的时间部分为 24:00 时,LocalDateTime 接受输入,并将其转换为第二天
是的,它应该这样做。您看到的是一个功能,而不是一个错误。
在 00-23 时钟上使用 24:00 表示“今天结束”,也是“明天开始”。所以从逻辑上讲:
( 202308102400 = 202308110000 ) -> true
此特定行为已记录在案。
24:00 确实意味着第二天
24:00
表示第二天的第一刻。相反,00:00 表示指定日期的第一个时刻。
从概念上讲,每天有两个午夜,一个在一天的开始,一个在一天的结束。为了避免这种歧义,请注意 java.time 类避免了“午夜”这个术语和概念。相反,java.time 类关注的是一天中的第一个时刻。
一些行业通过使用 & 来区分两个午夜。他们使用符号作为“在今天一天结束时”的一种方式。00:00
24:00
24:00
因此,我们看到这与 .今天一天的结束也是明天一天的开始。202308102400
202308110000
因此,的行为在语义上是正确的,将第二天的日期以及一天中的时间 00:00 返回给您。LocalDateTime
请参阅以下代码,在 Ideone.com
final DateTimeFormatter BASIC_ISO_LOCAL_DATE_TIME = DateTimeFormatter.ofPattern( "uuuuMMddHHmm" ) ;
String input = "202308102400" ;
try {
LocalDateTime ldt = LocalDateTime.parse ( input , BASIC_ISO_LOCAL_DATE_TIME ) ;
System.out.println( "ldt.toString() = " + ldt ) ;
} catch ( DateTimeParseException e ) {
System.out.println( "Yuck. Your input is distasteful. " ) ;
}
ldt.toString() = 2023-08-11T00:00
检查输入String#endsWith
如果您确定应该拒绝时间,您可以简单地检查字符串输入是否存在 at end。24:00
2400
if ( input.endsWith( "2400" ) { … }
else { LocalDateTime ldt = LocalDateTime.parse( input ) ; }
Resolver.STRICT
正如您最终发现的那样,若要使 DateTimeFormatter
对象拒绝时间为 24:00 的输入,请将 ResolverStyle
设置为枚举对象 STRICT
。严格模式只能容忍 00-23 小时。
final DateTimeFormatter STRICT_BASIC_ISO_LOCAL_DATE_TIME =
DateTimeFormatter
.ofPattern( "uuuuMMddHHmm" )
.withResolverStyle( ResolverStyle.STRICT ) ; // <— Reject 2400 as time of day.
捕获 DateTimeParseException
以处理此类不受欢迎的输入。
String input = "202308102400" ;
try {
LocalDateTime ldt = LocalDateTime.parse ( input , STRICT_BASIC_ISO_LOCAL_DATE_TIME ) ;
System.out.println( "ldt.toString() = " + ldt ) ;
} catch ( DateTimeParseException e ) {
System.out.println( "Yuck. Your input is distasteful. " ) ;
}
评论
事实证明,没有足够注意阅读文档是我的错......感谢Marco F.指出我错过了什么。
DateTimeResolver
默认情况下,它对每个字段执行合理的默认值,例如将 24:00 解释为“第二天的第一个时刻”,或者允许当月值最多 31,静默转换为实际有效日期值。ResolverStyle.SMART
正确的代码以获取预期行为:
val INPUT_DATETIME_PATTERN = "uuuuMMddHHmm"
val formatter = DateTimeFormatter
.ofPattern(INPUT_DATETIME_PATTERN)
.withResolverStyle(ResolverStyle.STRICT)
val datetime = "202308102400"
val localdatetime = LocalDateTime.parse(datetime, formatter)
println(datetime)
println(localdatetime.format(formatter))
另请注意,年份说明符从 yyyy(纪元年)更改为 uuuu(年),因为严格解析器需要一个纪元与 YearOfEra(源)一起使用。
将您的行
更改为
“查看此问题以获取更多信息”:使用新的 Java 8 DateTimeFormatter 执行严格的日期解析 此外,在您的格式中,将年份部分从“”更改为“”。没有这个,它就行不通。val formatter = DateTimeFormatter.ofPattern(INPUT_DATETIME_PATTERN)
val formatter = DateTimeFormatter.ofPattern(INPUT_DATETIME_PATTERN).withResolverStyle(ResolverStyle.STRICT)
yyyy
uuuu
评论