51

I want to create an interval between the beginning of the week, and the end of the current week.

I have the following code, borrowed from this answer:

private LocalDateTime calcNextSunday(LocalDateTime d) {
    if (d.getDayOfWeek() > DateTimeConstants.SUNDAY) {
        d = d.plusWeeks(1);
    }
    return d.withDayOfWeek(DateTimeConstants.SUNDAY);
}

private LocalDateTime calcPreviousMonday(LocalDateTime d) {
    if (d.getDayOfWeek() < DateTimeConstants.MONDAY) {
        d = d.minusWeeks(1);
    }
    return d.withDayOfWeek(DateTimeConstants.MONDAY);
}

But now I want the Monday LocalDateTime to be at 00:00:00, and the Sunday LocalDateTime at 23:59:59. How would I do this?

Community
  • 1
  • 1
nhaarman
  • 98,571
  • 55
  • 246
  • 278
  • 1
    It is always a *very* bad idea to subtract a "small unit" from the end of time intervals. Time intervals have their start included and their end excluded. Checking if a (date)time is included in the interval you do: start <= time && time < end. Otherwise you end up with a small amount of values (actually in the interval) that suddenly get excluded. Think about the last second in your case. 23:59:59.5 is not in that interval. Using small units just makes the number of excluded values small, but doesn't remove the problem. Using the right comparison does. – Mikkel R. Lund Apr 23 '19 at 08:28

7 Answers7

156

You can use the withTime method:

 d.withTime(0, 0, 0, 0);
 d.withTime(23, 59, 59, 999);

Same as Peter's answer, but shorter.

