【问题标题】:How to correctly deal with time saving on server and client side如何正确处理服务器端和客户端的节省时间
【发布时间】:2019-07-30 12:12:26
【问题描述】:

我读过很多关于时间的文章(增量时间、观察时间、偏移量、时区...) 现在我正在用 Spring 开发一个后端架构,它到了我必须将它们组合在一起的地步。客户端发送和接收带有时间戳和值的 JSON,我可以决定语法应该如何。我不确定星座是否与我计划的方式正确,所以如果您在必要的地方纠正我,我会很高兴。

第一:我如何理解基本概念。

口头禅:始终使用 UTC 时间。

格式:iso8601 YYYY-MM-DDThh:mm:ss.sTZD(例如 1997-07-16T19:20:30.45+01:00) 或者 1997-07-16T20:20:30.45Z

时区:例如“欧洲/柏林” 这是时区数据库的 ID,其中的特定规则 区域已定义(DST 偏移量..)。

偏移量:告诉您相对于 UTC 时间的正偏移量或负偏移量。

我的计划: 就我而言,数据可能是在不同的时区进行采样的。在采样过程中更改时区也是很可能的。

所以我想在我的mongodb中存储如下数据:

 "TimeStamp": {
                            "StartTime":  "1997-07-16T20:20:30.45Z",
                            "EndTime":  "1997-07-16T20:20:30.45Z",
                            "StartTimeZone":"Europe/Berlin",
                            "EndTimeZone":"Europe/Berlin",
                            "StartTimeOffset": +7,
                            "EndTimeOffset" : +7
                        } 

客户端可以选择一个区间内的数据将被返回。间隔也必须定义为没有偏移的 UTC iso 格式。据我所知,使用 UTC 格式,我可以在日期上执行 $lte 和 $gte 操作来过滤间隔。 所以客户端会收到一个带有多个 JSOnObjects 的 JSOnArray。每个对象都有一个值和一个时间戳对象。使用 TimeZone 可以查看数据采样时的偏移量,因此我不必添加偏移量信息,因此可以计算用户的本地时间。但是,如果偏移量发生变化怎么办。我想为此存储偏移量也是有意义的,因此人们也可以重建“旧”时区规则。 你认为这是一个很好的解决方案还是我忘记/误解了/可以做得更有效

【问题讨论】:

  • 你的方法对我来说听起来不错。 Java 最适合使用两位数小时、冒号和分钟的偏移量,例如 +07:00

标签: java mongodb time timezone utc


【解决方案1】:

在 Java 中,如果您想谈论某个特定时间点,请使用Instant。如果两个事件发生在不同的时区,但在同一实际时刻,它们的Instant 值将是相同的。

如果你想知道那个地区的各种时钟当时说了什么,你需要知道你在哪个时区。例如:

Instant now = Instant.now();  // refers to a point in time, independent of location
LocalDateTime nowHere = now.atZone(ZoneId.systemDefault()).toLocalDate();  // refers to "what the clocks say" at your machine's current timezone
LocalDateTime nowSomewhereElse = now.atZone(ZoneId.of("Timezone string")).toLocalDate();  // same as above, but for somewhere else.

以下 API 是 Java 8 的一部分,与 JPA 和其他常用库/API 完全兼容。如果您只关心事件发生的那一天,则存在等效类。

Instant 也可以使用instant.toEpochMilli()instant.getEpochSecond()instant.getNano() 转换为时间戳。

【讨论】:

  • 谢谢,但就我而言,我不必处理时间戳的创建方式。我刚刚收到完整的 JSONS,其中客户端已经输入了所有时间信息。所以我需要知道更多关于我要使用的格式是否正确以及我提到的当地时间的重建过程是否正确
  • LocalDateTime 在这里使用的类是错误的。正如 Javadoc 中所解释的,它不能代表片刻。
【解决方案2】:

您的问题相当混乱。

如果您有 UTC 时间,您知道与 UTC 的偏移量为零,因此您不必关心更多时区信息。

Instant instant = Instant.now() ;      // Capture the current moment as seen in UTC (an offset-from-UTC of zero hours-minutes-seconds).
String output = instant.toString() ;   // Generate text representing this moment in UTC in standard ISO 8601 format. The `Z` on end means UTC (an offset of zero), and is pronounced "Zulu". 

如果有人想通过德国使用的挂钟时间看到那一刻,他们可以应用ZoneId 来生成ZonedDateTime 对象。

ZoneId zBerlin = ZoneId.of( "Europe/Berlin" ) ;
ZonedDateTime zdtBerlin = instant.atZone( zBerlin ) ;

如果有人想通过日本使用的挂钟时间看到那一刻,同上。

ZoneId zTokyo = ZoneId.of( "Asia/Tokyo" ) ; 
ZonedDateTime zdtTokyo = instant.atZone( zTokyo ) ;

如果有人想通过魁北克使用的挂钟时间看到那一刻,同上。

ZoneId zMontréal = ZoneId.of( "America/Montreal" ) ; 
ZonedDateTime zdtMontréal = instant.atZone( zMontréal ) ;

所有这些(instantzdtBerlinzdtTokyozdtMontréal)都代表同一时刻,时间轴上的同一点。只有挂钟时间不同。想象一下与每个地区的参与者进行的电话会议。他们都经历了相同的时刻,但是当他们抬头看挂在各自墙上的时钟时,他们每个人看到的读数都不同。

TimeZone:例如“Europe/Berlin”这是时区数据库的 ID,其中定义了区域的特定规则(DST 偏移量..)。

