当前位置: 首页 > 面试题库 >

从XMLGregorianCalendar转换为Calendar时的日期更改

童宏富
2023-03-14
问题内容

在测试可在系统之间映射日期时间类型的Web服务时,我注意到在公历开始时间之前发送任何日期会导致转换为最终类型时准确性下降,最终结果总是在该范围内稍早几天。

我将问题缩小到确切的范围,但是我仍然无法弄清 为什么
要这样进行转换,从文档中可以看出,儒略历用于公历开始前的日期时间:1582年10月15日。

问题行位于从开始XMLGregorianCalendarGregorianCalendar第78行:calendarDate = argCal.toGregorianCalendar();
当时间从calendarDate第86行开始时:cal.setTime(calendarDate.getTime());时间比原定时间提前了2天,即1月03日而不是1月01日,如您所见从下面程序的输出中。

这是我制作的一个示例程序,用来展示铸造过程的端到端:

import java.sql.Date;
import java.util.Calendar;
import java.util.GregorianCalendar;

import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;



public class TestDateConversions {

    public static void main(String[] args)
    {
        TestDateConversions testDates = new TestDateConversions();
        try
        {
            XMLGregorianCalendar testDate1 = DatatypeFactory.newInstance().newXMLGregorianCalendar();
            testDate1.setYear(0001);
            testDate1.setMonth(01);
            testDate1.setDay(01);
            System.out.println("Start date: "+testDate1.toString() +"\n**********************");

            testDates.setXMLGregorianCalendar(testDate1);
            System.out.println("\nNull given \n"+ "**********");
            testDates.setXMLGregorianCalendar(null);
        }
        catch(Exception e)
        {
            System.out.println(e);
        }
    }


    public void setXMLGregorianCalendar(XMLGregorianCalendar argCal)
    {
        GregorianCalendar calendarDate;
        if (argCal != null)
        {
            calendarDate = argCal.toGregorianCalendar();
            System.out.println("XMLGregorianCalendar time: " + argCal.getHour() + ":"+argCal.getMinute()+":"+argCal.getSecond());
            System.out.println("XMLGregorianCalendar time(ms): "+argCal.getMillisecond());
            System.out.println("XMLGregorianCalendar -> GregorianCalendar: "+calendarDate.get(GregorianCalendar.YEAR) + "-"+(calendarDate.get(GregorianCalendar.MONTH)+1) + "-"+calendarDate.get(GregorianCalendar.DAY_OF_MONTH));
            System.out.println("!!!!PROBLEM AREA!!!!");
            Calendar cal = Calendar.getInstance();
            System.out.println("-- New Calendar instance: "+cal.get(Calendar.YEAR) + "-"+(cal.get(Calendar.MONTH)+1)+"-"+cal.get(Calendar.DAY_OF_MONTH));
            System.out.println("-- Calling Calendar.setTime(GregorianCalendar.getTime())");
            cal.setTime(calendarDate.getTime());
            System.out.println("-- calendarDate.getTime() = " + calendarDate.getTime() + " <-- time is incorrect");
            System.out.println("-- Calendar with time set from GregorianCalendar: "+cal.get(Calendar.YEAR) + "-"+(cal.get(Calendar.MONTH)+1)+"-"+cal.get(Calendar.DAY_OF_MONTH) + " <-- day is increased here");
            setCalendar(cal);
        }
        else 
        {
            setCalendar(null);
        }
    }

    public void setCalendar(Calendar argCal)
    {
        if (argCal != null)
        {
            Date date = new Date(argCal.getTimeInMillis());
            System.out.println("Calendar to Date: "+date);
            setDate(date);
        }
        else
        {
            setDate(null);
        }

    }

    public void setDate(Date argDate)
    {
        try
        {
            if (argDate == null)
            {
                Calendar cal  = new GregorianCalendar(1,0,1);
                Date nullDate = new Date(cal.getTimeInMillis());
                System.out.println("Null Calendar created: "+cal.get(Calendar.YEAR) + "-"+(cal.get(Calendar.MONTH)+1)+"-"+cal.get(Calendar.DAY_OF_MONTH));
                System.out.println("Null Date created: "+nullDate);
            }
            else 
            {
                System.out.println("Final date type: "+argDate);
            }
        }
        catch (Exception  ex)
        {
            System.out.println(ex);
        }
    }
}

问题答案:

摘自XMLGregorianCalendar.toGregorianCalendar()JavaDoc的他们是如何创建的GregorianCalendar实例:

通过调用GregorianCalendar.setGregorianChange(new Date(Long.MIN_VALUE))获得纯阳历。

这意味着,创建的日历将是多用的,并且不会像默认情况下那样切换到朱利安日历(对于旧日期)。然后问题就在这里:

  • argCal.toGregorianCalendar()- 使用字段表示形式*XMLGregorianCalendar 转换为 GregorianCalendar (不使用Julian系统-参见上文) *
  • cal.setTime(calendarDate.getTime());
    • 这实际上是将字段表示形式转换为 时间戳表示形式, 并使用该时间戳初始化新日历
    • 新日历使用儒略历系统表示日期,因为该日期早于1582

有几种解决方法:

  • LocalDate#fromCalendarFiels如果您只对日期感兴趣,请使用JodaTime
  • 使用字段访问而不是 #getTime方法转换日历
  • 强制使用公历系统(与 XMLGregorianCalendar 一样)

更新 请注意,Java Date和Calendar API的设计不是很好,有时可能会(并且确实)令人困惑。这也是Java
8包含完全重做的日期时间库JSR-310(顺便基于JodaTime)的原因。

