我正在比较似乎相同的两个日期,但它们包含不同的区域名称:一个是closeDialog('dialog_gui');
function closeDialog(elementId){
let dialog = new mdc.dialog.MDCDialog(document.getElementById(elementId));
dialog.close();
}
,另一个是Etc/UTC
。
根据这个问题:Is there a difference between the UTC and Etc/UTC time zones? - 这两个区域是相同的。但我的测试失败了:
UTC
结果:
import org.junit.Test;
import java.sql.Timestamp;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import static org.junit.Assert.assertEquals;
public class TestZoneDateTime {
@Test
public void compareEtcUtcWithUtc() {
ZonedDateTime now = ZonedDateTime.now();
ZonedDateTime zoneDateTimeEtcUtc = now.withZoneSameInstant(ZoneId.of("Etc/UTC"));
ZonedDateTime zoneDateTimeUtc = now.withZoneSameInstant(ZoneId.of("UTC"));
// This is okay
assertEquals(Timestamp.from(zoneDateTimeEtcUtc.toInstant()), Timestamp.from(zoneDateTimeUtc.toInstant()));
// This one fails
assertEquals(zoneDateTimeEtcUtc,zoneDateTimeUtc);
// This fails as well (of course previous line should be commented!)
assertEquals(0, zoneDateTimeEtcUtc.compareTo(zoneDateTimeUtc));
}
}
更具体地说,我希望java.lang.AssertionError:
Expected :2018-01-26T13:55:57.087Z[Etc/UTC]
Actual :2018-01-26T13:55:57.087Z[UTC]
等于ZoneId.of("UTC")
,但它们不是!{/ p>
作为@NicolasHenneaux suggested,我应该使用ZoneId.of("Etc/UTC")
方法。这是个好主意,但是compareTo(...)
会返回zoneDateTimeEtcUtc.compareTo(zoneDateTimeUtc)
值,因为-16
中的这个实现:
ZoneDateTime
断言结果:
cmp = getZone().getId().compareTo(other.getZone().getId());
所以问题出在java.lang.AssertionError:
Expected :0
Actual :-16
实施的某个地方。但我仍然希望如果两个区域ID都有效且两者都指定相同的区域,那么它们应该相等。
我的问题是:它是库错误,还是我做错了什么?
更新
有些人试图说服我这是正常的行为,并且正常比较方法的实现使用了ZoneId
id表示String
。在这种情况下,我应该问,为什么以下测试运行正常?
ZoneId
如果 @Test
public void compareUtc0WithUtc() {
ZonedDateTime now = ZonedDateTime.now();
ZoneId utcZone = ZoneId.of("UTC");
ZonedDateTime zonedDateTimeUtc = now.withZoneSameInstant(utcZone);
ZoneId utc0Zone = ZoneId.of("UTC+0");
ZonedDateTime zonedDateTimeUtc0 = now.withZoneSameInstant(utc0Zone);
// This is okay
assertEquals(Timestamp.from(zonedDateTimeUtc.toInstant()), Timestamp.from(zonedDateTimeUtc0.toInstant()));
assertEquals(0, zonedDateTimeUtc.compareTo(zonedDateTimeUtc0));
assertEquals(zonedDateTimeUtc,zonedDateTimeUtc0);
}
与Etc/UTC
相同,那么我会看到两个选项:
UTC
已损坏,应将Zone.of(...)
和Etc/UTC
视为相同的时区。 否则我不明白为什么UTC
和UTC+0
正常工作。
UPDATE-2 我已经报告了一个错误,ID:9052414。将会看到Oracle团队将会做出决定。
UPDATE-3 错误报告已被接受(不知道是否将其关闭为“无法修复”):https://bugs.openjdk.java.net/browse/JDK-8196398
答案 0 :(得分:7)
您可以将ZonedDateTime
个对象转换为Instant
,因为已经告知了其他答案/评论。
ZonedDateTime::isEqual
或者您可以使用isEqual
method,比较两个ZonedDateTime
个实例是否与同一个Instant
对应:
ZonedDateTime now = ZonedDateTime.now();
ZonedDateTime zoneDateTimeEtcUtc = now.withZoneSameInstant(ZoneId.of("Etc/UTC"));
ZonedDateTime zoneDateTimeUtc = now.withZoneSameInstant(ZoneId.of("UTC"));
Assert.assertTrue(zoneDateTimeEtcUtc.isEqual(zoneDateTimeUtc));
答案 1 :(得分:4)
班级ZonedDateTime
在其equals()
- 方法中使用内部ZoneId
- 成员的比较。所以我们在那个类中看到了(Java-8中的源代码):
/**
* Checks if this time-zone ID is equal to another time-zone ID.
* <p>
* The comparison is based on the ID.
*
* @param obj the object to check, null returns false
* @return true if this is equal to the other time-zone ID
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof ZoneId) {
ZoneId other = (ZoneId) obj;
return getId().equals(other.getId());
}
return false;
}
词汇表示“Etc / UTC”和“UTC”显然是不同的字符串,因此zoneId-comparison通常会产生false
。这种行为在javadoc中有描述,所以我们没有错误。我强调documentation:
比较基于ID。
也就是说,ZoneId
就像是指向区域数据的命名指针,并不代表数据本身。
但我认为您更愿意比较两个不同区域ID的规则而不是词法表示。然后问规则:
ZoneId z1 = ZoneId.of("Etc/UTC");
ZoneId z2 = ZoneId.of("UTC");
System.out.println(z1.equals(z2)); // false
System.out.println(z1.getRules().equals(z2.getRules())); // true
所以你可以使用区域规则和ZonedDateTime
的其他非区域相关成员的比较(有点尴尬)。
顺便说一下,我强烈建议不要使用“Etc /..."-标识符,因为(除了”Etc / GMT“或”Etc / UTC“)他们的偏移标志是反向的< / strong>比通常预期的要好。
关于ZonedDateTime
- 实例的比较的另一个重要评论。看这里:
System.out.println(zoneDateTimeEtcUtc.compareTo(zoneDateTimeUtc)); // -16
System.out.println(z1.getId().compareTo(z2.getId())); // -16
我们看到ZonedDateTime
- 具有相同即时和本地时间戳的实例的比较是基于zone-id的词汇比较。通常不是大多数用户所期望的。但这也不是错误,因为这种行为是described in the API。这是我不喜欢使用ZonedDateTime
类型的另一个原因。它本质上太复杂了。您应该只将它用于Instant
和本地类型恕我直言之间的中间类型转换。具体意味着:在比较ZonedDateTime
- 个实例之前,请将它们转换(通常转换为Instant
),然后进行比较。
为此问题打开的JDK-issue已被Oracle关闭为“不是问题”。
答案 2 :(得分:2)
type :: my_type1
real(KIND=DP), dimension(:), allocatable :: elem1 ! interest rate
contains
procedure, nopass :: getMean => getMean_my_type1
end type my_type1
interface assignment (=)
module procedure assignEachThatInThis_my_type1, assignThatInThis_my_type1
end interface
应转换为ZoneDateTime
,然后与OffsetDateTime
进行比较,以便比较时间。
compareTo(..)
和ZoneDateTime.equals
比较即时和区域标识符,即标识时区的第t个字符串。
ZoneDateTime.compareTo
是一个瞬间和一个区域(带有id而不仅仅是一个偏移量),而ZoneDateTime
是一个瞬间和一个区域偏移。如果要比较两个OffsetDateTime
对象之间的时间,则应使用ZoneDateTime
。
方法OffsetDateTime
正在解析您提供的字符串,并在需要时对其进行转换。 ZoneId.of(..)
表示时区而不是偏移量,即GMT时区的时移。虽然ZoneId
表示抵消。
https://docs.oracle.com/javase/8/docs/api/java/time/ZoneId.html#of-java.lang.String-
如果区域ID等于'GMT','UTC'或'UT',则结果是具有相同ID的ZoneId,并且规则等同于ZoneOffset.UTC。
如果区域ID以“UTC +”,“UTC-”,“GMT +”,“GMT-”,“UT +”或“UT-”开头,则ID是带前缀的基于偏移量的ID。 ID分为两部分,带有两个或三个字母前缀和以符号开头的后缀。后缀被解析为ZoneOffset。结果将是具有指定UTC / GMT / UT前缀的ZoneId和根据ZoneOffset.getId()的标准化偏移ID。返回的ZoneId的规则将等同于已解析的ZoneOffset。
所有其他ID都被解析为基于区域的区域ID。区域ID必须与正则表达式[A-Za-z] [A-Za-z0-9~ /._+-] +匹配,否则抛出DateTimeException。如果区域ID不在配置的ID集中,则抛出ZoneRulesException。区域ID的详细格式取决于提供数据的组。默认数据集由IANA时区数据库(TZDB)提供。这具有“{area} / {city}”形式的区域ID,例如“Europe / Paris”或“America / New_York”。这与TimeZone的大多数ID兼容。
所以ZoneOffset
转换为UTC+0
,而Etc / UTC不加修改
UTC
比较id(ZoneDateTime.compareTo
)的字符串。这就是你应该使用"Etc/UTC".compareTo("UTC") == 16
的原因。