new Date( y , m , d )
uses default time zone
Some of the other Answers are correct and very useful. But I want to make very plain and simple where your code went wrong:
➥ The deprecated constructor of java.util.Date
for year-month-day arguments implicitly applies your JVM’s current default time zone.
Take the first part of the key line in your code:
new Date(2020 - 1900, 0, 1).toInstant()
… where an Instant
is always in UTC (an offset of zero hours-minutes-seconds), by definition. On my machine the current default time zone in my JVM is America/Los_Angeles
. On your date and time, this zone was eight hours behind UTC.
So let's try these three lines of code code:
System.out.println(
ZoneId.systemDefault()
);
System.out.println(
new Date(2020 - 1900, 0, 1)
);
System.out.println(
new Date(2020 - 1900, 0, 1).toInstant()
);
When run, we see indeed that the moment represented by new Date
is the first moment of that day as seen in the time zone America/Los_Angeles
, colloquially known as PST
. That zone on that date is eight hours behind UTC. We can see this fact in the third line, when calling toInstant
has adjusted to UTC where the time-of-day is 8 AM.
America/Los_Angeles
Wed Jan 01 00:00:00 PST 2020
2020-01-01T08:00:00Z

Avoid Date
In the bigger picture, stop using Date
class!
There are no benefits to be had by studying the behavior of java.util.Date
. That class is absolutely terrible, written by people who did not understand date-time handling. Along with Calendar
, java.sql.Date
, and SimpleDateFormat
, these classes should never be used.
These legacy classes were supplanted years ago by the java.time classes, defined in JSR 310. Sun, Oracle, and the JCP community unanimously gave up on these classes. So should you.