1970 年伊朗没有夏令时
伊朗的时区 Asia/Tehran 在 2018 年夏天观察到 Daylight Saving Time (DST),但早在 1970 年就没有这样做。
在 2018 年夏天,伊朗比 UTC 时间早四个半小时,而不是三个半小时。因此,从 UTC 的 4:55 开始调整应该会导致夏季的 9:25,而不是 8:25。
System.out.println(
OffsetDateTime.of(
LocalDate.of( 2018 , Month.JUNE , 1 ) ,
LocalTime.parse( "04:55" ) ,
ZoneOffset.UTC
)
.atZoneSameInstant(
ZoneId.of( "Asia/Tehran" )
)
);
2018-06-01T09:25+04:30[亚洲/德黑兰]
但我怀疑您的代码在解析字符串 1970-01-01T00:00:00Z 时默认为 UTC 1970 年第一刻的epoch reference 日期,因为您在尝试解析时间时滥用该类-of-day 不指定日期。
1970 年,伊朗没有遵守夏令时 (DST)。因此,偏移量在 1970 年夏天比 UTC 提前三个半小时,而在 2018 年夏天比 UTC 提前四个半小时。
System.out.println(
OffsetDateTime.of(
LocalDate.EPOCH ,
LocalTime.parse( "04:55" ) ,
ZoneOffset.UTC
)
.atZoneSameInstant(
ZoneId.of( "Asia/Tehran" )
)
);
1970-01-01T08:25+03:30[亚洲/德黑兰]
错误的类
您使用了错误的类。
您想表示一个时间值。所以你应该使用一天中的时间课程。但是您正在使用日期与时间类java.util.Date。
您正在使用麻烦的设计糟糕的日期时间类,java.util.Date 和 java.text.SimpleDateFormat。这些在几年前被 java.time 类所取代。完全避免遗留类。仅使用在java.time package 中找到的类。
时间
解析时间字符串。
LocalTime.parse( "04:55" )
获取当前时间。
LocalTime.now() // Capture the current time of day per the JVM’s current default time zone.
最好明确说明您打算使用 JVM 当前的默认时区。
LocalTime.now(
ZoneId.systemDefault() // Capture the current time of day by explicitly asking for the JVM’s current default time zone.
)
或者指定一个特定的时区。
LocalTime.now(
ZoneId.of( "Asia/Kabul" ) // Capture the current time-of-day as seen in the wall-clock time used by the people of a particular region (a time zone).
)
9:25
以 UTC 格式获取当前时间。
LocalTime.now(
ZoneOffset.UTC
)
04:55
LocalTime
如果您有一个输入字符串,例如“04:55”,则解析为LocalTime 对象。此类表示没有日期和时区的时间。
String input = "04:55" ;
LocalTime lt = LocalTime.parse( input ) ;
术语
我尝试将 UTC 时间转换为本地时间
您的短语“本地时间”在日期时间处理中具有特定含义。不幸的是,这个意思与你的意图相反。在 *java.time.Local...” 类中看到的“本地”一词表示 any 位置或 所有 位置,而不是任何一个特定的位置。
因此,LocalTime 对象在附加到日期并放置在时区的上下文中(或从 UTC 偏移)之前没有真正的意义。
时区
如果 UTC 时间是 04:55,我的手机时钟是 9:25
这意味着您的 JVM 当前的默认时区使用比 UTC 提前四个半小时的 UTC 偏移量,+04:30。根据list of time zones as managed by the IANA,当前只有一个时区使用该偏移量:Asia/Kabul。
要完全表示当前时刻,您需要日期和时间以及区域/偏移量。要捕捉当前时刻,请使用Instant。 Instant 类代表UTC 时间线上的时刻,分辨率为nanoseconds(最多九 (9) 位小数)。
Instant instant = Instant.now() ; // Capture the current moment in UTC.
要查看您所在时区的同一时刻,请应用ZoneId 以获取ZonedDateTime。
ZoneId zKabul = ZoneId.of( "Asia/Kabul" ) ;
ZonedDateTime zdt = instant.atZone( zKabul ) ;
如果您真的只想要该值的时间部分,请提取LocalTime。这可能对在用户界面中呈现有用,但在您的业务逻辑中可能没有用。
LocalTime lt = zdt.toLocalTime() ; // Extract just the time-of-day as seen in the wall-clock time used by the people of this region (this time zone).
作为快捷方式,您可以拨打LocalTime.now:
LocalTime lt = LocalTime.now( zKabul ) ;
伊朗时间
稍后在您的 cmets 中,您解释您的预期时区是 Asia/Tehran、time in Iran。目前,伊朗遵守夏令时 (DST),这可能是您困惑的根源。虽然标准偏移量是 +03:30(比 UTC 提前三个半小时),但在 3 月 22 日和 9 月 22 日之间,偏移量是 +04:30,比 UTC 提前一个小时。
这正是您应该指定所需/预期时区的原因。对于随意使用,您可以使用 JVM 当前的默认时区。但是要知道默认值可以在运行时随时更改。并且默认值可能不是您想要的。对于关键用途,请始终与用户确认他们的预期时区。
让我们建立一个 6 月 1 日的日期时间,您的示例时间是 UTC 时间 4:55。我们可以使用常量ZoneOffset.UTC。如果仅使用与 UTC 的偏移量(在这种情况下偏移量为零),请使用 OffsetDateTime。 offset-from-UTC 只是几个小时和分钟,不多也不少。相比之下,time zone 是过去、现在和未来对特定区域的人们使用的偏移量变化的历史。
LocalTime lt = LocalTime.parse( "04:55" ) ;
LocalDate ld = LocalDate.of( 2018 , Month.JUNE , 1 ) ;
OffsetDateTime odt = OffsetDateTime.of( ld , lt , ZoneOffset.UTC ) ;
odt.toString(): 2018-06-01T04:55Z
通过应用ZoneId 调整到区域Asia/Tehran 以获得ZonedDateTime。同一时刻,时间轴上的同一点,但挂钟时间不同。
ZoneId zTehran = ZoneId.of( "Asia/Tehran" ) ;
ZonedDateTime zdt = odt.atZoneSameInstant( zTehran );
zdt.toString(): 2018-06-01T09:25+04:30[亚洲/德黑兰]
注意夏季节目中的时间是 9 小时,而不是 8 小时。
尝试与一月份相同的代码,此时 DST 不生效。
LocalTime lt = LocalTime.parse( "04:55" );
LocalDate ld = LocalDate.of( 2018 , Month.JANUARY , 1 );
OffsetDateTime odt = OffsetDateTime.of( ld , lt , ZoneOffset.UTC );
ZoneId zTehran = ZoneId.of( "Asia/Tehran" );
ZonedDateTime zdt = odt.atZoneSameInstant( zTehran );
zdt.toString(): 2018-01-01T08:25+03:30[亚洲/德黑兰]
现在我们看到一个 8 小时。
区域名称
以continent/region 的格式指定proper time zone name,例如America/Montreal、Africa/Casablanca 或Pacific/Auckland。切勿使用 3-4 个字母的缩写,例如 EST 或 IST,因为它们不是真正的时区,没有标准化,甚至不是唯一的 (!)。
ZoneId z = ZoneId.of( "America/Montreal" ) ;
如果你想使用 JVM 当前的默认时区,请求它并作为参数传递。如果省略,则隐式应用 JVM 的当前默认值。最好是明确的,因为默认值可能会在任何时候在运行时被 JVM 中任何应用程序的任何线程中的任何代码更改。
ZoneId z = ZoneId.systemDefault() ; // Get JVM’s current default time zone.
关于java.time
java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.Date、Calendar 和 SimpleDateFormat。
Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。
要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310。
您可以直接与您的数据库交换 java.time 对象。使用符合JDBC 4.2 或更高版本的JDBC driver。不需要字符串,不需要java.sql.* 类。
从哪里获得 java.time 类?
ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如Interval、YearWeek、YearQuarter 和more。