偏移量:告诉您相对于 UTC 时间的正偏移量或负偏移量。

明确zone和offset的含义:

  • 与 UTC 的偏移量只是几个小时-分钟-秒。而已。例如,-07:00
  • 时区更多。时区是特定地区的人们使用的偏移量的过去、现在和未来变化的历史。例如,在一个政客们疯狂到采用Daylight Saving Time (DST) 的地方,他们与 UTC 的偏移量每年变化两次,一个小时跳一个头,然后一个小时回落。 DST 不会在Einstein/Relativity sense 中扭曲时间,DST 只会玩挂钟时间的愚蠢游戏。

数据是在不同时区采样的

你不在乎。如果您在 UTC 中捕捉到某个时刻,这就是您所需要的。

在采样过程中更改时区也是可能的。

再一次……你不在乎。如果您在 UTC 中捕捉到某个时刻,这就是您所需要的。

所以我想在我的mongodb中存储如下数据:

 "TimeStamp": {
    "StartTime":  "1997-07-16T20:20:30.45Z",
    "EndTime":  "1997-07-16T20:20:30.45Z",
    "StartTimeZone":"Europe/Berlin",
    "EndTimeZone":"Europe/Berlin",
    "StartTimeOffset": +7,
    "EndTimeOffset" : +7
} 

不,太多了。最后四行是多余的,可能令人困惑。您只需要:

 "TimeStamp": {
    "StartTime":  "1997-07-16T20:20:30.45Z",
    "EndTime":  "1997-07-16T20:20:30.45Z"
} 

顺便说一句,timestamp 这个词通常表示特定的时刻,而不是时间跨度。我建议一个更好的名字,比如timespan

 "TimeSpan": {
    "StartTime":  "1997-07-16T20:20:30.45Z",
    "EndTime":  "1997-07-16T20:20:30.45Z"
} 

顺便说一下,在您的示例中,开始时间等于结束时间。可能你发错了。

使用 TimeZone 可以查看数据采样时的偏移量,因此我不必添加偏移量信息

您不需要额外的偏移信息。前两行末尾的Z 告诉您您需要知道的一切。 Z,发音为“Zulu”,表示 UTC,偏移量为零。

String input = "1997-07-16T20:20:30.45Z" ;    // The `Z` = UTC, an offset of zero.
Instant instant = Instant.parse( input ) ;
ZoneId z = ZoneId.of( "Pacific/Auckland" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;     // Same moment, different wall-clock time.

这样就可以计算出用户的当地时间

显然,这里的“用户”一词是一个糟糕的术语选择。您的意思是收集数据的人,而不是接收数据的人。因此,您需要记住收集数据的时区。

因此捆绑收集数据样本或仪器读数的时区名称。

 "TimeSpan": {
    "StartTime":  "1997-07-16T20:20:30.45Z",
    "EndTime":  "1997-07-16T20:20:30.45Z"
    "SampleTimeZone": "Europe/Berlin"
} 

当您想要本地化某个时刻时,您只需要一个时刻(Instant)和一个时区(ZoneId)。从中您可以生成使用该区域的挂钟时间的字符串。

String startInput = …  // Pull "StartTime" element from JSON.
String zoneName = …  // Pull "SampleTimeZone" from JSON.
Instant instant = Instant.parse( startInput ) ;  // "1997-07-16T20:20:30.45Z"
ZoneName zone = ZoneId.of( zoneName ) ;  //  "Europe/Berlin" 
ZonedDateTime zdt = instant.atZone( zone ) ;
String output = zdt.toString() ;    // Or use a `DateTimeFormatter`. 

你认为这是一个很好的解决方案

没有。您对 UTC、时区和与 UTC 的偏移量不相关感到困惑。但它们都是相互关联的。 UTC 是唯一的真实时间。有些地方使用偏移量来调整他们所在地区的挂钟时间。时区是该地区的这些更改的历史记录。

【讨论】:

  • 感谢您的详细解释/回答。对不起,我忘了给你更多关于我的商业日志的信息。故事:用户收集数据,例如在德国。时间跨度(好的措辞,谢谢;)),一天中的当地时间很重要,因为我测量与当地白天/夜晚/睡眠节奏相关的身体活动等等。所以我的计划是让观看数据的客户,例如从另一个时区重建用户收集数据的日期。我希望你明白我的意思。
  • @medTech 这是您需要在发布之前而不是之后写到问题中的详细信息。
  • @medTech 正如我的回答所解释的,无需发送柏林时间或偏移量。只需发送数据收集的时区名称 (Europe/Berlin)。该时区已经知道当时有效的偏移量。本地挂钟时间可以稍后通过从区域名称实例化ZoneId 并通过应用ZoneIdInstant 实例化ZonedDateTime 来构建。我在底部附近修改了我的答案,以解决您的澄清问题。请注意我建议的 JSON 中的 3 个元素。
  • 对不起,我忘了在我的问题中提到这一点。我考虑过保存偏移量以重建时间,以防偏移量发生变化并且我希望能够知道旧的偏移量。但我读过 die olson db 知道过去的所有变化。所以谢谢你,我会听从你的建议并使用 utc 时间戳
  • @medTech 了解该特定区域过去(以及现在和未来)的偏移变化就是时区的定义。
猜你喜欢
  • 1970-01-01
  • 2013-08-10
  • 2022-11-16
  • 2011-03-05
  • 2016-02-20
  • 2011-06-07
  • 2020-06-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多