LocalTime.MIDNIGHT vs. LocalTime.MIN - is there any difference? - java

I recently answered some questions using LocalDate.atStartOfDay() and LocalDate.atTime(LocalTime.MIN).
I was wondering why there is no LocalDate.atEndOfDay() or similar, so one has to use LocalDate.atTime(LocalTime.MAX) in order to get the very last moment (in nanos, I think) of that specific day.
I had a look at the source of LocalDate and LocalTime and got slightly confused by this:
/**
* Combines this date with the time of midnight to create a {#code LocalDateTime}
* at the start of this date.
* <p>
* This returns a {#code LocalDateTime} formed from this date at the time of
* midnight, 00:00, at the start of this date.
*
* #return the local date-time of midnight at the start of this date, not null
*/
public LocalDateTime atStartOfDay() {
return LocalDateTime.of(this, LocalTime.MIDNIGHT);
}
Contrary to my expectation, this method returns a LocalDateTime using LocalTime.MIDNIGHT instead of LocalTime.MIN.
Of course, I opened the OpenJDK source of LocalTime and was sure to find out the difference myself, but I found out there is no difference apart from the name of the constant:
/**
* Constants for the local time of each hour.
*/
private static final LocalTime[] HOURS = new LocalTime[24];
static {
for (int i = 0; i < HOURS.length; i++) {
HOURS[i] = new LocalTime(i, 0, 0, 0);
}
MIDNIGHT = HOURS[0]; // <--- == MIN
NOON = HOURS[12];
MIN = HOURS[0]; // <--- == MIDNIGHT
MAX = new LocalTime(23, 59, 59, 999_999_999);
}
While I totally understand the presence of NOON and MAX, I don't really get why there are MIN and MIDNIGHT when obviously one of them would be enough since they have the very same value.
Can anyone tell me the reason why...
... there are two constants having the very same value and
... why the code uses MIDNIGHT for the start of a day?
Is it just for having something more readable in some situations?
But why isn't MIN used in LocalTime.atStartOfDay() but rather LocalTime.MIDNIGHT?

MIN exists to provide the minimum value, which is consistent with other java.time.* classes.
MIDNIGHT exists to provide semantic meaning to developers, and as a place to indicate to Javadoc readers that midnight is considered to be at the start of the day (not the end).
Summary, the semantic benefits in code reading outweigh the cost of the extra constant.
(Source: I'm the main java.time.* author)

Related

Strip seconds and milliseconds from System.currentTimeMillis value

Suppose I have a System.currentTimeMillis() value as a long number.
How do I modify it to match the instant when last minute started? I.e., zero out seconds and milliseconds.
I would prefer to not use magic constants. Using java.time is fine.
I agree with the answers recommending java.time, but it can be done yet simpler as in those answers:
long lastWholeMinute = Instant.now().truncatedTo(ChronoUnit.MINUTES).toEpochMilli();
This just gave 1517940060000. Of course, if it makes sense for you to keep the Instant object, by all means do that rather than converting to a naked primitive long.
If your long value was one you had stored rather than the time now, it’s quite similar:
long someEpochMilliValue = 1_517_941_234_567L;
long lastWholeMinute = Instant.ofEpochMilli(someEpochMilliValue)
.truncatedTo(ChronoUnit.MINUTES)
.toEpochMilli();
Using java.time is probably the easiest way. You could use withNano and withSecond, like
java.time.ZonedDateTime zdt = java.time.ZonedDateTime.now().withNano(0).withSecond(0);
long millis = zdt.toInstant().toEpochMilli();
Since the value is in milliseconds, if we assume an idealized day (no leap seconds, etc.), then given l you could do it by simply removing the value of l % 60000L from it. I realize that's a magic constant, but it's truly a constant, there are always going to be 60,000 milliseconds in a minute. I'd give it symbolic name:
private static long SIXTY_SECONDS_IN_MS = 60000L;
and not worry about it. Then it's:
long l = /*...your number...*/;
l = l - (l % SIXTY_SECONDS_IN_MS);
Why this works: The Epoch value is from midnight Jan 1st 1970, and so at 0L, 60000L, 120000L, etc., the seconds and milliseconds of an idealized day based on that value are 0. So we use the remainder operator (%) to isolate the part of the value that would remain if we divided by 60000L and remove it. Thus the resulting value, again assuming idealized days, has 0 for seconds and milliseconds. It also works across timezones if we assume all timezones are going to be at whole-minute offsets to UTC. I've only ever heard of timezones that were multiples of hours or half-hours offset from UTC ("GMT plus five hours", "GMT plus 5.5 hours"), never (say) "GMT plus five hours seven minutes and 20 seconds". (And indeed, the standard notation for timezome offsets, +0600 or similar, only includes hours and minutes, not fractional minutes.)
Live Example:
import java.time.*;
public class Example
{
private static long SIXTY_SECONDS_IN_MS = 60000L;
public static void main (String[] args) throws java.lang.Exception
{
long l = System.currentTimeMillis();
l = l - (l % SIXTY_SECONDS_IN_MS);
System.out.println("l = " + l);
// Checking the result
LocalDateTime dt = Instant.ofEpochMilli(l).atZone(ZoneId.systemDefault()).toLocalDateTime();
System.out.println(dt);
System.out.println(dt.getSecond()); // 0
System.out.println(dt.getNano()); // 0
}
}
Still, though, if that constant violates the terms of the question such that you think I shouldn't have answered, let me know and I'll delete the answer. :-)

