Kent Beck
Kent Beck

Reputation: 5011

Calculating a day given a Date

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

Upvotes: 5

Views: 1832

Answers (8)

John
John

Reputation: 1673

Using the date4j library :

DateTime start = dt.getStartOfDay();

Upvotes: 1

Don Kirkby
Don Kirkby

Reputation: 56230

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.

Upvotes: 0

Yishai
Yishai

Reputation: 91931

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.

Upvotes: 1

Steve McLeod
Steve McLeod

Reputation: 52488

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

Upvotes: 7

Kibbee
Kibbee

Reputation: 66162

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.

Upvotes: 0

Norman Ramsey
Norman Ramsey

Reputation: 202695

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.

Upvotes: 0

duffymo
duffymo

Reputation: 309008

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.

Upvotes: 5

kdgregory
kdgregory

Reputation: 39606

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.

Upvotes: 2

Related Questions