4

I came across an ancient piece of code by someone who doesn't work in the company anymore.

I'm wondering what the purpose is of this calendar magic:

if (value instanceof Date) {
    Calendar calendar = Calendar.getInstance();
    calendar.setTime((Date) value);
    return new Date(calendar.get(Calendar.YEAR) - 1900, calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH));
}

It seems to me this would return a new Date object with the same values as the original value. Is there some Calendar initialization going on that I'm missing? If the purpose is to return a new object with the same value, I would assume value.clone() does the job:

(Date) originalDate.clone()
// or
new Date(originalDate.getTime())

Version control shows the code never changed. Are there possible side effects of pulling the date through a Calendar first?

mhasan
  • 3,703
  • 1
  • 18
  • 37
Benny Bottema
  • 11,111
  • 10
  • 71
  • 96
  • 5
    It's not an exact copy since `Date` contains the time of day as well whereas the code above only considers year, month and day of month. – Thomas Oct 06 '16 at 11:00
  • 4
    No, it won't result in an equal `Date` instance. It will be at midnight and the year will be less by 1900. It smells like very poor design. – Marko Topolnik Oct 06 '16 at 11:00
  • 3
    @MarkoTopolnik The `-1900` part is actually because of `Date`'s weirdness. The code is correct on that part. – Kayaman Oct 06 '16 at 11:01
  • 2
    Don't assume that code you find on the web or elsewhere is logical - unfortunately, people often write something that seems to do what they want but they don't understand what they are writing themselves. This is especially true with Java `Date` and `Calendar`, which seems to be a confusing subject for many programmers. – Jesper Oct 06 '16 at 11:02
  • 1
    I guess "ancient" is part of the answer, i.e. it might be from the time where that `Date` constructor wasn't deprecated (which would mean _really ancient_ :) ). – Thomas Oct 06 '16 at 11:04
  • @Thomas to be fair, lots of people still use the constructor *even though* it's deprecated. – rorschach Oct 06 '16 at 11:06
  • @Thomas If the purpose was to get rid of the timestamp, I would've expected `return new Date(oldDate.getYear(), ...)`. – Benny Bottema Oct 06 '16 at 11:08
  • Yeah I might expect the same. I can't tell why the code has been written that way (maybe the programmer was told to use that fancy new "Calendar" ;) ), I just can tell what it does. – Thomas Oct 06 '16 at 11:10
  • @Kayaman clone can may cause some issues , you can read this http://stackoverflow.com/questions/869033/how-do-i-copy-an-object-in-java – Pavneet_Singh Oct 06 '16 at 11:11
  • 2
    @PavneetSingh you're basically right but `Date.clone()` won't just do a shallow copy if you have a look at the sources. – Thomas Oct 06 '16 at 11:13

2 Answers2

3

It is nothing more than an outdated way to remove hours, minutes and seconds from your initial date.

As you can see in this simple example:

Date value = new Date();
System.out.printf("Before %s%n", value);
Calendar calendar = Calendar.getInstance();
calendar.setTime(value);
System.out.printf(
    "After %s%n", 
    new Date(
        calendar.get(Calendar.YEAR) - 1900, 
        calendar.get(Calendar.MONTH), 
        calendar.get(Calendar.DAY_OF_MONTH)
    )
);

Output:

Before Thu Oct 06 11:19:26 GMT 2016
After Thu Oct 06 00:00:00 GMT 2016
Nicolas Filotto
  • 43,537
  • 11
  • 94
  • 122
  • 2
    This is a way to remove the timestamp, yes, but if that was its purpose, it could be done with just Date rather than involve Calendar. `return new Date(oldDate.getYear(), ...)`. I'm wondering if the code is relying on side-effects of the Calendar. – Benny Bottema Oct 06 '16 at 11:21
  • I don't believe so, it just seem to be a misuse of `Calendar` and `Date`. You should not overthink it knowing that they are both *out dated* now with the arrival of the `java.time` package – Nicolas Filotto Oct 06 '16 at 11:29
1

The Answer by Filotto is correct, and should be accepted.

That code is trying to clear the time-of-day to 00:00:00. But it is an abuse of the classes, ignores crucial issue of time zone, and uses the notoriously troublesome old date-time classes. Those old classes are now legacy, supplanted by the java.time classes.

The programmer probably wanted either:

  • Date-only value
  • First moment of the day

LocalDate

The LocalDate class represents a date-only value without time-of-day and without time zone.

A time zone is crucial in determining a date. For any given moment, the date varies around the globe by zone. For example, a few minutes after midnight in Paris France is a new day while still “yesterday” in Montréal Québec.

ZoneId z = ZoneId.of( “America/Montreal” );
LocalDate today = LocalDate.now( z );

today.toString(): 2016-01-23

ZonedDateTime

For a date-time value start by getting the current date as a LocalDate object as seen above. Then call atStartOfDay to get the first moment of the day. Do not assume that first moment is the time 00:00:00. Anomalies such as Daylight Saving Time (DST) means the first moment may be something like 01:00:00. So let java.time determine that time-of-day.

ZonedDateTime todayStart = today.atStartOfDay( z );

today.toString(): 2016-01-23T00:00:00-05:00[America/Montreal]

Tip: Never try to determine the last moment of the day. Always use the first moment of the following day. Search Stack Overflow for "Half-Open" for more info.

Instant

Much of your work should be done in UTC. To move your ZonedDateTime into UTC, extract an Instant. The Instant class represents a moment on the timeline in UTC with a resolution of nanoseconds (up to nine (9) digits of a decimal fraction).

Instant instant = zdtStart.toInstant();

Conversion

You should avoid the legacy date-time classes. But if you must interoperate with some old code not yet updated to the java.time types, you can convert to/from java.time. Look for new methods added to the old classes.

java.util.GregorianCalendar gc = GregorianCalendar.from( zdtStart );
java.util.Date d = Date.from( instant );

About java.time

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

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

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

Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport and further adapted to Android in ThreeTenABP (see How to use…).

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.

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