现在,您必须意识到,您可以通过两种截然不同的方法来存储和使用特定的 即时 (日历独立关键字):

  • 存储与定义良好的称为epoch的 瞬间 的偏移量(例如毫秒)(例如unix epoch 1970-01-01)
  • 按日历字段存储日期(例如1970年1月1日)

第一种方法是在引擎盖下使用的方法java.util.Date。但是,这种表示通常是非人类友好的。人类使用日历日期,而不是时间戳。Calendar将介入将时间戳转换为日期字段。这也是有趣的部分开始的地方…如果要通过其字段表示日期,则需要意识到总是有多种方法来实现。一些国家可以决定使用阴历月份,而其他国家则可以说0年只是10年前。阳历只是将实际时间转换为实际日期字段的一种方法。

关于 XMLGregorianCalendarGregorianCalendar的一些知识

  • XML规范明确指出人类可读的日期是 公历 日期
  • Java的 GregorianCalendar 包含此 “ magic” ,如果该时刻早于定义的转换日期,则将在后台转换为Julian系统
  • 这就是为什么 XMLGregorianCalendar 在初始化期间修改 GregorianCalendar 以禁用此魔术开关的原因(请参见上述JavaDoc的摘录)

现在有趣的部分:

如果不会禁用Julian开关, GregorianCalendar
会假定日历字段来自Julian系统,并且将其转换3天。您以为日期已改了3天,肯定出了点问题,对吧? 不,日期实际上一直都是正确的
,并且包含正确的时间戳记!仅日历向您提供了朱利安字段而不是格里高利字段。我会说这很令人困惑:) [JSR-310在后台笑]。

所以,如果你想用纯阳历工作(即使用所谓的 proleptic阳历 旧日期),你需要初始化日历是这样的:

Calendar calendar = Calendar.getInstance();
((GregorianCalendar) calendar).setGregorianChange(new Date(Long.MIN_VALUE));

您可能会说:calendar.getTime()仍然给我错误的日期。好吧,这是因为java.util.Date.toString()(由调用System.out.println)使用的是default
Calendar,它将为较旧的日期切换到Julian系统。困惑?也许生气(我知道我是:))?

更新2

// Get XML gregorian calendar
XMLGregorianCalendar xmlCalendar = DatatypeFactory.newInstance().newXMLGregorianCalendar();
xmlCalendar.setYear(1); // Watch for octal number representations (you had there 0001)
xmlCalendar.setMonth(1);
xmlCalendar.setDay(1);

// Convert to Calendar as it is easier to work with it
Calendar calendar = xmlCalendar.toGregorianCalendar(); // Proleptic for old dates

// Convert to default calendar (will misinterpret proleptic for Julian, but it is a workaround)
Calendar result = Calendar.getInstance();
result.setTimeZone(calendar.getTimeZone());
result.set(Calendar.YEAR, calendar.get(Calendar.YEAR));
result.set(Calendar.MONTH, calendar.get(Calendar.MONTH));
result.set(Calendar.DAY_OF_MONTH, calendar.get(Calendar.DAY_OF_MONTH));
result.set(Calendar.HOUR_OF_DAY, calendar.get(Calendar.HOUR_OF_DAY));
result.set(Calendar.MINUTE, calendar.get(Calendar.MINUTE));
result.set(Calendar.SECOND, calendar.get(Calendar.SECOND));
result.set(Calendar.MILLISECOND, calendar.get(Calendar.MILLISECOND));

System.out.println(result.getTime());

Disclamer: 此代码是错误的 (结果即时与XML文件中的即时不同),但是OP理解了问题及其后果(请参见此答案下的讨论)。



 类似资料:
  • 问题内容: 我已经写了这个功能: 当我通过日期以及获取输出日期作为 我要去哪里的时候?还做什么,如果我想只得到和 任何帮助表示赞赏 问题答案: 找到以下解决方案…。发布它,因为它也可以帮助其他人:) 输出: 2014-04-24T11:15:00.000 + 02:00

  • 我有一个对象,它有2个XMLGregorianCalendar对象--一个用于日期,另一个用于时间。我使用Jackson对象映射器将日期转换为JSON格式。转换前日期为2014-02-10&时间为11:15:00。转换为JSON后,它变为{“Date”:1392008400000,“Time”:58500000}。在用JSON打印后,如何保留相同的日期和时间格式({“日期”:2014-02-10,

  • 问题内容: 我正在开发Java应用程序,并且有一个(位于中)。我可以轻松地使用以下代码将其更改为公历: 但是我需要在贾拉利日历中注明日期。我搜索了但没有找到任何好的图书馆。您知道可靠可靠的库来进行转换(或从中以Jalali格式创建日期)吗?我不需要实现或算法,因为此问题过于麻烦并且有很多规则,所以我需要一个可靠的解决方案 问题答案: 看看这个:https : //github.com/amirme

  • 我使用JAXB对从服务器获得的XML消息进行UN/Marshing处理。通常,我在字段中获取XMLGregorianCalendar值,这些字段在描述XSD文件中被defind为xs:datetime,因此到XMLGregorianCalendar的转换是由JAXB自动完成的。

  • 我正在尝试将JAXBElement-XMLGregorianCalendar转换为offsetDateTime。我可以这样做,但我想以特定格式转换日期。 代码我用于转换:日历值是2016-03-25T00:00:00 05:30,但我需要隐蔽类型以偏移Date时间所以我正在做下面的转换 作为响应,我在转换后得到的值:2016-03-24T18:30:00Z,而我想要转换后的值为:2016-03-2

  • 周日->明谷-0 周一->赛宁-1 周二->Selasa-2 帮我谢谢