How to compare XMLGregorianCalendar with only the Date portion (day, month, year)?
Asked Answered
D

5

4

I'm developing a webservice integrated with spring-struts web application, in XSD there is a XMLGregorianCalendar type property, let's say the property name is trxDate.

In SOAPUI testing application, if I inserted the value of trxDate with: 2013-02-21, then I sent the soap xml request data and I printed the value in service method with: System.out.println(trxDate) method, the printout result is same as inputted: 2013-02-21.

Now, I'm trying to create a function to compare trxDate with current date. I know we can compare it using trxDate.compare(currentDate) method. The problem is I don't how to create XMLGregorianCalendar object set with current date with Date portion only (day, month, and year) to be used for comparing.

I tried with this code:

    GregorianCalendar gc = new GregorianCalendar();
    gc.set(GregorianCalendar.HOUR_OF_DAY, 0);
    gc.set(GregorianCalendar.MINUTE, 0);
    gc.set(GregorianCalendar.SECOND, 0);
    gc.set(GregorianCalendar.MILLISECOND, 0);

    XMLGregorianCalendar xgc = DatatypeFactory.newInstance().newXMLGregorianCalendar(gc);
    System.out.println(xgc);

The result is:
2013-02-20T00:00:00.000+07:00

But I'm expecting:
2013-02-20

If use the date (xgc) to compare with trxDate:

int result = trxDate.compare(xgc);

The result is 2, which means: INDETERMINATE (from DatatypeConstants class). The proper result should be -1, 0, or 1.

So what's wrong with my code?

Dorser answered 20/2, 2013 at 11:7 Comment(0)
M
5

Instead of trying to clear out the unwanted fields from the GregorianCalendar, it may be easier to create an un-initialized XMLGregorianCalendar and then copy just the fields you do want:

XMLGregorianCalendar xgc = DatatypeFactory.newInstance().newXMLGregorianCalendar();
GregorianCalendar now = new GregorianCalendar();
xgc.setYear(now.get(Calendar.YEAR));
xgc.setMonth(now.get(Calendar.MONTH) + 1);
xgc.setDay(now.get(Calendar.DAY_OF_MONTH));
System.out.println(xgc);

This avoids the round trip to String and back again that would be necessary if you were to use newXMLGregorianCalendar(lexicalRepresentation)

Mussman answered 20/2, 2013 at 11:37 Comment(2)
Whoa, I'm so confused there are many ways, which is better between your way and Blaise's way?Dorser
I just tried your code, there is a bit of mistake, you need to add +1 to the xgc.setMonth(), so the code should become: xgc.setMonth(now.get(Calendar.MONTH)+1);. It seems XMLGregorianCalendar uses one-based month index.Dorser
S
4

The javadoc for XMLGregorianCalendar.compare explains that it uses the rules from the XML Schema specification for the comparison, to which the javadoc links.

Section B.1. of the comparison algorithm states that both dateTimes must have exactly the same (sub)set of {year, month, day, hour, minute, second} fields defined. If they don't, the result is indeterminate. (The XML Schema spec uses <> in the algorithm description to indicate an indeterminate result.)

So if you have an XMLGregorianCalendar with just year, month and day defined, you must compare it with another XMLGregorianCalendar with just year, month and day defined. Either you must parse it from a string, as Blaise suggested, or you must instantiate an XMLGregorianCalendar and call setYear, setMonth and setDay on it yourself.

Sum answered 20/2, 2013 at 11:29 Comment(1)
This was an excellent answer, in my book. I came across this issue because I was comparing 2 XMLGregorianCalendar instances but one had the time zone set. Converting them into GregorianCalendar objects and using the compareTo() method provided me with the comparison logic I was looking for. Otherwise, using XMLGregorianCalendar's compare() method will always return a DatatypeConstants.INDETERMINATE.Commercialism
T
2

UPDATE

You could also create your XMLGregorianCalendar this way:

    XMLGregorianCalendar xgc = df.newXMLGregorianCalendar(
            2012, 
            DatatypeConstants.FEBRUARY, 
            21, 
            DatatypeConstants.FIELD_UNDEFINED, 
            DatatypeConstants.FIELD_UNDEFINED, 
            DatatypeConstants.FIELD_UNDEFINED, 
            DatatypeConstants.FIELD_UNDEFINED, 
            DatatypeConstants.FIELD_UNDEFINED);
    System.out.println(xgc);

You can use the following method:

  • newXMLGregorianCalendar(String lexicalRepresentation)

Demo

import javax.xml.datatype.*;

public class Demo {

    public static void main(String[] args) throws Exception {
        DatatypeFactory df = DatatypeFactory.newInstance();

        XMLGregorianCalendar xgc = df.newXMLGregorianCalendar("2013-02-12");
        System.out.println(xgc);
    }

}

Output

2013-02-12
Tumer answered 20/2, 2013 at 11:20 Comment(2)
Wow, I didn't aware there is such method, very quick and easy. Thanks very much. But I wonder if using my code way above, is it possible to achieve same result?Dorser
Whoa, I'm so confused there are many ways, which is better between your way and Ian's way?Dorser
P
1

try

    String str = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
    XMLGregorianCalendar xgc = DatatypeFactory.newInstance().newXMLGregorianCalendar(str);
    System.out.println(xgc);

this is a hint (order) to XMLGregorianCalendar to use xsd:date type where time is undefined

Phillie answered 20/2, 2013 at 11:24 Comment(0)
E
0

In case anyone is still looking for an alternative. This is what suited my requirements.

    ...

    date.setTimezone(DatatypeConstants.FIELD_UNDEFINED);
    date.setTime(DatatypeConstants.FIELD_UNDEFINED,
            DatatypeConstants.FIELD_UNDEFINED,
            DatatypeConstants.FIELD_UNDEFINED,
            DatatypeConstants.FIELD_UNDEFINED);
    ...

This remove the timezone and the timestamp.

Erich answered 9/11, 2019 at 3:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.