Reputation: 8833
So as in the title, in Java, if I do
System.out.print(new java.util.Date(Long.MAX_VALUE));
System.out.print(new java.util.Date(0));
System.out.print(new java.util.Date(-86400000));
System.out.print(new java.util.Date(Long.MIN_VALUE));
I get as output
Sun Aug 17 02:12:55 EST 292278994
Wed Dec 31 19:00:00 EST 1969
Tue Dec 30 19:00:00 EST 1969
Sun Dec 02 11:47:04 EST 292269055
So year values go Long.MAX_VALUE > Long.MIN_VALUE > 0 > -86400000
? What happened? Why isn't Date(Long.MIN_VALUE)
a negative year?
Searching, I wasn't able to find any information related to this, and the Java documentation doesn't mention anything about it.
I am using Java 8.
Also, executing the following
(new java.util.Date(Long.MIN_VALUE).getYear() >= new java.util.Date(0).getYear())
returns true
, but
(new java.util.Date(Long.MIN_VALUE).after(new java.util.Date(0)))
returns false
?
Upvotes: 2
Views: 2305
Reputation: 48297
First things first: The following
Long.MIN_VALUE > -86400000
evaluates to false
.
On the other hand, negative values for the constructor in the Date class mean dates in the calendar before "the epoch", namely January 1, 1970, 00:00:00 GMT.
So a date constructed with Long.MIN_VALUE
is a way back in the past. Changing the way you print the Date
may help you to spot the issue…
System.out.println(new SimpleDateFormat("dd.MM.yyyy GG HH:mm:ss.SSS").format(new Date(Long.MAX_VALUE / 1000)));
System.out.println(new SimpleDateFormat("dd.MM.yyyy GG HH:mm:ss.SSS").format(new Date(0)));
System.out.println(new SimpleDateFormat("dd.MM.yyyy GG HH:mm:ss.SSS").format(new Date(-86400000)));
System.out.println(new SimpleDateFormat("dd.MM.yyyy GG HH:mm:ss.SSS").format(new Date(Long.MIN_VALUE)));
System.out.println(Long.MIN_VALUE > -86400000);
So, new Date(Long.MIN_VALUE)
is a date before Christ.
The formatter patterns are very helpful to identify such spots.
In this case G means era designator, and all the pattern designators are here.
Upvotes: 4
Reputation: 8833
OK, so digging through Date
source, getYear()
is
/**
* Returns the difference between the year represented by this
* <code>Date</code> object and 1900.
*
* @return the year minus 1900 represented by this date object.
* @deprecated Use Calendar instead of Date, and use get(Calendar.YEAR)
* instead. Note the 1900 difference in the year.
* @see Calendar
* @see #setYear(int)
*/
public int getYear() {
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(time);
return cal.get(Calendar.YEAR) - 1900;
}
So, Date.getYear()
is actually Calendar.getYear() - 1900
. (Why -1900
?)
Subtract 1900 why? Between 1960 and 1995 years were usually written with just two digits, e.g., 60 or 95. When Java came out, the ”year 2000 problem“ arising from this fact was being talked about, so they didn’t want to return just year % 100, the last two digits. So they figured subtracting 1900 would still allow you to figure out the century, and everyone should be happy. Well, we aren`t really. :-) – Ole V.V.
Calendar.getInstance()
(source) defaults to java.util.GregorianCalendar
(source).
So, in the Calendar
abstract…
public void setTimeInMillis(long time) {
clear();
this.time = time;
isTimeSet = true;
computeFields();
}
And computeFields()
is long and complicated, but tracing it long enough. you find
if (year <= 0) {
fields[ERA] = BC;
fields[YEAR] = 1 - year;
} else {
fields[ERA] = AD;
fields[YEAR] = year;
}
SO! Long story short, Date
uses the GregorianCalendar
, which displays negative years as a positive number, but Date
doesn't have a way to check the Calendar
era… So don't use Date
for dates before the year 1 AD. And this is where encapsulation fails; you expect 1 behaviour, but inherit another that you are never told about! (I feel stupid now, but at least I wasn't the only one confused about this behaviour.)
Upvotes: 3
Reputation: 340070
The other Answers are correct. A few more notes here.
Why is new java.util.Date(Long.MIN_VALUE) in Java
New? Not new at all. The java.util.Date
class is very old, bundled with the original release of Java.
That troublesome old class is now legacy, supplanted by the java.time classes. Specifically, the Instant
class. To convert to/from, see new methods added to the old class.
As for the maximum/epoch/minimum values, those are defined as constants.
Instant.MAX
Instant.EPOCH
Instant.MIN
Beware of making any practical use of the minimum or maximum value.
If your intent is for something like meaning "unknown value", use another value. Many other tools such as databases will have very different limits, so you may run into problems. Also, such extreme values may not have any recognizable meaning to future readers/programmers/admins. Such values may even be mistaken for an error or bug.
If working with only recent dates, I would use the Instant.EPOCH
for such a flag. The start of 1970 is recognizable by many technical folks as being special.
The strings seen above are all in standard ISO 8601 format.
The java.time classes use these standard formats by default when parsing/generating strings.
To generate strings in other formats, see the DateTimeFormatter
class.
I am using Java 8.
Then you should definitely be migrating from the old classes to modern java.time classes. They are an enormous improvement.
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.
With a JDBC driver complying with JDBC 4.2 or later, you may exchange java.time objects directly with your database. No need for strings or 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.
Upvotes: 2
Reputation: 74098
I found a possible implementation of java.util.Date
where the final two lines of toString()
are
1029 sb.append(' ').append(date.getYear()); // yyyy
1030 return sb.toString();
So toString
relies on getYear
, which in turn gives the observed behaviour, e.g.
Date d = new Date(Long.MIN_VALUE);
System.out.println(d.getYear());
will indeed print
292267155
Upvotes: 1