2

What's the cleanest way to convert java.sql.Timestamp to javax.xml.datatype.XMLGregorianCalendar?

Given that XMLGregorianCalendar has BigDecimal precision for fractional seconds there's no loss of precision, however I'm not sure what time zone I should set at the XMLGregorianCalendar object given that java.sql.Timestamp is time-zone independent.

There is an answer on SO on how to convert java.util.Date to XMLGregorianCalendar so I could cast my Timestamp to java.util.Date but that would lead to loss of precision in the sub-millisecond range which is unnecessary as the target datatype (XMLGregorianCalendar) can hold the nanoseconds component of the source datatype (Timestamp).

Community
  • 1
  • 1
Marcus Junius Brutus
  • 26,087
  • 41
  • 189
  • 331

3 Answers3

5

I'm doing this way:

import java.math.BigDecimal;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.Date;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;

public class ApiRuntime {

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

        Timestamp ts = new Timestamp(new Date().getTime());
        ts.setNanos(123456789);

        LocalDateTime ldt = ts.toLocalDateTime();

        XMLGregorianCalendar cal = DatatypeFactory.newInstance().newXMLGregorianCalendar();

        cal.setYear(ldt.getYear());
        cal.setMonth(ldt.getMonthValue());
        cal.setDay(ldt.getDayOfMonth());
        cal.setHour(ldt.getHour());
        cal.setMinute(ldt.getMinute());
        cal.setSecond(ldt.getSecond());
        cal.setFractionalSecond(new BigDecimal("0." + ldt.getNano()));

        System.out.println("Timestamp::" + ts);
        System.out.println("Calendar:::" + cal);
    }
}

That example outputs:

Timestamp::2015-08-25 20:59:35.123456789  
Calendar:::2015-08-25T20:59:35.123456789
AlexCG
  • 126
  • 1
  • 8
1

java.sql.Timestamp holds the number of millis since January 1, 1970, 00:00:00 GMT/UTC + nanos. Hence, unless you're doing anything daft in between I suggest that based on the XMLGregorianCalendar docs you set the timezone to 0 (UTC).

Marcel Stör
  • 22,695
  • 19
  • 92
  • 198
1

AlexCG answer is working not quite properly for some cases (when we give less than 9 digits number in nanos)e.g. when we set nanos to 25 then we receive output like this:

Timestamp::2020-01-02 16:30:01.000000025
Calendar:::2020-01-02T16:30:01.25

which is not true. I modified a little this method, now it's:

    @Test
    public void convertLocalDataTimeToXmlGCal() throws Exception {

        Timestamp ts = new Timestamp(new Date().getTime());
        ts.setNanos(25);

        LocalDateTime ldt = ts.toLocalDateTime();

        XMLGregorianCalendar cal = DatatypeFactory.newInstance().newXMLGregorianCalendar();

        cal.setYear(ldt.getYear());
        cal.setMonth(ldt.getMonthValue());
        cal.setDay(ldt.getDayOfMonth());
        cal.setHour(ldt.getHour());
        cal.setMinute(ldt.getMinute());
        cal.setSecond(ldt.getSecond());
        String nanos = "0." + StringUtils.leftPad(String.valueOf(ldt.getNano()), 9, '0');
        cal.setFractionalSecond(new BigDecimal(nanos));

        System.out.println("Timestamp::" + ts);
        System.out.println("Calendar:::" + cal);
    }

now the output is correct:

Timestamp::2020-01-02 16:33:02.000000025
Calendar:::2020-01-02T16:33:02.000000025

Apache Commons library is required.

piterpti
  • 11
  • 3