2014-04-01 50 views
17

我在Java 8中使用了新的java.time包。我有一个旧数据库,它给了我java.util.Date,我将其转换为InstantJava 8 java.time:在Instant中添加TemporalUnit与LocalDateTime

我想要做的是添加一段基于另一个数据库标志的时间。我可以增加几天,几周,几个月或几年。我不想关心我所添加的内容,我希望能够在未来添加更多选项。

我的第一个想法是Instant.plus(),但这给了我一个UnsupportedTemporalTypeException值大于一天。即时显然不支持大单位时间的操作。好吧,无论如何,LocalDateTime呢。

所以这给了我这个代码:

private Date adjustDate(Date myDate, TemporalUnit unit){ 
    Instant instant = myDate.toInstant(); 
    LocalDateTime dateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); 
    dateTime = dateTime.plus(1, unit); 
    Instant updatedInstant = dateTime.atZone(ZoneId.systemDefault()).toInstant(); 
    return new Date(dueInstant.toEpochMilli()); 
} 

现在,这是使用新的API时我的第一次,所以我会在这里错过了一些东西。但似乎笨重,我认为我必须去:

Date --> Instant --> LocalDateTime --> do stuff--> Instant --> Date. 

即使我没有使用日期部分,我仍然认为这是一个有点尴尬。所以我的问题是,我是否完全错误,做这件事的最好方法是什么?


编辑:扩大对注释的讨论。

我想我现在有更好的想法了解LocalDateTime和Instant如何与java.util.Date和java.sql.Timestamp一起玩。感谢大家。

现在,更实际的考虑。假设用户向我发送一个来自世界任何地方的任意时间的日期。他们发给我2014-04-16T13:00:00,我可以解析成LocalDateTime。然后我直接将其转换为java.sql.Timestamp并保存在我的数据库中。

现在,没有做任何事情,我从我的数据库中取出java.sql.timestamp,使用timestamp.toLocalDateTime()转换为LocalDateTime。都好。然后我使用ISO_DATE_TIME格式将此值返回给我的用户。结果是2014-04-16T09:00:00

我认为这种差异是由于某种类型的隐式转换为/从UTC。我认为我的默认时区可能会被应用到数值(EDT,UTC-4),这可以解释为什么数字关闭了4个小时。

新的问题。本地时间到UTC的隐式转换在哪里发生?什么是更好的方式来保存时区。我不应该直接从当地时间作为一个字符串(2014-04-16T13:00:00)到LocalDateTime?我是否应该期待用户输入的时区?

+3

你打算在这里代表什么价值? “即时”在逻辑上不知道日历系统 - 这只是一个时间点 - 所以增加一个月没有意义。您还应该仔细考虑您是否真的想要使用系统时区 - 您是否希望针对相同的值获得不同的结果,具体取决于您正在运行的位置? –

+0

@JonSkeet那么随意选择ZoneId会更合适吗?总是格林威治时间什么的看起来这可能会带来一系列的问题。也许问题是我不知道如何表示我的java.util.date。我有一个时间点。如果符合某些条件,我想在未来将这一点改为一个月(或日,年,无论)。 – jacobhyphenated

+0

使用UTC可能是最佳选择,但您确实需要考虑您的要求。它甚至意味着将一个时间点(没有时区或日历)改变一个月? –

回答

16

我会继续前进,并根据我最终的解决方案发表一个答案,以及对非常长的评论链的总结。

要启动,整个转换链:

Date --> Instant --> LocalDateTime --> Do stuff --> Instant --> Date 

是必要的,以保存时区信息,仍然做喜欢的对象一个日期,是意识到日历和所有在其中的上下文的操作。否则,我们冒着隐式转换到当地时区的风险,如果我们试图将其转换为人类可读的日期格式,则时间可能因此而改变。

例如,java.sql.Timestamp类中的toLocalDateTime()方法隐式转换为默认时区。这对我的目的是不理想的,但不一定是不好的行为。但是,重要的是要意识到这一点。这是直接从遗留java日期对象转换为LocalDateTime对象的问题。由于传统对象通常被认为是UTC,因此转换使用本地时区偏移量。

现在,让我们的程序将输入2014-04-16T13:00:00并保存到数据库作为java.sql.Timestamp

//Parse string into local date. LocalDateTime has no timezone component 
LocalDateTime time = LocalDateTime.parse("2014-04-16T13:00:00"); 

//Convert to Instant with no time zone offset 
Instant instant = time.atZone(ZoneOffset.ofHours(0)).toInstant(); 

//Easy conversion from Instant to the java.sql.Timestamp object 
Timestamp timestamp = Timestamp.from(instant); 

现在我们采取了时间戳,并添加天的一定数量它:

Timestamp timestamp = ... 

//Convert to LocalDateTime. Use no offset for timezone 
LocalDateTime time = LocalDateTime.ofInstant(timestamp.toInstant(), ZoneOffset.ofHours(0)); 

//Add time. In this case, add one day. 
time = time.plus(1, ChronoUnit.DAYS); 

//Convert back to instant, again, no time zone offset. 
Instant output = time.atZone(ZoneOffset.ofHours(0)).toInstant(); 

Timestamp savedTimestamp = Timestamp.from(output); 

现在,我们只需要输出在​​格式人类可读的字符串。

Timestamp timestamp = .... 
LocalDateTime time = LocalDateTime.ofInstant(timestamp.toInstant(), ZoneOffset.ofHours(0)); 
String formatted = DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(time); 
+6

ZoneOffset.ofHours(0)可以更好地表示为ZoneOffset.UTC。您也可能发现使用ZonedDateTime更容易,因为该类支持LocalDateTime的所有正/负行为,但保留了时区信息。 – JodaStephen

+0

对于日期的具体使用情况,您不需要(本地/已分区)日期时间,因为持续时间可以直接添加到即时:'Timestamp.from(timestamp.toInstant()。plus(Duration。 ofDays(1)))' –

+1

将'Duration.ofDays'添加到'Instant'将不考虑日光节约等,因此不推荐。改为转换为“ZonedDateTime”。 –

相关问题