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

Based above code, I did some tests as below:

  • Question #1 why do I get different localDate object with same timezone?

If I change local timezone to UTC+8 Singapore Timezone, will get 1899-12-31

If I change local timezone to UTC+8 China Timezone, will get 1900-01-01

  • Question #2 why do I get same time with different timezone?

If I change local timezone to UTC-8 America/Los_Angeles Timezone, will get 1900-01-01

If I change local timezone to UTC+8 China Timezone, will get 1900-01-01

Could someone help clarify that?? it is little bit confused.

xingbin
  • 27,410
  • 9
  • 53
  • 103
Haven Lin
  • 176
  • 1
  • 16
  • I think this link help you : https://stackoverflow.com/questions/47128190/format-a-local-date-at-another-time-zone – Feras Al Sous Sep 03 '18 at 14:26
  • how did you `change local timezone`? – xingbin Sep 03 '18 at 14:41
  • @Sun I changed timezone with "Date & Time Preference"(Mac) in local machine – Haven Lin Sep 03 '18 at 15:29
  • The reason for the confusion is that Singapore has changed its time zone six times in history, I happen to be using the time of my example when the Singapore time zone was UTC+06:55 between the 1900 and 1924, because I always thought China and Singapore were the same time zone(UTC+8) – Haven Lin Sep 03 '18 at 17:24
  • @HavenLin That answer was assuming that you were constructing `org.joda.time.LocalDate` with specific time zone. I updated it. – xingbin Sep 03 '18 at 18:42

2 Answers2

5

In 1900, Singpore's offset is UTC +6:55:25, so when you create date 1900-01-01 in Singapore time zone, it should be 1899-12-31T17:04:35Z[UTC] as below:

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]

However, when you use java.sql.Date, it use wrong offset 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

and when you create org.joda.time.LocalDateTime with this wrong value, Joda use UTC +6:55:25 as offset, resulting in a date time 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 

You can use the similar approach to check the result of Shanghai and Los Angelas, the point is, avoid using java.sql.Date and other deprecated date time related classes. Use java.time instead if you are working with java8 or higher.

xingbin
  • 27,410
  • 9
  • 53
  • 103
  • When using Java 6 or Java 7, use the [*ThreeTen-Backport*](http://www.threeten.org/threetenbp/) project rather than the terrible legacy classes. Most of the *java.time* functionality was successfully back-ported. Nearly identical API allows you to eventually migrate to *java.time* by doing little more than change `import` statements. – Basil Bourque Sep 03 '18 at 20:06
  • @Sun I agree with you, In 1900, `java.sql.Date` uses wrong offset `UTC+8` for Singapore timezone – Haven Lin Sep 03 '18 at 20:37
2

tl;dr

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

Avoid legacy date-time classes

You are using a terrible old class java.sql.Date that was badly designed with flawed hacks. Among its problems is that it pretends to represent a date-only value but actually holds a time-of-day. It even holds a time zone deep inside, though without getter or setter. Never use this class. Supplanted years ago by the java.time.LocalDate class.

The Joda-Time project too is supplanted by the java.time classes. The project is in maintenance mode, receiving updates and critical bug fixes but no new feature work. The project advises migration to the java.time classes. Migration is easy as they share similar concepts, both having been led by the same man, Stephen Colebourne.

java.time.LocalDate

Use java.time.LocalDate when you want a date-only value without time-of-day and without time zone. All your problems raised in your Question go away.

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

Also notice that java.time uses sane numbering where 2018 means the year 2018, and 1-12 means January-December. This is in distinct contrast to the troublesome legacy classes.


About java.time

The 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.

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

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.

Where to obtain the java.time classes?

The 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.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154