JodaStephen
  • 60,927
  • 15
  • 95
  • 117
  • 52
    Beginning of the day can also be got with _d.withTimeAtStartOfDay()_ – Touko Feb 19 '13 at 13:26
  • 16
    `withTimeAtStartOfDay()` is not available for `LocalDateTime`, but it is available for [`DateTime`](http://joda-time.sourceforge.net/apidocs/org/joda/time/DateTime.html#withTimeAtStartOfDay()) – Abdull Oct 31 '13 at 12:18
  • +1. One note: `LocalDateTime is immutable, so there are no set methods. Instead, this method returns a new instance with the value of millis of day changed.` So you have to assign the result to some variable. – informatik01 Mar 28 '14 at 10:33
  • Oh, I'm sorry. I have just noticed that I addressed my above note to the author of Joda-Time. Quite confusing. Sorry, sir. – informatik01 Mar 31 '14 at 08:44
  • 8
    Keep in mind that with this method, you're missing one millisecond of each day. I'd suggest using `d.plusDays(1).withTime(0, 0, 0, 0)` for the end of that day. – Feuermurmel Aug 26 '14 at 09:01
  • @Touko where is `withTimeAtStartOfDay()`? there is no such in LocalDateTime – yetanothercoder Dec 16 '14 at 12:15
  • 6
    better to use `plusDays(1).withTime(0,0,0,0)` approach as @Feuermurmel pointed out, as it works with leap seconds, as for 2015-06-30T23:59:60 – ryenus Jul 13 '15 at 10:33
  • 8
    Pay attention the daylightsaving. Better to use `d.millisOfDay().withMaximumValue();` – deldev May 25 '16 at 13:24
  • This will throw `IllegalInstantException` on some instances of daytime saving. Eg, on Lebanon daytime saving occured on March 26th, 2023 at 0:00 and the clocks are then set one hour forward. We can't have `DateTime(2023,3,26)withTime(0,0,0,0)` – makata Mar 11 '23 at 05:48
99

A simple way is:

d.millisOfDay().withMaximumValue();
Tomerikoo
  • 18,379
  • 16
  • 47
  • 61
TheRueger
  • 2,508
  • 1
  • 16
  • 12
  • 16
    This should be the accepted answer, as compared with the other answers is the best in terms of performance (less objects created as DateTime is inmutable). The method withMaximunValue Documentation even says is the best way to accomplish what the question is asking – le0diaz Jan 26 '16 at 22:31
  • What is the difference with `getMaximumValueOverall`? – Yichuan Wang Oct 27 '16 at 22:16
24

How about:

private LocalDateTime calcNextSunday(LocalDateTime d) {
    return d.withHourOfDay(23).withMinuteOfHour(59).withSecondOfMinute(59).withDayOfWeek(DateTimeConstants.SUNDAY);
}

private LocalDateTime calcPreviousMonday(final LocalDateTime d) {
    return d.withHourOfDay(0).withMinuteOfHour(0).withSecondOfMinute(0).withDayOfWeek(DateTimeConstants.MONDAY);
}
Peter Svensson
  • 6,105
  • 1
  • 31
  • 31
  • 5
    `setXxx` by convention has no return type in Java so it wouldn't allow chaining. This here reminds of the builder pattern (which originally would not work on an existing object). – Hauke Ingmar Schmidt Feb 05 '12 at 22:53
  • 2
    Indeed. This lets you string the calls together, rather than having them all on different lines, and gets you all the benefits of immutable objects. – Louis Wasserman Feb 06 '12 at 02:28
  • I think the "pattern" is called "Fluent interface" http://martinfowler.com/bliki/FluentInterface.html - gives a lot of benefits in readability and clarity in my opinion. – Peter Svensson Feb 06 '12 at 06:07
  • 3
    I wrote up the "with" verb in 2006 - http://blog.joda.org/2006/05/immutable-pojos-improving-on_6406.html and updated that in 2011 - http://blog.joda.org/2011/08/common-java-method-names.html . Immutable setters are very different from normal setters in terms of how you use them (you must use the return value). – JodaStephen Feb 07 '12 at 10:16
  • 1
    @TheRueger response is the best answer. This one is correct but has the the worst performance of current answers as it creates many objects in each withXXX method. d.millisOfDay().withMaximumValue(); is the one to accomplish the best posible way. – le0diaz Jan 26 '16 at 22:34
  • Wouldn't these potentially fail with an exception if the local timezone had a daylight savings time change that prevented the specified times existing? – Jules May 02 '17 at 01:30
2

With Kotlin you could write an extension function:

fun DateTime.withTimeAtEndOfDay() : DateTime = this.withTime(23,59,59,999)

This would allow you to write:

d.withDayOfWeek(DateTimeConstants.SUNDAY).withTimeAtEndOfDay()
Cory Roy
  • 5,379
  • 2
  • 28
  • 47
1

For those coming here looking for the answer for "js-joda", you have two options depending on what you're looking to accomplish

Option 1: You want the start of the day in the same timezone

Since you've chosen to calculate your times based on an instant in time in relation to a timezone, you should use ZonedDateTime:

import { ZonedDateTime, LocalDate, ZoneId, DateTimeFormatter} from "js-joda";
import 'js-joda-timezone';

const nowInNewYorkCity = ZonedDateTime.now(ZoneId.of("America/New_York"))
const startOfTodayInNYC = nowInNewYorkCity.truncatedTo(ChronoUnit.DAYS);
console.log(startOfTodayInNYC.toString()) // Prints "2019-04-15T00:00-04:00[America/New_York]"
// And if you want to print it in ISO format
console.log(startOfTodayInNYC.format(DateTimeFormatter.ISO_INSTANT)) // "2019-04-14T04:00:00Z"

Option 2: You know the exact day that you want to get the time for

Then you can use the following methods off of LocalDate to derive the relative time (i.e. ZonedDateTime) you'd like:

    atStartOfDay(): LocalDateTime
    atStartOfDay(zone: ZoneId): ZonedDateTime
    atStartOfDayWithZone(zone: ZoneId): ZonedDateTime

Option 3: I want just the day the instant occurred on

Notice with this code, you get the day that it would be relative to where you are. So for those in New York City, it's "2019-04-14" and for those in London it would be "2019-04-15" (which is great!) because the instant in time was during the period of time where it's actually tomorrow in London ("2019-04-15T00:00:05Z"). Pretend that you were calling someone in London from NYC, and the Londoner would say, "geez, why are you calling me so early... it's 5 seconds past midnight."

import { ZonedDateTime, LocalDate, ZoneId} from "js-joda";
import 'js-joda-timezone';

const aTimeWhenLondonIsAlreadyInTomorrow = "2019-04-15T00:00:05.000Z";
const inBetweenTimeInLondon = ZonedDateTime.parse(aTimeWhenLondonIsAlreadyInTomorrow);
const inBetweenTimeInNYC = inBetweenTimeInLondon.withZoneSameInstant(ZoneId.of("America/New_York"))
const dayInLondon = inBetweenTimeInLondon.toLocalDate();
const dayInNYC = inBetweenTimeInNYC.toLocalDate();
console.log(inBetweenTimeInLondon.toString()); // "2019-04-15T00:00:05Z"
console.log(dayInLondon.toString()); // "2019-04-15"
console.log(inBetweenTimeInNYC.toString()) // "2019-04-14T20:00:05-04:00[America/New_York]"
console.log(dayInNYC.toString()); // "2019-04-14"

References: https://js-joda.github.io/js-joda/class/src/LocalDate.js~LocalDate.html#instance-method-atStartOfDayWithZone

GreeneCreations
  • 1,072
  • 1
  • 9
  • 20
0
begin = d
    // Go to previous or same Sunday
    .with(TemporalAdjusters.previousOrSame(DayOfWeek.SUNDAY))
    // Beginning of day
    .truncatedTo(ChronoUnit.DAYS)

end = d
    // Go to next Sunday
    .with(TemporalAdjusters.next(DayOfWeek.SUNDAY))
    // Beginning of day
    .truncatedTo(ChronoUnit.DAYS)

I also think it is a bad idea to represent the end of week interval with small amount of time before the actual, exclusive end. It is better to treat begin as inclusive, and end as exclusive instead (when doing comparisons etc.).

Nikola Mihajlović
  • 2,286
  • 2
  • 19
  • 23
0
DateTime.now().withTimeAtStartOfDay(); // start of day
DateTime.now().plusDays(1).withTimeAtStartOfDay().minusSeconds(1); // end of day
Tomerikoo
  • 18,379
  • 16
  • 47
  • 61
hd1
  • 33,938
  • 5
  • 80
  • 91