vviston
vviston

Reputation: 183

Java Dates in the same day or month

I need to write a method that takes Date as input parameter in format YYYY-MM-DD and return all the Dates from the List which are on the same day.

The Dates in the List looks e.g. like this: 2020-06-09T07:10:28.1985307Z

Do I need to parse the parameter using "-" split and takhe the MM portion of the String and compare to MM portion of the List Date? It seems to be too complicated, could you give me some advice how to solve that better?

Upvotes: 0

Views: 1082

Answers (4)

Anonymous
Anonymous

Reputation: 86359

This is not possible without deciding on a time zone. It is never the same date everywhere on earth, so whether a point in time from your list falls on this, that or even the other date, depends on time zone. This also implies that the new month begins at different times in different time zones. If my memory serves, one country even changed its time zone in order to be the first country to enter the new millennium some 20 years ago.

I have picked a time zone at random. You just pick your preferred time zone and substitute it into my code below. I am answering both of your questions in turn:

  1. Find moments that are on the same day.
  2. Find moments that are in the same month.

Moments that fall on the same day

    ZoneId zone = ZoneId.of("Africa/Kinshasa");

    List<Instant> theList = List.of(
            Instant.ofEpochSecond(1_590_965_000),
            Instant.ofEpochSecond(1_590_966_000),
            Instant.ofEpochSecond(1_592_002_800),
            Instant.ofEpochSecond(1_592_089_100),
            Instant.ofEpochSecond(1_593_557_000),
            Instant.ofEpochSecond(1_593_558_000));
    System.out.println("The gross list");
    theList.forEach(System.out::println);

    LocalDate userInput = LocalDate.parse("2020-06-13");

    List<Instant> sameDay = theList.stream()
            .filter(i -> i.atZone(zone).toLocalDate().equals(userInput))
            .collect(Collectors.toList());
    System.out.println("Same day: " + sameDay);

Output is:

The gross list
2020-05-31T22:43:20Z
2020-05-31T23:00:00Z
2020-06-12T23:00:00Z
2020-06-13T22:58:20Z
2020-06-30T22:43:20Z
2020-06-30T23:00:00Z
Same day: [2020-06-12T23:00:00Z, 2020-06-13T22:58:20Z]

If your moments are not already Instant objects, use Instant.parse to convert them to Instant before doing any other work with them. Congo is at offset +01:00 from UTC (all year), so 2020-06-12T23:00:00Z equals 2020-06-13T00:00:00+01:00[Africa/Kinshasa], so does fall on 13th June.

Moments that fall in the same month

The code is quite similar.

    YearMonth userMonth = YearMonth.from(userInput);
    List<Instant> sameMonth = theList.stream()
            .filter(i -> YearMonth.from(i.atZone(zone)).equals(userMonth))
            .collect(Collectors.toList());
    System.out.println(sameMonth);

[2020-05-31T23:00:00Z, 2020-06-12T23:00:00Z, 2020-06-13T22:58:20Z, 2020-06-30T22:43:20Z]

A YearMonth is what the class name says, a year and a month. I figured that this was the correct class for representing your concept of same month

Link

Oracle tutorial: Date Time explaining how to use java.time.

Upvotes: 0

Daniel Vilas-Boas
Daniel Vilas-Boas

Reputation: 896

1- Convert the input string to LocalDate

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");


LocalDate date = LocalDate.parse(str, formatter);

2- With the LocalDate in hands, you have a number of operations that can be performed, such as retrieving the current day, month, year etc. Parse all the dates and implement your logic, these are some relevant methods:

    date.getDayOfYear();
    date.minusMonths();
    date.isBefore();

Upvotes: 0

Rustam Shafigullin
Rustam Shafigullin

Reputation: 376

Date class is obsolete. Use LocalDate and ZonedDateTime for your solution.

public class Test {

    public static void main(String[] args){
        Test t = new Test();
        List<LocalDate> result = t.fetchDates(LocalDate.parse("2020-06-09"));
        System.out.println(result);
    }

    private List<ZonedDateTime> datesList = List.of(
            ZonedDateTime.parse("2020-06-09T07:10:28.1985307Z"),
            ZonedDateTime.parse("2020-06-01T01:10:28.1985307Z"),
            ZonedDateTime.parse("2020-06-09T01:10:28.1985307Z"),
            ZonedDateTime.parse("2020-06-08T01:10:28.1985307Z"),
            ZonedDateTime.parse("2020-06-09T01:10:28.1985307Z")
            );


    public List<LocalDate> fetchDates(LocalDate date)
    {
        return datesList.stream()
                .filter(zdt -> zdt.toLocalDate().equals(date))
                .map(zdt -> zdt.toLocalDate())
                .collect(Collectors.toList());
    }
}

Prints [2020-06-09, 2020-06-09, 2020-06-09]

Upvotes: 1

rzwitserloot
rzwitserloot

Reputation: 103637

You say 'in format YYYY-MM-DD', which suggests what you actually get are String objects, not Date objects. Dates, LocalDates, Instants, etc, these don't have a format. They represent the thing they are named after (except Date, which is badly misnamed, and actually represents a timezoneless instant based on epochmillis and has absolutely no relation whatsoever to the human concept of dates).

To work with dates, you want to.. have date objects. Or rather, the right type from the java.time package. If it's just a date, you're looking for LocalDate. These are timezoneless. They just represent the concept of, say, 'april 4th, 1985', without location being involved. The inputs you have aren't dates. They are effectively moments in time, given that it's a minutiously specified time (with 6 digits for second fractions no less), and includes a timezone, which is a timezone no humans use for normal purposes even. That suggests what you're really looking for is Instant.

You then want to do something really bizarre to the instant: Check the DATE. You're mixing the notion of a moment in time, with the notion of a date, which is fundamentally not a moment in time. After all, if it's the 4th of april in the netherlands, it could well be the 3rd of april in new york. If it's October 25th, 1917 in Moscow, it is November 7th in Amsterdam. Trying to equate 'dates' is nonsensical without first localizing. Who are you asking? Where did they live?

There are many ways you can turn the various types in the java.time package from instants to dates and back. Here's one approach:

public static Instant read(String in) {
    return Instant.parse(in);
}

public static LocalDate localize(Instant i, ZoneId zone) {
    return i.atZone(zone).toLocalDate();
}

public static void main(String[] args) throws Exception {
    Instant a = Instant.parse("2020-06-09T07:10:28.1985307Z");
    Instant b = Instant.parse("2020-04-09T07:10:28.1985307Z");
    Instant c = Instant.parse("2020-06-09T23:10:28.1985307Z");

    ZoneId ams = ZoneId.of("Europe/Amsterdam");

    LocalDate aDate = localize(a, ams);
    LocalDate bDate = localize(b, ams);
    LocalDate cDate = localize(c, ams);

    System.out.println(aDate.isEqual(cDate));
}

The above would end up printing 'false'. The instant in time described by 2020-06-09T07:10:28.1985307Z would be described as being the 9th of june in 2020 by someone in amsterdam. The instant 2020-06-09T23:10:28.1985307Z would be described as the 10th (as amsterdam would at that point by +0200). Switch the zone to new york, and the answer would be true instead.

The question 'are these 2 instants the same date' is not a sane question to ask without also including in that question which zone is asking. Note that UTC is itself a zone, you can use it: ZoneId utc = ZoneOffset.UTC; will get you there.

Upvotes: 3

Related Questions