tags:

views:

416

answers:

7

From a given Date I need to calculate midnight of its day. Here's what I came up with. It's so ugly that I figure there must be a better way.

private Date day(Date creation) {
 Calendar calendar = Calendar.getInstance();
 calendar.setTime(creation);
 calendar.set(Calendar.HOUR_OF_DAY, 0);
 calendar.set(Calendar.MINUTE, 0);
 calendar.set(Calendar.SECOND, 0);
 calendar.set(Calendar.MILLISECOND, 0);
 return calendar.getTime();
}

Suggestions?

Kent

+3  A: 

If you're looking for local time, that's the best you're going to get. And although you may consider it ugly, there's (potentially) a lot going on behind the scenes.

If you want midnight UTC, the following will work:

public static void main(String[] argv)
throws Exception
{
    final long MILLIS_PER_DAY = 24 * 3600 * 1000L;

    long midnightUTC = (System.currentTimeMillis() / MILLIS_PER_DAY) * MILLIS_PER_DAY;
}

Edit: I really don't recommend using local dates in "production" code. They cause more trouble than they're worth -- consider a person on the US west coast who suddenly finds his/her "day" trimmed by 3 hours because an east coast computer has a different idea of midnight.

kdgregory
+5  A: 

JODA might have a better solution, at the cost of another dependency on a library. I'm looking at its DateMidnight property. I'm sorry that I can't answer more authoritatively, but I'm not a JODA user myself.

duffymo
A: 

I think the only way you're going to make this code significantly better is by a change in representation. For example,

  • Represent the day by the "absolute date" system explained by Nachum Dershowitz and Ed Reingold in Calendrical Calculations.

  • Represent the time by counting seconds from midnight. Or if you need sub-second resolution, count milliseconds.

From your brief example I can't tell at what points in your program you need to be compatible with existing Date and Calendar classes, or if you can create a useful subclass using a saner representation.

Norman Ramsey
A: 

Haven't programmed Java in a while, but you might be able to call this set method passing in the current values for year, month, and day. Although from reading a little close, you may have to call clear first. This may or may not be less verbose than using your current method.

Kibbee
+3  A: 

You should consider the built-in date API obselete. Instead use the Joda date and time API.

Here's a drop-in replacement for your method.

private Date day(Date creation) {
    return new DateMidnight(creation).toDate();
}

Here's a simple test:

public static void main(String[] args) {
    final Date creation = new Date();
    final Date midnight = new Foobar().day(creation);
    System.out.println("creation = " + creation);
    System.out.println("midnight = " + midnight);
}

The output is:

creation = Sun May 31 10:09:38 CEST 2009    
midnight = Sun May 31 00:00:00 CEST 2009
Steve McLeod
+1  A: 

JODA is the way to go if you have serious calendar needs, at least until JSR-310 gets into the JDK (1.7 maybe, if not 1.8).

That being said, there are a couple of things that could be done to make this code a little nicer.

import static java.util.Calendar.*;

...

private static final List<Integer> TIME_FIELDS = 
    Arrays.asList(HOUR_OF_DAY, MINUTE, SECOND, MILLISECOND);

private Date day(Date creation) {
     Calendar c = getInstance();
     c.setTime(creation);
     for(int field : TIME_FIELDS) c.set(field, 0);
     return c.getTime();
}

That won't win any performance awards. You could do a standard for loop relying on the specific field values (the Calendar class has a FIELD_COUNT field kind of implying you can do something like that) but that risks issues across JDK implementations and between versions.

Yishai
A: 

This is just the complement of the originally posted code. It sets the Year, Month, and Day in a new GregorianCalendar object, instead of clearing everything but the Year, Month, and Day in the calendar variable. It's not much of an improvement, but I think it's clearer to say which fields you are copying, instead of which fields you're ignoring.

public Date day(Date creation) {
 Calendar calendar = Calendar.getInstance();
 calendar.setTime(creation);
 return new GregorianCalendar(
   calendar.get(Calendar.YEAR),
   calendar.get(Calendar.MONTH),
   calendar.get(Calendar.DAY_OF_MONTH)).getTime();
}

Personally, I think I'd go with the Joda library proposed by others.

Don Kirkby