【问题标题】:How to store original date/time without timezone calculation to MongoDb如何在没有时区计算的情况下将原始日期/时间存储到 MongoDb
【发布时间】:2019-12-19 01:36:19
【问题描述】:

我从具有事件日期/时间的提供商处获得了不同位置的一些事件的列表(事件位于不同的时区,因此日期/时间始终是本地的)。我需要在获得它们时将其保存到 mongodb,但看起来 Mongo 正在使用我的本地时区对其进行转换,因此我将 18:00 保存在数据库中,而不是从 20:00 开始的事件。

我正在使用 java spring boot 应用程序,当我尝试过滤结果时,如果我选择 19:00 之后的事件,我的事件不在列表中,但它应该在那里。我想存储这些日期而不计算时区。有没有办法配置mongo不做计算?我正在使用 mongodb 驱动程序版本 3.9.0。

更新(时间字段定义):

@Temporal(TemporalType.TIMESTAMP)
@Field("time")
private LocalDateTime time;

更新 2: 我玩过 shell,当我插入类似的东西时:

var mydate2 = ISODate();
db.test.insertOne( { iso: mydate2 } );

结果是:

{ "_id" : ObjectId("5d5173dc6240209d9377677a"), "iso" : ISODate("2019-08-12T14:12:42.762Z") }

但是当我改为插入日期时:

var mydate2 = Date();
db.test.insertOne( { iso: mydate2 } );

结果是:

{ "_id" : ObjectId("5d51741b6240209d9377677b"), "iso" : "Mon Aug 12 2019 16:13:42 GMT+0200 (Central Europe Standard Time)" }

,所以是正确的。 我可以从 Spring 文档定义中以某种方式说我想使用 Date 吗?我应该使用 ZonedDateTime 而不是 LocalDateTime?

【问题讨论】:

  • 到目前为止你尝试过什么?你在用ISODate吗?
  • 是的,ISODate。也使用 mongobee。在保存之前将 LocalDateTime 转换为 Date 时,mongo 似乎正在这样做。也许我不应该在 mongo 文档定义中使用 LocalDateTime?
  • 你应该使用 UTC/GMT/Zulu,而不是本地。
  • 在哪里,怎么做?对于文档定义中的字段?我将在文档中使用我当前的时间字段定义更新问题...

标签: java mongodb spring-boot date timezone


【解决方案1】:

我设法使用 ZonedDateTime 而不是 LocalDateTime 解决了这个问题: 在spring data mongo文档中定义

@Field("eventTime")
private ZonedDateTime eventTime;

Mongo 没有自己的 ZonedDateTime 转换器,所以我创建了自己的:

public class ZonedDateTimeReadConverter implements Converter<Date, ZonedDateTime> {

    @Override
    public ZonedDateTime convert(Date date) {
        return date.toInstant().atZone(ZoneOffset.UTC);
    }
}

public class ZonedDateTimeWriteConverter implements Converter<ZonedDateTime, Date> {

    @Override
    public Date convert(ZonedDateTime zonedDateTime) {
        return Date.from(zonedDateTime.toInstant());
    }
}

我们还需要在 MongoDbConfig 类中注册它,例如:

@Configuration
public class MongoDbCommonConfiguration {

    @Bean
    public ValidatingMongoEventListener validatingMongoEventListener() {
        return new ValidatingMongoEventListener(validator());
    }

    @Bean
    public LocalValidatorFactoryBean validator() {
        return new LocalValidatorFactoryBean();
    }

    @Bean
    @Primary
    public CustomConversions customConversions() {
        ZonedDateTimeReadConverter zonedDateTimeReadConverter = new ZonedDateTimeReadConverter();
        ZonedDateTimeWriteConverter zonedDateTimeWriteConverter = new ZonedDateTimeWriteConverter();

        return new CustomConversions(Arrays.asList(zonedDateTimeReadConverter, zonedDateTimeWriteConverter));
    }
}

当我得到我的事件提供者时,我将其 LocalDateTime 更改为 UTC 区域的 ZonedDateTime,例如:

return new Event(providerEvent.getTime().atZone(ZoneId.of("UTC")), providerEvent.getName(),...);

现在当我从事件提供者那里得到 18:00 时,18:00 将被保存到 mongodb..

【讨论】:

  • ZonedDateTime 这里违背了目的 - 最初它有 TZ,但你将它存储在 Z 中。当你恢复它时 - 原来的 TZ 丢失了。如果你总是想要 Z 中的日期 - 使用 Instant - 这会少得多,而且默认情况下支持它。
【解决方案2】:

我没有看到任何data type in MongoDB 没有时区或偏移的日期和时间。

ISO 8601

所以我建议您将值保存为标准 ISO 8601 格式 YYYY-MM-DDTHH:MM:SS 的文本。 T 将日期部分与时间部分分开。

请注意这些文本在按字母顺序排序时是如何按时间顺序排列的。

LocalDate ld = LocalDate.of( 2020 , 1 , 23 ) ;
LocalTime lt = LocalTime.of( 15 , 30 ) ;
LocalDateTime ldt = LocalDateTime.of( ld , lt ) ;
String output = ldt.toString() ; 

2020-01-23T15:30

解析。

LocalDateTime ldt = LocalDateTime.parse( "2020-01-23T15:30:00" ) ;

【讨论】:

  • 不能使用文本选项,因为我正在使用聚合,并且在使用 String 而不是 Iso 时存在限制。
  • 我会在字符串末尾添加“Z”以强调它是 UTC:“yyyy-mm-ddTHH:MM:SS.sssZ”。它是 ISO8601 的一部分
  • @Shloim 您错过了问题的重点:没有考虑区域/偏移的日期和时间。因此,为 UTC 添加Z 正是在这里要做的。
  • 你是对的。我确实错过了。我习惯了一个约定,要么你存储自纪元以来的毫秒/秒,然后实际上没有时区信息,要么你将时间存储在 UTC 中,然后它也是时区中立的。以 yyyymmddHHMMSS 格式存储时间而没有任何时区信息的想法让我感到困惑,因为它只会引入复杂性,例如时区转换和夏令时不兼容。
【解决方案3】:

尝试使用 Instant。

Instant i = Instant.now();
ZoneId losAngelesTimeZone = ZoneId.of("America/Los_Angeles");
System.out.println(i.atZone(chicagoTimeZone));

【讨论】:

  • 但我想避免使用时区。我只想按原样保存我的日期/时间......不可能这么难吗?我错过了什么?
  • 通常情况下,我会收到 ISO 1861 UTC 的日期时间。以 UTC 格式将其保存在数据库中,并在您显示客户端转换为客户端时区或应用程序用户配置时区时。我认为您需要保存“LocalDateTime”并保存时区。
  • 当我将它加载回来时,它会被转换回来,但是当我直接使用 repo 进行过滤时,这不是计算偏移量......所以我的过滤不起作用。我设法解决了它,明天将提供答案...
猜你喜欢
  • 2016-11-18
  • 2020-06-02
  • 2020-03-15
  • 1970-01-01
  • 2020-05-21
  • 1970-01-01
  • 2017-09-02
  • 1970-01-01
  • 2013-09-09
相关资源
最近更新 更多