JodaTime Interval irrespective of the year?

I'd like to have a JodaTime Interval which represents a range of days within a year. For example, January 21 - February 23 or the like, or even December 7 - January 13. Now I'd like to figure out if a given DateTime falls within that range of the year, regardless of the particular year.
Unfortunately, Interval#contains doesn't seem to work this way. For example, January 7, 2013 might match, but January 7, 1863 will not. Is there any workaround or another bit of the API I can use?
I don't believe there's any such type within Joda Time - and Interval deals with instants, where it sounds like you're interested in day/month values anyway.
You should probably construct your own type that is composed of two MonthDay fields.
Then to determine whether a particular value is within that range, extra the MonthDay for that value, and compare the three values to each other.
For example:
// Note: this assumes you always want end to be exclusive, and start to be inclusive.
// You may well want to make end inclusive instead; it depends on your use case.
public final class MonthDayInterval {
private final MonthDay start;
private final MonthDay end;
public MonthDayInterval(MonthDay start, MonthDay end) {
this.start = start;
this.end = end;
}
public boolean contains(DateTime dateTime) {
MonthDay monthDay =
return contains(new MonthDay(
dateTime.getMonthOfYear(), dateTime.getDayOfMonth());
}
public boolean contains(MonthDay monthDay) {
boolean natural = start.compareTo(monthDay) <= 0 && monthDay.compareTo(end) < 0;
// We need to invert the result if end is after or equal to start.
return start.compareTo(end) < 0 ? natural : !natural;
}
}

Increment existing date by 1 day [duplicate]

This question already has answers here:
Closed 12 years ago.
Possible Duplicate:
How can I increment a date by one day in Java?
I have an existing date object that I'd like to increment by one day while keeping every other field the same. Every example I've come across sheds hours/minutes/seconds or you have to create a new date object and transfers the fields over. Is there a way you can just advance the day field by 1?
Thanks
EDIT: Sorry i didn't mean increment the value of the day by one, i meant advance the day forward by 1
Calendar c = Calendar.getInstance();
c.setTime(yourdate);
c.add(Calendar.DATE, 1);
Date newDate = c.getTime();
The Date object itself (assuming you mean java.util.Date) has no Day field, only a "milliseconds since Unix Epoch" value. (The toString() method prints this depending on the current locale.)
Depending of what you want to do, there are in principle two ways:
If you want simply "precisely 24 hours after the given date", you could simply add 1000 * 60 * 60 * 24 milliseconds to the time value, and then set this. If there is a daylight saving time shift between, it could then be that your old date was on 11:07 and the new is on 10:07 or 12:07 (depending of the direction of shift), but it still is exactly 24 hours difference.
private final static long MILLISECONDS_PER_DAY = 1000L * 60 * 60 * 24;
/**
* shift the given Date by exactly 24 hours.
*/
public static void shiftDate(Date d) {
long time = d.getTime();
time += MILLISECONDS_PER_DAY;
d.setTime(time);
}
If you want to have "the same time on the next calendar day", you better use a Calendar, like MeBigFatGuy showed. (Maybe you want to give this getInstance() method the TimeZone, too, if you don't want your local time zone to be used.)
/**
* Shifts the given Date to the same time at the next day.
* This uses the current time zone.
*/
public static void shiftDate(Date d) {
Calendar c = Calendar.getInstance();
c.setTime(d);
c.add(Calendar.DATE, 1);
d.setTime(c.getTimeInMillis());
}
If you are doing multiple such date manipulations, better use directly a Calendar object instead of converting from and to Date again and again.
org.apache.commons.lang.time.DateUtils.addDays(date, 1);

Adding a number of days to a JodaTime Instant

I'm trying to write a simple utility method for adding aninteger number of days to a Joda time instant. Here is my first stab.
/**
* Adds a number of days specified to the instant in time specified.
*
* #param instant - the date to be added to
* #param numberOfDaysToAdd - the number of days to be added to the instant specified
* #return an instant that has been incremented by the number of days specified
*/
public static Instant addNumberOfDaysToInstant(final Instant instant, final int numberOfDaysToAdd) {
Days days = Days.days(numberOfDaysToAdd);
Interval interval = new Interval(instant, days);
return interval.getEnd().toInstant();
}
This works fine for the most part except when you consider the example when the number of days added takes you across the BST / GMT boundary. Here is a small example.
public class DateAddTest {
/**
* Zone to use for input and output
*/
private static final DateTimeZone ZONE = DateTimeZone.forId("Europe/London");
/**
* Formatter used to translate Instant objects to & from strings.
*/
private static final DateTimeFormatter FORMATTER = DateTimeFormat.forPattern(DATE_FORMAT).withZone(ZONE);
/**
* Date format to be used
*/
private static final String DATE_FORMAT = "dd/MM/yyyy";
public static void main(String[] args) {
DateTime dateTime = FORMATTER.parseDateTime("24/10/2009");
Instant toAdd = dateTime.toInstant();
Instant answer = JodaTimeUtils.addNumberOfDaysToInstant(toAdd, 2);
System.out.println(answer.toString(FORMATTER)); //25/10/2009
}
}
I think this problem is because the interval does not take into acount the fact that it has crossing the bst boundary. Any ideas of a better way to implement this would be appreciated.
If you want to deal with dates, don't use instants. I suspect it's correctly adding 48 hours to the instant.
Use a LocalDate instead, and then the plusDays method.
If you want to know the instant that occurs n days after the specified instant, at the same time of day, we could no doubt work out a way of doing that (split the instant into a LocalDate and a LocalTime, advance the LocalDate and then reassemble, or check whether LocalDateTime does what you want) but you need to work out what you want to happen if the original time occurs twice on the new day, or doesn't occur at all.
EDIT: Okay, so you need to work with an instant. Does that have to be in an original time zone? Could you use UTC? That would take away the DST issues. If not, what do you want it to do in cases of ambiguity or non-existence (e.g. at 12.30am before each of the transitions).
Assuming the rest of your code:
public static void main(String[] args) {
DateTime dateTime = FORMATTER.parseDateTime("24/10/2009");
Instant pInstant = dateTime.withFieldAdded(DurationFieldType.days(),2).toInstant();
System.out.println("24/10/2009 + 2 Days = " + pInstant.toString(FORMATTER));
}
This is the solution that was chosen.
/**
* Zone to use for input and output
*/
private static final DateTimeZone ZONE = DateTimeZone.forId("Europe/London");
/**
* Adds a number of days specified to the instant in time specified.
*
* #param instant - the date to be added to
* #param numberOfDaysToAdd - the number of days to be added to the instant specified
* #return an instant that has been incremented by the number of days specified
*/
public static Instant addNumberOfDaysToInstant(final Instant instant, final int numberOfDaysToAdd) {
return instant.toDateTime(ZONE).withFieldAdded(DurationFieldType.days(), numberOfDaysToAdd).toInstant();
}

Android java.util.Calendar - Time Difference

I want to make calendar view in order to support touch interaction.
So I'd like to build new custom calendar view.
I tried to make mapping function between view offset and real date value.
Here is my idea:
If I can compute the number of weeks since base date(in my case, 1989-12-31),
it is easy to know offset. HEIGHT_FOR_WEEK * NUM_OF_WEEK is very simple computation
to know exact offset.
My problem is this:
First I got milliseconds value from base date. And I set the milliseconds to
another calendar object. I expected same date from that object. But actually
it was different date.
mBaseDateInMillis = mBaseDate.getTimeInMillis();
mAnotherDate.setTimeInMillis(mBaseDateInMillis);
/* I expect mBaseDate == mAnotherDate.
* but it was different.
*/
Here is my code:
public class CalendarCoordinate {
public static final long ONEWEEK_IN_MILLISECONDS = 60 * 60 * 24 * 7 * 1000;
public Calendar mBaseDate = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
public long mBaseDateInMillis = 0;
public Calendar mDate = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
public int mWeekHeight = 30;
/**
* CTOR
*/
public CalendarCoordinate() {
/* Base date is 1989-12-31 0, 0, 0
* It was Sunday and offset 0 will be mapped onto this day.
*/
mBaseDate.set(Calendar.MILLISECOND, 0);
mBaseDate.set(1989, 12, 31, 0, 0, 0);
mBaseDateInMillis = mBaseDate.getTimeInMillis();
Log.v(TAG, "BaseDate:" + mBaseDate.toString());
}
/**
* Compute DATE from Y-Offset
* #param yOffset
* #return
*/
public Calendar dateFromYOffset(int yOffset) {
long nthWeeks = yOffset / mWeekHeight;
long millsSinceBaseDate = nthWeeks * ONEWEEK_IN_MILLISECONDS;
mDate.clear();
mDate.set(Calendar.MILLISECOND, 0);
mDate.setTimeInMillis(mBaseDateInMillis + millsSinceBaseDate);
/* We SHOULD call to update mDate internal data structure.
* Java is really strange for this thing
**/
mDate.getTimeInMillis();
return mDate;
}
/**
* Compute Y-Offset from DATE
* #param date
* #return
*/
public long yOffsetFromDate(Calendar cal) {
long mills = cal.getTimeInMillis();
long nthWeeks = (mills - mBaseDateInMillis)/ONEWEEK_IN_MILLISECONDS;
return nthWeeks * mWeekHeight;
}
}
Anybody can help me? I'm not a good Java programmer.
This statement confuses me:
/* I expect mBaseDate == mAnotherDate.
* but it was different.
*/
Are you actually trying to check for equality by doing the comparison:
if (mBaseDate == mAnotherDate) { System.out.println("They are the same"); }
If so, your issue is that you are misunderstanding how the "==" operator works in Java. It compares references, rather than comparing the underlying object data, and since these are different objects (with the same values) that will always be false. For a lot more details, see the Java Notes on comparison operators.
Also, these lines look really suspicious to me:
/* We SHOULD call to update mDate internal data structure.
* Java is really strange for this thing
**/
mDate.getTimeInMillis();
I would really be surprised if Android had a bug requiring you to do this, but I guess anything is possible. What kind of problems do you have without this call?
This is because you need to use the "equals" method to compare different objects. Using operator "==" will tell you if the objects are identical (do they reside in the exact same memory location), while the "equals" comparison function will tell you if the two objects are logically equivalent.
tl;dr
long weeks = ChronoUnit.WEEKS.between ( LocalDate.of ( 1989 , 12 , 31 ) , LocalDate.of ( 1990 , 1 , 14 ) ); // Results: 2
Avoid count-from-epoch
Do not work in a count-since-epoch such as milliseconds. Confusing, cloaks bugs, and ignores issues such as time zones.
Let a good date-time library do the heavy-lifting in these calculations.
java.time
You are using troublesome old date-time classes such as java.util.Calendar. These poorly-designed classes have been supplanted by the java.time framework built into Java 8 and later. See Oracle Tutorial. Much of the java.time functionality has been back-ported to Java 6 & 7 in ThreeTen-Backport and further adapted to Android in ThreeTenABP.
ChronoUnit
The ChronoUnit class calculates elapsed time such as number of whole weeks between a pair of LocalDate (date-only, no time-of-day nor time zone) values.
LocalDate start = LocalDate.of ( 2016 , 1 , 1 );
LocalDate stop = start.plusDays ( 17 ); // Ex: 13 days = 1 week. 14 days = 2 weeks.
long weeks = ChronoUnit.WEEKS.between ( start , stop );
Dump to console.
System.out.println ( "start: " + start + " | stop: " + stop + " | weeks: " + weeks );
start: 2016-01-01 | stop: 2016-01-18 | weeks: 2
If you want a number of weeks since 1989-12-31, use that as the start object seen above.
LocalDate start = LocalDate.of( 1989 , 12 , 31 );
Half-Open
But I notice your base date is the last day of the year. Tip: spans of time are generally best handled with the Half-Open approach where the beginning is inclusive while the ending is exclusive. So you may want to use 1990-01-01 as your base date (I do not know your business logic, so just a guess on my part).
So you first two weeks would be this (1st - 15th):
long firstTwoWeeks = ChronoUnit.WEEKS.between ( LocalDate.of ( 1990 , 1 , 1 ) , LocalDate.of ( 1990 , 1 , 15 ) );
…rather than this (31st - 14th):
long firstTwoWeeks = ChronoUnit.WEEKS.between ( LocalDate.of ( 1989 , 12 , 31 ) , LocalDate.of ( 1990 , 1 , 14 ) );

Categories

Resources