tl;博士
- 尽可能使用现代的java.time 类,而不是糟糕的遗留类。
- 始终指定所需/预期的时区或与 UTC 的偏移量,而不是隐式依赖 JVM 的当前默认值。
示例代码(无异常处理):
XMLGregorianCalendar xgc =
DatatypeFactory // Data-type converter.
.newInstance() // Instantiate a converter object.
.newXMLGregorianCalendar( // Converter going from `GregorianCalendar` to `XMLGregorianCalendar`.
GregorianCalendar.from( // Convert from modern `ZonedDateTime` class to legacy `GregorianCalendar` class.
LocalDate // Modern class for representing a date-only, without time-of-day and without time zone.
.parse( "2014-01-07" ) // Parsing strings in standard ISO 8601 format is handled by default, with no need for custom formatting pattern.
.atStartOfDay( ZoneOffset.UTC ) // Determine the first moment of the day as seen in UTC. Returns a `ZonedDateTime` object.
) // Returns a `GregorianCalendar` object.
) // Returns a `XMLGregorianCalendar` object.
;
将仅日期输入字符串解析为 XMLGregorianCalendar 类的对象
尽可能避免使用糟糕的遗留日期时间类,例如XMLGregorianCalendar、GregorianCalendar、Calendar 和Date。仅使用现代 java.time 类。
当出现"2014-01-07" 等字符串时,解析为LocalDate。
LocalDate.parse( "2014-01-07" )
要获取带有时间的日期,假设您想要一天中的第一刻,请指定一个时区。让 java.time 确定一天中的第一个时刻,因为在某些日期的某些区域中,它并不总是 00:00:00.0。
LocalDate.parse( "2014-01-07" )
.atStartOfDay( ZoneId.of( "America/Montreal" ) )
这将返回一个ZonedDateTime 对象。
ZonedDateTime zdt =
LocalDate
.parse( "2014-01-07" )
.atStartOfDay( ZoneId.of( "America/Montreal" ) )
;
zdt.toString() = 2014-01-07T00:00-05:00[美国/蒙特利尔]
但显然,您希望以 UTC 格式显示一天的开始时间(零时分秒的偏移量)。所以我们指定ZoneOffset.UTC 常量作为我们的ZoneId 参数。
ZonedDateTime zdt =
LocalDate
.parse( "2014-01-07" )
.atStartOfDay( ZoneOffset.UTC )
;
zdt.toString() = 2014-01-07T00:00Z
末尾的Z 表示UTC(零偏移),发音为“Zulu”。
如果您必须使用旧类,请转换为GregorianCalendar,它是Calendar 的子类。
GregorianCalendar gc = GregorianCalendar.from( zdt ) ;
gc.toString() = java.util.GregorianCalendar[time=1389052800000,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="UTC",offset=0 ,dstSavings=0,useDaylight=false,transitions=0,lastRule=null],firstDayOfWeek=2,minimalDaysInFirstWeek=4,ERA=1,YEAR=2014,MONTH=0,WEEK_OF_YEAR=2,WEEK_OF_MONTH=2,DAY_OF_MONTH=7, DAY_OF_YEAR=7,DAY_OF_WEEK=3,DAY_OF_WEEK_IN_MONTH=1,AM_PM=0,HOUR=0,HOUR_OF_DAY=0,MINUTE=0,SECOND=0,MILLISECOND=0,ZONE_OFFSET=0,DST_OFFSET=0]
显然,您确实需要一个遗留类XMLGregorianCalendar 的对象。如果调用代码无法更新为使用java.time,请转换。
XMLGregorianCalendar xgc =
DatatypeFactory
.newInstance()
.newXMLGregorianCalendar( gc )
;
实际上,该代码需要 try-catch。
try
{
XMLGregorianCalendar xgc =
DatatypeFactory
.newInstance()
.newXMLGregorianCalendar( gc );
}
catch ( DatatypeConfigurationException e )
{
e.printStackTrace();
}
xgc = 2014-01-07T00:00:00.000Z
通过适当的异常处理将所有这些放在一起。
// Given an input string such as "2014-01-07", return a `XMLGregorianCalendar` object
// representing first moment of the day on that date as seen in UTC.
static public XMLGregorianCalendar getXMLGregorianCalendar ( String input )
{
Objects.requireNonNull( input );
if( input.isBlank() ) { throw new IllegalArgumentException( "Received empty/blank input string for date argument. Message # 11818896-7412-49ba-8f8f-9b3053690c5d." ) ; }
XMLGregorianCalendar xgc = null;
ZonedDateTime zdt = null;
try
{
zdt =
LocalDate
.parse( input )
.atStartOfDay( ZoneOffset.UTC );
}
catch ( DateTimeParseException e )
{
throw new IllegalArgumentException( "Faulty input string for date does not comply with standard ISO 8601 format. Message # 568db0ef-d6bf-41c9-8228-cc3516558e68." );
}
GregorianCalendar gc = GregorianCalendar.from( zdt );
try
{
xgc =
DatatypeFactory
.newInstance()
.newXMLGregorianCalendar( gc );
}
catch ( DatatypeConfigurationException e )
{
e.printStackTrace();
}
Objects.requireNonNull( xgc );
return xgc ;
}
用法。
String input = "2014-01-07";
XMLGregorianCalendar xgc = App.getXMLGregorianCalendar( input );
转储到控制台。
System.out.println( "xgc = " + xgc );
xgc = 2014-01-07T00:00:00.000Z
现代日期时间类与旧版
日期时间!= 字符串
不要将日期时间值与其文本表示混为一谈。我们解析字符串以获取日期时间对象,并要求日期时间对象生成一个字符串来表示其值。日期时间对象没有“格式”,只有字符串有格式。
因此,请将您的思维转变为两种不同的模式:模型和演示。确定您想到的日期值,应用适当的时区作为模型。当您需要显示该值时,以用户期望的特定格式生成一个字符串。
避免使用旧的日期时间类
问题和其他答案都使用旧的麻烦的日期时间类,现在被 java.time 类取代。
ISO 8601
您的输入字符串"2014-01-07" 是标准ISO 8601 格式。
中间的T 将日期部分与时间部分分开。
末尾的Z是祖鲁语的缩写,意思是UTC。
幸运的是,java.time 类在解析/生成字符串时默认使用 ISO 8601 格式。所以不需要指定格式模式。
LocalDate
LocalDate 类表示没有时间和时区的仅日期值。
LocalDate ld = LocalDate.parse( "2014-01-07" ) ;
ld.toString(): 2014-01-07
一天的开始ZonedDateTime
如果您想查看当天的第一时刻,请指定 ZoneId 时区以获取时间线上的时刻,即 ZonedDateTime。时区至关重要,因为全球各地的日期因地区而异。法国巴黎午夜过后几分钟是新的一天,而魁北克蒙特利尔仍是“昨天”。
永远不要假设一天从 00:00:00 开始。夏令时 (DST) 等异常意味着一天可能从一天中的另一个时间开始,例如 01:00:00。让 java.time 确定第一个时刻。
ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = ld.atStartOfDay( z ) ;
zdt.toString(): 2014-01-07T00:00:00Z
对于您想要的格式,使用预定义的格式化程序DateTimeFormatter.ISO_LOCAL_DATE_TIME 生成一个字符串,然后用空格替换中间的T。
String output = zdt.format( DateTimeFormatter.ISO_LOCAL_DATE_TIME )
.replace( "T" , " " ) ;
2014-01-07 00:00:00
关于java.time
java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.Date、Calendar 和 SimpleDateFormat。
Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。
要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310。
从哪里获得 java.time 类?
ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如Interval、YearWeek、YearQuarter 和more。