【问题标题】:convert java.sql.Timestamp to javax.xml.datatype.XMLGregorianCalendar without losing nanos将 java.sql.Timestamp 转换为 javax.xml.datatype.XMLGregorianCalendar 而不会丢失 nanos
【发布时间】:2025-12-26 04:10:12
【问题描述】:

java.sql.Timestamp 转换为javax.xml.datatype.XMLGregorianCalendar 的最简洁方法是什么?

鉴于XMLGregorianCalendar 在小数秒内具有BigDecimal 精度,因此不会损失精度,但是鉴于java.sql.Timestamp 与时区无关,我不确定应该在XMLGregorianCalendar 对象上设置哪个时区.

how to convert java.util.Date to XMLGregorianCalendar 上有一个关于 SO 的答案,所以我可以将我的 Timestamp 转换为 java.util.Date 但这会导致亚毫秒范围内的精度损失,这对于目标数据类型是不必要的 (XMLGregorianCalendar ) 可以保存源数据类型 (Timestamp) 的纳秒部分。

【问题讨论】:

    标签: java


    【解决方案1】:

    AlexCG answer 在某些情况下不能正常工作(当我们在 nanos 中给出少于 9 位的数字时),例如当我们将 nanos 设置为 25 时,我们会收到如下输出:

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

    这不是真的。 这个方法我稍微修改了一下,现在是:

        @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);
        }
    

    现在输出是正确的:

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

    需要 Apache Commons 库。

    【讨论】:

      【解决方案2】:

      我正在这样做:

      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);
          }
      }
      

      该示例输出:

      Timestamp::2015-08-25 20:59:35.123456789  
      Calendar:::2015-08-25T20:59:35.123456789
      

      【讨论】:

        【解决方案3】:

        java.sql.Timestamp 保存自 1970 年 1 月 1 日 00:00:00 GMT/UTC + nanos 以来的毫秒数。因此,除非您在两者之间做任何愚蠢的事情,否则我建议 based on the XMLGregorianCalendar docs 您将时区设置为 0(UTC)。

        【讨论】: