为什么要获得具有相同时区的不同 localDate

Why get different localDate with same timezone

java.sql.Date date = java.sql.Date.valueOf("1900-01-01");
org.joda.time.LocalDate localDate = new org.joda.time.LocalDate(date);

根据上面的代码,我做了如下测试:

如果我将本地时区更改为 UTC+8 Singapore Timezone,将得到 1899-12-31

如果我将本地时区更改为 UTC+8 China Timezone,将得到 1900-01-01

如果我将本地时区更改为 UTC-8 America/Los_Angeles Timezone,将得到 1900-01-01

如果我将本地时区更改为 UTC+8 China Timezone,将得到 1900-01-01

有人可以帮忙澄清一下吗??有点困惑。

在 1900 年,Singpore's 偏移量是 UTC +6:55:25,所以当您在新加坡时区创建日期 1900-01-01 时,它应该是 1899-12-31T17:04:35Z[UTC],如下所示:

java.time.ZonedDateTime localDateTime = ZonedDateTime.of(1900, 01, 01, 0,
            0, 0, 0, ZoneId.of("Singapore"));

System.out.println(localDateTime.withZoneSameInstant(ZoneId.of("UTC")));
// 1899-12-31T17:04:35Z[UTC]

但是,当您使用 java.sql.Date 时,它使用了错误的偏移量 UTC +08:00:

TimeZone.setDefault(TimeZone.getTimeZone("Singapore"));
java.sql.Date date = java.sql.Date.valueOf("1900-01-01");

java.time.Instant instant = Instant.ofEpochMilli(date.getTime());
System.out.println(instant);   // 1899-12-31T16:00:00Z

并且当您使用此错误值创建 org.joda.time.LocalDateTime 时,Joda 使用 UTC +6:55:25 作为偏移量,导致日期时间 1899-12-31T16:00:00 + 6:55:25:

org.joda.time.LocalDateTime singaporeDateTime = new org.joda.time.LocalDateTime(date);
System.out.println(singaporeDateTime);  // 1899-12-31T22:55:25.000 

您可以使用类似的方法查看上海和洛杉矶的结果,重点是,避免使用 java.sql.Date 和其他已弃用的日期时间相关 类。如果您使用 java8 或更高版本,请改用 java.time

tl;博士

java.time.LocalDate
.of( 
    1900 , 
    Month.JANUARY , 
    1 
) ;

避免遗留日期时间 classes

您使用的是一个糟糕的旧 class java.sql.Date,它的设计很糟糕,有缺陷的黑客攻击。它的问题之一是它 假装 表示仅日期值但实际上包含一天中的时间。它甚至在内部深处拥有一个时区,尽管没有 getter 或 setter。 切勿使用 class. 多年前由 java.time.LocalDate class.

补充

Joda-Time 项目也被 java.time classes 取代。该项目处于维护模式,接收更新和关键错误修复,但没有新功能工作。该项目建议迁移到 java.time classes。迁移很容易,因为他们有相似的概念,两者都由同一个人 Stephen Colebourne 领导。

java.time.LocalDate

如果您想要一个没有时间和时区的纯日期值,请使用 java.time.LocalDate。您在问题中提出的所有问题都会消失。

java.time.LocalDate ld = LocalDate.of( 1900 , Month.JANUARY , 1 ) ;  // Simply a year-month-day date, no problems, no issues, no confusion. 

ld.toString(): 1900-01-01

另请注意,java.time 使用合理的编号,其中 2018 表示 2018 年,1-12 表示一月至十二月。这与麻烦的遗留 classes 形成鲜明对比。


关于java.time

java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

要了解更多信息,请参阅 Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310

您可以直接与数据库交换 java.time 对象。使用 JDBC driver compliant with JDBC 4.2 或更高版本。不需要字符串,不需要 java.sql.* classes.

从哪里获得java.time classes?

ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.