Reputation: 5011
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
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
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
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
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
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
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
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