使用小时调整而不是区域设置打印 DateTime 的最简单方法是什么?
What is the easiest way to print a DateTime using an hour adjustment instead of a locale?
我正在开发一个系统,在该系统中,每个用户都可以根据 UTC 以小时为单位指定他们的调整设置。
因此,当此用户输入 date/time 时,该值会根据他们的设置进行调整并保存为 UTC。同样,在出路时,date/time 从数据库中检索,根据其设置进行调整并显示。
我可能想太多了,但这是否意味着要为每个人显示正确的 date/time,我必须有效地调整时间并告诉我的 SimpleDateFormat 实例这是 "UTC"?现在我在英国,当前时区是 UTC+1,如果我不指定以 UTC 打印,那么时间会延迟一小时!
DateTime dateWithOffset = statusDate.plusMinutes(currentTimezoneOffsetInMinutes);
SimpleDateFormat sdf = new SimpleDateFormat("dd MMM yyyy HH:mm");
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
return sdf.format(dateTime.toDate());
我的想法是否正确?或者有没有更简单的方法来打印日期格式,因为我只想从 UTC 调整小时数?
你太辛苦了。切勿手动调整偏移量和时区,切勿为此目的向日期时间值添加或减去分钟数。让体面的日期时间库来完成这项工作。
java.time
Joda-Time 团队建议我们迁移到 Java 8 及更高版本中内置的 java.time 框架。
ZoneOffset
class represents an offset-from-UTC。请记住,在某些地区,偏移可能不仅涉及小时数,还涉及分钟甚至秒。
OffsetDateTime
class 表示时间轴中具有指定偏移量的时刻。
int hours = 3; // input by user
ZoneOffset offset = ZoneOffset.ofHours( hours );
OffsetDateTime odt = OffsetDateTime.now( offset );
java.time 中的 toString 方法使用标准 ISO 8601 格式。
String output = odt.toString();
一般而言,最佳做法是在 UTC 中执行业务逻辑和数据存储。转换 to/from 偏移量或分区值仅用于与用户交互。
java.time UTC is represented by the Instant
class 时间线上的一个时刻。您可以从 OffsetDateTime
.
中提取一个 Instant
对象
Instant instant = odt.toString();
这个Instant
和这个OffsetDateTime
都代表了时间轴上的同一时刻。他们呈现出不同的wall-clock times.
跳过OffsetDateTime.now
便捷方法的使用,从Instant
开始可能更清晰。
Instant instant = Instant.now(); // Always in UTC, by definition.
ZoneOffset offset = ZoneOffset.ofHours( hours );
OffsetDateTime odt = OffsetDateTime.ofInstant( instant , offset ); // Same moment but presenting alternate wall-clock time.
处理输入
如果用户将日期时间值作为字符串输入,我们需要进行解析。在 java.time 中表示 DateTimeFormatter
class。格式代码与过时的 java.text.SimpleDateFormat
相似但不完全相同,因此请务必研究文档。
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "dd MMM uuuu HH:mm";
由于与 UTC 的偏移量是单独给出的,我们将此输入字符串解析为 LocalDateTime
缺少时区信息。
LocalDateTime ldt = LocalDateTime.parse( inputString , formatter );
要查看,请通过调用 ldt.toString()
创建一个 ISO 8601 格式的字符串对象。
2016-01-02T12:34:45
应用预先确定的 ZoneOffset
对象生成 OffsetDateTime
对象。
OffsetDateTime odt = ldt.atOffset( offset );
2016-01-12T12:34:45+03:00
以 UTC 思考
处理日期时间值很头疼。 UTC 是你的阿司匹林。
当程序员到达办公室时,她应该摘下“英国公民/伦敦居民”的帽子,戴上“UTC”的帽子。忘记您自己的本地时区。学会用 UTC(和 24 小时制)思考。在您的办公桌或计算机上添加另一个时钟,设置为 UTC(或 Reykjavík Iceland), or at least bookmark a page like time.is/UTC。在 UTC 中执行所有日志记录、业务逻辑、数据序列化、数据交换和调试。
让 Instant
成为您的第一个想法,您的首选 class。根据定义,它的值始终采用 UTC。
Instant instant = Instant.now();
查看从我们在上面看到的 OffsetDateTime
值中提取的 Instant
,其字符串表示形式为 2016-01-12T12:34:45+03:00
。在 UTC 中意味着上午 9 点而不是中午,同一时刻但挂钟时间相差三个小时。 Z
是 Zulu
的缩写,表示 UTC。
String output = odt.toInstant().toString();
2016-01-12T09:34:45Z
仅在用户或数据接收器期望时根据需要调整偏移量或时区。仅供参考,时区是与 UTC 的偏移量 加上 一组用于处理夏令时 (DST) 等异常的规则。 尽可能使用时区而不是单纯的偏移量。
Europe/London
时区夏天和UTC一样,但是冬天用夏令时废话,比UTC早一小时。因此,使用上面看到的相同 Instant
,伦敦挂钟时间是上午 10 点,而不是 UTC 中的上午 9 点,并且与我们看到的偏移 +03:00
.[=68= 的中午不同]
ZoneId zoneId = ZoneId.of( "Europe/London" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );
2016-01-12T10:34:45+01:00[Europe/London]
始终指定 desired/required 偏移量或时区;永远不要通过省略此可选参数来依赖隐式当前默认值。 (Locale
by the way.) Note how in all the code of this answer the fact that your JVM has a current default time zone (ZoneId.systemDefault
) of Europe/London
and the fact that my JVM has a current default time zone of America/Los_Angeles
的同上 完全无关 。代码运行相同,得到相同的结果,无论您使用什么机器进行开发、测试和部署。
语言环境
在生成日期时间值的文本表示时指定一个 Locale
对象,该日期时间值涉及月份或日期、逗号或句点等名称。 Locale
决定 (a) 翻译此类名称时使用的人类语言,以及 (b) 在决定标点符号等问题时遵循的文化规范。
Locale
与时区和与 UTC 的偏移量无关。例如,您可以在印度使用 Locale.CANADA_FRENCH
with a date-time zoned for Asia/Kolkata
if you had a Québécois 用户。
我正在开发一个系统,在该系统中,每个用户都可以根据 UTC 以小时为单位指定他们的调整设置。
因此,当此用户输入 date/time 时,该值会根据他们的设置进行调整并保存为 UTC。同样,在出路时,date/time 从数据库中检索,根据其设置进行调整并显示。
我可能想太多了,但这是否意味着要为每个人显示正确的 date/time,我必须有效地调整时间并告诉我的 SimpleDateFormat 实例这是 "UTC"?现在我在英国,当前时区是 UTC+1,如果我不指定以 UTC 打印,那么时间会延迟一小时!
DateTime dateWithOffset = statusDate.plusMinutes(currentTimezoneOffsetInMinutes);
SimpleDateFormat sdf = new SimpleDateFormat("dd MMM yyyy HH:mm");
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
return sdf.format(dateTime.toDate());
我的想法是否正确?或者有没有更简单的方法来打印日期格式,因为我只想从 UTC 调整小时数?
你太辛苦了。切勿手动调整偏移量和时区,切勿为此目的向日期时间值添加或减去分钟数。让体面的日期时间库来完成这项工作。
java.time
Joda-Time 团队建议我们迁移到 Java 8 及更高版本中内置的 java.time 框架。
ZoneOffset
class represents an offset-from-UTC。请记住,在某些地区,偏移可能不仅涉及小时数,还涉及分钟甚至秒。
OffsetDateTime
class 表示时间轴中具有指定偏移量的时刻。
int hours = 3; // input by user
ZoneOffset offset = ZoneOffset.ofHours( hours );
OffsetDateTime odt = OffsetDateTime.now( offset );
java.time 中的 toString 方法使用标准 ISO 8601 格式。
String output = odt.toString();
一般而言,最佳做法是在 UTC 中执行业务逻辑和数据存储。转换 to/from 偏移量或分区值仅用于与用户交互。
java.time UTC is represented by the Instant
class 时间线上的一个时刻。您可以从 OffsetDateTime
.
Instant
对象
Instant instant = odt.toString();
这个Instant
和这个OffsetDateTime
都代表了时间轴上的同一时刻。他们呈现出不同的wall-clock times.
跳过OffsetDateTime.now
便捷方法的使用,从Instant
开始可能更清晰。
Instant instant = Instant.now(); // Always in UTC, by definition.
ZoneOffset offset = ZoneOffset.ofHours( hours );
OffsetDateTime odt = OffsetDateTime.ofInstant( instant , offset ); // Same moment but presenting alternate wall-clock time.
处理输入
如果用户将日期时间值作为字符串输入,我们需要进行解析。在 java.time 中表示 DateTimeFormatter
class。格式代码与过时的 java.text.SimpleDateFormat
相似但不完全相同,因此请务必研究文档。
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "dd MMM uuuu HH:mm";
由于与 UTC 的偏移量是单独给出的,我们将此输入字符串解析为 LocalDateTime
缺少时区信息。
LocalDateTime ldt = LocalDateTime.parse( inputString , formatter );
要查看,请通过调用 ldt.toString()
创建一个 ISO 8601 格式的字符串对象。
2016-01-02T12:34:45
应用预先确定的 ZoneOffset
对象生成 OffsetDateTime
对象。
OffsetDateTime odt = ldt.atOffset( offset );
2016-01-12T12:34:45+03:00
以 UTC 思考
处理日期时间值很头疼。 UTC 是你的阿司匹林。
当程序员到达办公室时,她应该摘下“英国公民/伦敦居民”的帽子,戴上“UTC”的帽子。忘记您自己的本地时区。学会用 UTC(和 24 小时制)思考。在您的办公桌或计算机上添加另一个时钟,设置为 UTC(或 Reykjavík Iceland), or at least bookmark a page like time.is/UTC。在 UTC 中执行所有日志记录、业务逻辑、数据序列化、数据交换和调试。
让 Instant
成为您的第一个想法,您的首选 class。根据定义,它的值始终采用 UTC。
Instant instant = Instant.now();
查看从我们在上面看到的 OffsetDateTime
值中提取的 Instant
,其字符串表示形式为 2016-01-12T12:34:45+03:00
。在 UTC 中意味着上午 9 点而不是中午,同一时刻但挂钟时间相差三个小时。 Z
是 Zulu
的缩写,表示 UTC。
String output = odt.toInstant().toString();
2016-01-12T09:34:45Z
仅在用户或数据接收器期望时根据需要调整偏移量或时区。仅供参考,时区是与 UTC 的偏移量 加上 一组用于处理夏令时 (DST) 等异常的规则。 尽可能使用时区而不是单纯的偏移量。
Europe/London
时区夏天和UTC一样,但是冬天用夏令时废话,比UTC早一小时。因此,使用上面看到的相同 Instant
,伦敦挂钟时间是上午 10 点,而不是 UTC 中的上午 9 点,并且与我们看到的偏移 +03:00
.[=68= 的中午不同]
ZoneId zoneId = ZoneId.of( "Europe/London" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );
2016-01-12T10:34:45+01:00[Europe/London]
始终指定 desired/required 偏移量或时区;永远不要通过省略此可选参数来依赖隐式当前默认值。 (Locale
by the way.) Note how in all the code of this answer the fact that your JVM has a current default time zone (ZoneId.systemDefault
) of Europe/London
and the fact that my JVM has a current default time zone of America/Los_Angeles
的同上 完全无关 。代码运行相同,得到相同的结果,无论您使用什么机器进行开发、测试和部署。
语言环境
在生成日期时间值的文本表示时指定一个 Locale
对象,该日期时间值涉及月份或日期、逗号或句点等名称。 Locale
决定 (a) 翻译此类名称时使用的人类语言,以及 (b) 在决定标点符号等问题时遵循的文化规范。
Locale
与时区和与 UTC 的偏移量无关。例如,您可以在印度使用 Locale.CANADA_FRENCH
with a date-time zoned for Asia/Kolkata
if you had a Québécois 用户。