- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
在过去的几年里,我一直在成功地使用 SimpleDateFormat
。我使用它构建了一堆时间实用程序类。
当我遇到 SimpleDateFormat
(SDF) 不是线程安全的问题时,我花了最近几天重构这些实用程序类以在内部使用 DateTimeFormatter
(DTF)现在。由于两个类(class)的时间模式几乎相同,这种转变在当时看来是个好主意。
我现在在获取 EpochMillis
(自 1970-01-01T00:00:00Z
以来的毫秒数)时遇到问题:将使用 HH:mm
解析的 10:30
解释为 1970-01-01T10:30:00Z
,DTF 不这样做。 DTF 可以使用 10:30
来解析 LocalTime
,但不能使用 ZonedDateTime
来获取 EpochMillis
。
我理解 java.time
的对象遵循不同的哲学; Date
、Time
和 Zoned
对象分开保存。但是,为了让我的实用程序类像以前一样解释所有字符串,我需要能够动态地为所有丢失的对象定义默认解析。我试着用
DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder();
builder.parseDefaulting(ChronoField.YEAR, 1970);
builder.parseDefaulting(ChronoField.MONTH_OF_YEAR, 1);
builder.parseDefaulting(ChronoField.DAY_OF_MONTH, 1);
builder.parseDefaulting(ChronoField.HOUR_OF_DAY, 0);
builder.parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0);
builder.parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0);
builder.append(DateTimeFormatter.ofPattern(pattern));
但这并不适用于所有模式。它似乎只允许未在 pattern
中定义的参数使用默认值。 有没有办法测试 pattern
中定义了哪些 ChronoField
,然后有选择地添加默认值?
或者,我试过
TemporalAccessor temporal = formatter.parseBest(time,
ZonedDateTime::from,
LocalDateTime::from,
LocalDate::from,
LocalTime::from,
YearMonth::from,
Year::from,
Month::from);
if ( temporal instanceof ZonedDateTime )
return (ZonedDateTime)temporal;
if ( temporal instanceof LocalDateTime )
return ((LocalDateTime)temporal).atZone(formatter.getZone());
if ( temporal instanceof LocalDate )
return ((LocalDate)temporal).atStartOfDay().atZone(formatter.getZone());
if ( temporal instanceof LocalTime )
return ((LocalTime)temporal).atDate(LocalDate.of(1970, 1, 1)).atZone(formatter.getZone());
if ( temporal instanceof YearMonth )
return ((YearMonth)temporal).atDay(1).atStartOfDay().atZone(formatter.getZone());
if ( temporal instanceof Year )
return ((Year)temporal).atMonth(1).atDay(1).atStartOfDay().atZone(formatter.getZone());
if ( temporal instanceof Month )
return Year.of(1970).atMonth((Month)temporal).atDay(1).atStartOfDay().atZone(formatter.getZone());
也没有涵盖所有情况。
启用动态日期/时间/日期时间/区域日期时间解析的最佳策略是什么?
最佳答案
Java-8-解决方案:
更改构建器中解析指令的顺序,以便默认指令全部发生在模式指令之后。
例如使用这个静态代码(好吧,你的方法将使用不同模式的基于实例的组合,根本不是高性能的):
private static final DateTimeFormatter FLEXIBLE_FORMATTER;
static {
DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder();
builder.appendPattern("MM/dd");
builder.parseDefaulting(ChronoField.YEAR_OF_ERA, 1970);
builder.parseDefaulting(ChronoField.MONTH_OF_YEAR, 1);
builder.parseDefaulting(ChronoField.DAY_OF_MONTH, 1);
builder.parseDefaulting(ChronoField.HOUR_OF_DAY, 0);
builder.parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0);
builder.parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0);
FLEXIBLE_FORMATTER = builder.toFormatter();
}
原因:
parseDefaulting(...)
方法以一种有趣的方式工作,即类似于嵌入式解析器。这意味着,如果尚未解析该字段,则此方法将为定义的字段注入(inject)默认值。后面的模式指令尝试解析相同的字段(此处:模式“MM/dd”和输入“07/13”的 MONTH_OF_YEAR)但可能具有不同的值。如果是这样,那么复合解析器将中止,因为它发现相同字段的矛盾值并且无法解决冲突(解析值 7,但默认值 1)。
official API包含以下通知:
During parsing, the current state of the parse is inspected. If the specified field has no associated value, because it has not been parsed successfully at that point, then the specified value is injected into the parse result. Injection is immediate, thus the field-value pair will be visible to any subsequent elements in the formatter. As such, this method is normally called at the end of the builder.
我们应该这样理解:
不要在同一字段的任何解析指令之前调用 parseDefaulting(...)
。
旁注 1:
您基于 parseBest(...)
的替代方法更糟糕,因为
它不涵盖所有缺少分钟或仅缺少年份(MonthDay?)等的组合。默认值解决方案更灵活。
性能方面不值得讨论。
旁注 2:
我宁愿让整个实现顺序不敏感,因为这个细节对许多用户来说就像一个陷阱。并且可以通过为默认值选择基于映射的实现来避免此陷阱,就像在我自己的时间库中所做的那样 Time4J default-value-instructions 的顺序根本不重要,因为注入(inject)默认值只发生在所有字段都被解析之后。 Time4J 还专门回答了“启用动态日期/时间/日期-时间/区域-日期-时间解析的最佳策略是什么?”通过提供 MultiFormatParser .
更新:
在 Java-8 中:使用 ChronoField.YEAR_OF_ERA
而不是 ChronoField.YEAR
因为模式包含字母“y”(=year-of-era,不是与 proleptic 公历年相同)。否则,解析引擎除了解析的时代年份之外,还会注入(inject)预期的默认年份,并会发现冲突。一个真正的陷阱。就在昨天,我修好了一个 similar pitfall在我的时间库中,月份字段存在两种略有不同的变化。
关于java - 从 SimpleDateFormat 移动到 DateTimeFormatter 时出现的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40557933/
我们在 java.util.Date 对象的 JAXB 序列化/反序列化过程中使用 SimpleDateFormat,我正在编写以下实用程序来实现这一点 public DateFormat getDa
我正在尝试借助 SimpleDateFormat 将时区设置为不同国家/地区的时区。 SimpleDateFormat.format() 返回给定时区的正确当前时间,但 SimpleDateForma
我正在尝试将 DatePicker 日期格式化为 SimpleDateFormat(“yyyy-MM-dd HH:mm:ss Z”)。有人告诉我,我需要使用 SimpleDateFormatter 将
我遇到this article并感到困惑 在我们的项目中,有几个地方我们使用 ThreadLocal 来构造 ThreadLocal 变量 在其他一些地方,我们在私有(private)本地方法中构造了
在创建 Date 对象期间,我遇到了一个奇怪的问题。我有以下代码将日期的时区设置为 UTC。在解析语句之前,我看到 sdfDate 的时区为 UTC,但在解析语句之后,我看到 PunchDate 具有
我的日期格式如下:2010-03-01T00:00:00-08:00 我已经抛出了以下 SimpleDateFormats 来解析它: private static final SimpleDateF
我有一个从给定日期中提取确切时间的逻辑(例如: 12:00 PM )。 在逻辑的最后,我使用了 SimpleDateFormat解析日期。 当我在 Android 设备上运行代码时,一切正常。 此外,
根据下图,当我使用 item.takendate至SimpleDateFormat 我得到了 《2016年1月》而不是 《2017年1月》 这里发生了什么?请指教。 编辑 但是,当我使用 cdate
我正在尝试使用 java.text.SimpleDateFormat 将日期字符串解析为 java.util.Date;但是,生成的格式化日期是错误的。 这是一个显示问题的测试用例: @Test pu
我正在尝试将“5 月 15 日星期一下午 1 点”格式的字符串解析为日期时间,以便我可以将其输入到数据库中。但是,当我对此进行测试时,解析并没有在正确的日子返回。有谁知道发生了什么? import j
背景: 在我的数据库表中,我有两个时间戳 timeStamp1 = 2011-08-23 14:57:26.662 timeStamp2 = 2011-08-23 14:57:26.9 当我执行“OR
我需要解析以下来自网络服务的日期。 2014-09-16T18:05:00.000-05:00 所以我尝试创建 SimpleDateFormat 对象 SimpleDateFormat simpleD
我很困惑为什么 SimpleDateFormat 没有正确解析以下输入数据。 我试图在 String 上测试各种可能允许的日期格式,看看它是否可以使用其中一种格式正确解析。 注意:我使用的其他不正确的
我想将字符串存储到具有当前时间和日期的 Android 应用程序的数据库(SQLite)中。为此,我正在使用 SimpleDateFormat。不幸的是,它没有显示正确的时间。我尝试了两种选择。 第一
也许这个问题很傻,但我没有找到令我满意的答案。 我有一个 SimpleDataFormat,例如: sdf = new SimpleDateFormat("dd/MM/yyyy"); 如果我尝试解析这
这里我尝试比较 3 个日期,为此,我已将一个日期和两个字符串传递给函数。我使用简单的日期格式来制作 3 个相同格式的日期,以便我可以比较它们。但是对于两个字符串值,我在解析它时得到了错误的日期。有人可
我正在尝试将日期从 2009 年 5 月 15 日 19:24:11 PM MDT 转换为 20090515192411。但是当我尝试下面的代码时,读取格式本身将输入视为 5 月 16 日而不是 5
这个问题已经有答案了: How to get date from date time in java (6 个回答) Why can't this SimpleDateFormat parse thi
这个问题已经有答案了: Changing Java Timestamp format causes a change of the timestamp (2 个回答) Java / convert I
以下代码: SimpleDateFormat sdf = new SimpleDateFormat("MM/dd"); System.out.println(sdf.format(new Date(1
我是一名优秀的程序员,十分优秀!