0

For eg: we have a List<XMLGregorianCalendar> dates we need to find the latest date in this list.

Really appreciate your help!

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
Pranav
  • 1
  • 3
  • `dates.stream().max(Comparator.comparing(Function.identity())).get();` use `min` function for older date. – the Hutt Mar 30 '21 at 16:16
  • Alternatively, dates and calendars are comparable objects, you could put them in a SortedSet instead of a list and then get the first or the last. See `NavigableSet.pollFirst` or `NavigableSet.pollLast`. TreeSet implements NavigableSet. – Edwin Dalorzo Mar 30 '21 at 16:20
  • XMLGregorianCalendar does not implement Comparable. – Michael Kay Mar 30 '21 at 22:58
  • Do your `XMLGregorianCalendar` objects represent dates without time of day, or have they got time and/or UTC offset (called timezone in the API doc)? – Ole V.V. Mar 31 '21 at 09:02

2 Answers2

2

Rather strangely, XMLGregorianCalendar has a compareTo() method but does not implement Comparable, which makes this unnecessarily difficult. I'd be inclined to convert your values to something more Java-friendly like a ZonedDateTime and work with that. I guess if you're using XMLGregorianCalendar then it's probably because you're using JAXB data binding to XML: the class was invented because at the time, none of the Java date/time options quite matched the XSD data types, but that's no longer really the case.

Michael Kay
  • 156,231
  • 11
  • 92
  • 164
  • The method is called `compare` and should not be confused with a usual `compareTo` method. `compare()` may return `LESSER`, `EQUAL`, `GREATER` or `INDETERMINATE`. – Ole V.V. Mar 31 '21 at 09:40
  • *For example, there is no determinate ordering between (a) 2000-01-20T12:00:00 and (b) 2000-01-20T12:00:00Z. Based on timezones currently in use, (c) could vary from 2000-01-20T12:00:00+12:00 to 2000-01-20T12:00:00-13:00. It is, however, possible for this range to expand or contract in the future, based on local laws. Because of this, the following definition uses a somewhat broader range of indeterminate values: +14:00..-14:00.* (from [XML doc](https://www.w3.org/TR/xmlschema-2/#dateTime-order)). – Ole V.V. Mar 31 '21 at 09:43
  • Yes, thanks. In practice in a given dataset the values usually either all have a timezone or all have none, which is why it's best to convert to a data type such as ZonedDateTime that makes ordering more predictable. – Michael Kay Mar 31 '21 at 13:21
0

As Michael Kay said in a comment, in practice date-times in a list usually all have time of day or none of them, and all have UTC offset (“timezone” in the XML parlance) or none. And this is a requirement for your request to make sense. Which of 23:34:45 and 2021-03-31 is later? XMLGregorianCalendar is flexible enough to accommodate both.

If your XMLGregorianCalendar objects all have full dates, times and offset, I suggest sorting them as OffsetDateTimes:

    DatatypeFactory factory = DatatypeFactory.newInstance();
    List<XMLGregorianCalendar> dates = Arrays.asList(
            factory.newXMLGregorianCalendar(
                    2021, DatatypeConstants.MARCH, 30, 12, 0, 0, 0, 0),
            factory.newXMLGregorianCalendar(
                    2021, DatatypeConstants.MARCH, 30, 9, 0, 0, 0, 60),
            factory.newXMLGregorianCalendar(
                    2021, DatatypeConstants.MARCH, 30, 13, 0, 0, 0, 60));
    
    if (dates.isEmpty()) {
        System.out.println("No dates");
    } else {
        XMLGregorianCalendar maxDate
                = Collections.max(dates,
                        Comparator.comparing(xgc -> OffsetDateTime.parse(xgc.toString())));
        System.out.println(maxDate );
    }

Output from this snippet is:

2021-03-30T13:00:00.000+01:00

I have chosen my data so that the first and the last item in the list denote the same point in time, only one is on UTC, the other at offset +01:00 (for example Irish Summer Time). That the latter is output is no coincidence. OffsetDateTime.compareTo() sorts first by point in time (instant in java.time parlance), next by local date-time, and since 13 sounds later than 12, this is chosen.

There are more ways to convert from XMLGregorianCalendar to OffsetDateTime. I chose parsing as string because it gives us validation that the XMLGregorianCalendar has all of date, time and offset.

If date is to be taken literally, your objects represent only dates, instead use LocalDate. The code is almost the same:

    List<XMLGregorianCalendar> dates = Arrays.asList(
            factory.newXMLGregorianCalendarDate(2021, DatatypeConstants.MARCH, 26,
                    DatatypeConstants.FIELD_UNDEFINED),
            factory.newXMLGregorianCalendarDate(2021, DatatypeConstants.MARCH, 29,
                    DatatypeConstants.FIELD_UNDEFINED),
            factory.newXMLGregorianCalendarDate(2021, DatatypeConstants.MARCH, 28,
                    DatatypeConstants.FIELD_UNDEFINED));
    
    if (dates.isEmpty()) {
        System.out.println("No dates");
    } else {
        XMLGregorianCalendar maxDate
                = Collections.max(dates,
                        Comparator.comparing(xgc -> LocalDate.parse(xgc.toString())));
        System.out.println(maxDate );
    }

2021-03-29

If you have got dates and times but no offsets, use LocalDateTime in the same way.

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