RSS 2.0
Sign In
# Wednesday, May 4, 2011

As you may know, JAX-WS uses javax.xml.datatype.XMLGregorianCalendar abstract class in order to present date/time data type fields. We have used this class rather long time in happy ignorance without of any problem. Suddenly, few days ago, we ran into a weird bug of its Sun’s implementation (com.sun.org.apache.xerces.internal.jaxp.datatype.XMLGregorianCalendarImpl). The bug appears whenever we try to convert an XMLGregorianCalendar instance to a java.util.GregorianCalendar using toGregorianCalendar() method. I’ve written a simple JUnit test in order to demonstrate this bug:

  @Test
  public void testXMLGregorianCalendar()
    throws Exception
  {    
    SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    XMLGregorianCalendar calendar = 
      javax.xml.datatype.DatatypeFactory.newInstance().newXMLGregorianCalendar();

    calendar.setDay(1);
    calendar.setMonth(1);
    calendar.setYear(1);
    
    System.out.println("1: " + calendar.toString());

    System.out.println("2: " +
      formatter.format(calendar.toGregorianCalendar().getTime()));
    
    GregorianCalendar cal = new GregorianCalendar(
      calendar.getYear(), 
      calendar.getMonth() - 1, 
      calendar.getDay());
    
    cal.clear(Calendar.AM_PM);
    cal.clear(Calendar.HOUR_OF_DAY);
    cal.clear(Calendar.HOUR);
    cal.clear(Calendar.MINUTE);
    cal.clear(Calendar.SECOND);
    cal.clear(Calendar.MILLISECOND);

    System.out.println("3: " + formatter.format(cal.getTime()));
    
    /*
     * Output:
     * 
     * 1: 0001-01-01
     * 2: 0001-01-03 00:00:00
     * 3: 0001-01-01 00:00:00
     */
  }

As you see, the date 0001-01-01 is transformed to 0001-01-03 after call of toGregorianCalendar() method (see output 2).

Moreover, if we’ll serialize this XMLGregorianCalendar instance to XML we’ll see it as 0001-01-01+02:00 which is rather weird and could be potential problem for interoperability between Java and other platforms.

Conclusion: in order to convert XMLGregorianCalendar value to GregorianCalendar do the following. Create a new instance of GregorianCalendar and just set the corresponding fields with values from XMLGregorianCalendar instance.

Wednesday, May 4, 2011 9:19:52 AM UTC  #    Comments [1] -
Java | Tips and tricks
Tuesday, August 14, 2012 3:46:19 PM UTC
Hi,
I also encountered this problem, and this is what I have found:

1. GregorianCalendar alows the Gregorian change date to be set, use one of the following:
private static final Date PURE_GREGORIAN_CHANGE = new Date(Long.MIN_VALUE);
private static final Date PURE_JULIAN_CHANGE = new Date(Long.MAX_VALUE);
private static final Date DEFAULT_GREGORIAN_CHANGE = new GregorianCalendar().getGregorianChange(); // year 1582
Important: is has to be changed before setting date fields, otherwise it has no effect.

2. XMLGregorianCalendar's toGregorianCalendar method actually uses PURE_GREGORIAN_CHANGE when constructing GregorianCalendar, and I think this is the reason why there is a mismatch with displayed dates before year 1582.

Here is the code to play with:

private static final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");

public static void main(String[] args) throws Exception {

XMLGregorianCalendar xmlCal = DatatypeFactory.newInstance()
.newXMLGregorianCalendar(12, 5, 4,
DatatypeConstants.FIELD_UNDEFINED, // hours
DatatypeConstants.FIELD_UNDEFINED, // minutes
DatatypeConstants.FIELD_UNDEFINED, // seconds
DatatypeConstants.FIELD_UNDEFINED, // millis
DatatypeConstants.FIELD_UNDEFINED); // timezone - offset in minutes

GregorianCalendar gc1 = xmlCal.toGregorianCalendar();
String sgc1 = dateFormat.format(gc1.getTime());
System.out.println(sgc1);
System.out.println(gc1);

GregorianCalendar gc2 = new GregorianCalendar();
gc2.clear();
//gc2.setGregorianChange(PURE_JULIAN_CHANGE);
//gc2.setGregorianChange(DEFAULT_GREGORIAN_CHANGE);
gc2.setGregorianChange(PURE_GREGORIAN_CHANGE);

gc2.set(Calendar.YEAR, xmlCal.getYear());
gc2.set(Calendar.MONTH, xmlCal.getMonth() - 1);
gc2.set(Calendar.DAY_OF_MONTH, xmlCal.getDay());

String sgc2 = dateFormat.format(gc2.getTime());
System.out.println(sgc2);
System.out.println(gc2);

}

When PURE_GREGORIAN_CHANGE is used, the problem is fully reproduced. And when DEFAULT_GREGORIAN_CHANGE is used, all dates are correctly displayed. The question is why XMLGregorianCalendar's toGregorianCalendar method is using PURE_GREGORIAN_CHANGE.

Igor
Igor
All comments require the approval of the site owner before being displayed.
Name
E-mail
Home page

Comment (Some html is allowed: a@href@title, b, blockquote@cite, em, i, strike, strong, sub, super, u) where the @ means "attribute." For example, you can use <a href="" title=""> or <blockquote cite="Scott">.  

[Captcha]Enter the code shown (prevents robots):

Live Comment Preview
Archive
<May 2019>
SunMonTueWedThuFriSat
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678
Statistics
Total Posts: 365
This Year: 1
This Month: 0
This Week: 0
Comments: 221
Locations of visitors to this page
Disclaimer
The opinions expressed herein are our own personal opinions and do not represent our employer's view in anyway.

© 2019, Nesterovsky bros
All Content © 2019, Nesterovsky bros
DasBlog theme 'Business' created by Christoph De Baene (delarou)