0

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?

vviston
  • 183
  • 1
  • 12
  • 4
    *FYI:* You may want to check out [`java.time`](https://docs.oracle.com/javase/8/docs/api/java/time/package-summary.html) for that, which replaced the old API and has a much nicer design and more robust functionality. Check out [this](https://www.baeldung.com/java-8-date-time-intro) guide. – akuzminykh Jun 09 '20 at 18:41
  • Please take a look here on how to parse dates: https://stackoverflow.com/questions/12781273/what-are-the-date-formats-available-in-simpledateformat-class – Yusef Maali Jun 09 '20 at 18:42
  • 1
    It’s not completely clear, sorry. Did you mean? You have a list of instants like `2020-06-09T07:10:28.1985307Z` and then you accept user input in format YYYY-MM-DD? And then find the instants from the list that fall on the date entered by the user? And in which time zone? – Ole V.V. Jun 09 '20 at 19:07

4 Answers4

3

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.

rzwitserloot
  • 85,357
  • 5
  • 51
  • 72
  • For future readers: the Russians used the Julian calendar until 1918, so the Moscow date was 13 days behind. – MC Emperor Jun 09 '20 at 19:18
  • Good Answer. But the term *localize* is misused here. Adjusting time zones is not localization. For example, a Japanese engineer in the Netherlands for a conference might want to see a schedule of talks listed in `Europe/Amsterdam` time zone but have the formatting of that schedule localized to her native Japanese. `Instant.parse("2020-06-09T07:10:28.1985307Z").atZone( ZoneId.of( "Europe/Amsterdam" ) ).format( DateTimeFormatter.ofLocalizedDateTime( FormatStyle.LONG ).withLocale( Locale.JAPAN ) ).` ➙ 2020年6月9日 9:10:28 CEST – Basil Bourque Jun 09 '20 at 19:50
  • @Basil you're right, 'localize' usually is the slang term for adjusting things into the language and format of the user, and not as much the timezone of the event. Any suggestions for better wording? – rzwitserloot Jun 09 '20 at 20:05
  • Maybe "extractLocalDateByZone". – Basil Bourque Jun 09 '20 at 20:12
  • See this code run live [at IdeOne.com](https://ideone.com/0dsx3d). – Basil Bourque Jun 09 '20 at 20:18
  • extractLocalDateByZone is such a mouthful. Given that 'LocalDate' uses the term local, and 'localize' is fairly generic, I think given the context it'll be clear enough. – rzwitserloot Jun 09 '20 at 23:21
1

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]

  • 2
    Very good solution (if my interpretation of the question is correct). `ZonedDateTime` is overkill when the datetimes in the list have an offset (`Z` meaning offset 0 from UTC) and not a time zone (like Atlantic/Madeira). I recommend `OffsetDateTime`. The rest of the code is the same. – Ole V.V. Jun 09 '20 at 19:12
  • 1
    The variable `result` is a `List` while your method returns `List` – TiiJ7 Jun 09 '20 at 19:20
  • Thank you for the answers! I have one more. How to find out that two dates are in the same month? – vviston Jun 10 '20 at 11:43
  • LocalDate date1 = LocalDate.parse("2020-06-09"); LocalDate date2 = LocalDate.parse("2020-06-01"); LocalDate date3 = LocalDate.parse("2020-05-09"); System.out.println(date1.getMonth().equals(date2.getMonth())); System.out.println(date1.getMonth().equals(date3.getMonth())); System.out.println(date2.getMonth().equals(date3.getMonth())); Prints true false false – Rustam Shafigullin Jun 10 '20 at 17:47
  • @wiston This method will return all full dates from your list which have months equal to month in passed "LocalDate date" param: public List getSameMonths(LocalDate date) { return datesList.stream().filter(zdt -> zdt.getMonth().equals(date.getMonth())).collect(Collectors.toList()); } – Rustam Shafigullin Jun 10 '20 at 18:06
  • @vviston *I have one more. How to find out that two dates are in the same month?* I added one more answer on the occasion of this second question (which was hinted at but not clear from your original question title). Not that the answers by Rustam Shafigullin and rzwitserloot are not very good, they are. – Ole V.V. Jun 11 '20 at 19:54
0

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();
0

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.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161