Câu trả lời:
Tôi muốn lùi lại một bước và một cái nhìn hiện đại về câu hỏi 10 năm tuổi này. Các lớp được đề cập, Date
và XMLGregorianCalendar
, bây giờ đã cũ. Tôi thách thức việc sử dụng chúng và đưa ra giải pháp thay thế.
Date
luôn được thiết kế kém và đã hơn 20 tuổi. Điều này rất đơn giản: không sử dụng nó.XMLGregorianCalendar
cũng cũ và có một thiết kế lỗi thời. Theo tôi hiểu, nó được sử dụng để tạo ngày và giờ theo định dạng XML cho các tài liệu XML. Thích 2009-05-07T19:05:45.678+02:00
hay 2009-05-07T17:05:45.678Z
. Các định dạng này đủ phù hợp với ISO 8601 rằng các lớp của java.time, API ngày và giờ Java hiện đại, có thể tạo ra chúng, mà chúng tôi thích.Đối với nhiều mục đích (hầu hết?), Sự thay thế hiện đại cho một Date
sẽ là một Instant
. An Instant
là một điểm trong thời gian (giống như một Date
).
Instant yourInstant = // ...
System.out.println(yourInstant);
Một ví dụ đầu ra từ đoạn trích này:
2009-05-07T17: 05: 45.678Z
Nó giống như chuỗi sau của ví dụ của tôi XMLGregorianCalendar
ở trên. Như hầu hết các bạn đều biết, nó xuất phát từ Instant.toString
việc được gọi ngầm System.out.println
. Với java.time, trong nhiều trường hợp chúng ta không cần chuyển đổi mà trong những ngày cũ, chúng tôi thực hiện giữa Date
, Calendar
, XMLGregorianCalendar
và các lớp khác (trong một số trường hợp chúng tôi làm chuyển đổi cần thiết, tuy nhiên, tôi đang hiển thị cho bạn một vài trong phần tiếp theo) .
Không Date
phải cũng không Instant
có múi giờ hay bù UTC. Câu trả lời được chấp nhận trước đây và vẫn được bình chọn cao nhất bởi Ben Noland sử dụng múi giờ mặc định hiện tại của JVM để chọn phần bù của XMLGregorianCalendar
. Để bao gồm một phần bù trong một đối tượng hiện đại, chúng ta sử dụng một OffsetDateTime
. Ví dụ:
ZoneId zone = ZoneId.of("America/Asuncion");
OffsetDateTime dateTime = yourInstant.atZone(zone).toOffsetDateTime();
System.out.println(dateTime);
2009-05-07T13: 05: 45.678-04: 00
Một lần nữa điều này phù hợp với định dạng XML. Nếu bạn muốn sử dụng lại cài đặt múi giờ JVM hiện tại, hãy đặt zone
thành ZoneId.systemDefault()
.
Có nhiều cách để chuyển đổi Instant
sang XMLGregorianCalendar
. Tôi sẽ trình bày một cặp vợ chồng, mỗi người có ưu và nhược điểm của nó. Đầu tiên, giống như XMLGregorianCalendar
một chuỗi tạo ra 2009-05-07T17:05:45.678Z
, nó cũng có thể được xây dựng từ một chuỗi như vậy:
String dateTimeString = yourInstant.toString();
XMLGregorianCalendar date2
= DatatypeFactory.newInstance().newXMLGregorianCalendar(dateTimeString);
System.out.println(date2);
2009-05-07T17: 05: 45.678Z
Pro: nó ngắn và tôi không nghĩ nó mang lại bất ngờ nào. Con: Đối với tôi, nó giống như một sự lãng phí định dạng tức thời thành một chuỗi và phân tích lại nó.
ZonedDateTime dateTime = yourInstant.atZone(zone);
GregorianCalendar c = GregorianCalendar.from(dateTime);
XMLGregorianCalendar date2 = DatatypeFactory.newInstance().newXMLGregorianCalendar(c);
System.out.println(date2);
2009-05-07T13: 05: 45.678-04: 00
Pro: Đây là chuyển đổi chính thức. Kiểm soát bù đến một cách tự nhiên. Con: Nó trải qua nhiều bước hơn và do đó dài hơn.
Nếu bạn có một Date
đối tượng lỗi thời từ API kế thừa mà bạn không thể đủ khả năng để thay đổi ngay bây giờ, hãy chuyển đổi nó thành Instant
:
Instant i = yourDate.toInstant();
System.out.println(i);
Đầu ra giống như trước:
2009-05-07T17: 05: 45.678Z
Nếu bạn muốn kiểm soát phần bù, hãy chuyển đổi thành một OffsetDateTime
cách tương tự như trên.
Nếu bạn đã có một kiểu cũ Date
và hoàn toàn cần một kiểu cũ XMLGregorianCalendar
, chỉ cần sử dụng câu trả lời của Ben Noland.
GregorianCalendar c = new GregorianCalendar();
c.setTime(yourDate);
XMLGregorianCalendar date2 = DatatypeFactory.newInstance().newXMLGregorianCalendar(c);
Đối với những người có thể kết thúc ở đây, tìm kiếm chuyển đổi ngược lại (từ XMLGregorianCalendar
đến Date
):
XMLGregorianCalendar xcal = <assume this is initialized>;
java.util.Date dt = xcal.toGregorianCalendar().getTime();
Đây là một phương pháp để chuyển đổi từ GregorianCalWiki sang XMLGregorianCalWiki; Tôi sẽ để lại phần chuyển đổi từ java.util.Date sang GregorianCalWiki như một bài tập cho bạn:
import java.util.GregorianCalendar;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;
public class DateTest {
public static void main(final String[] args) throws Exception {
GregorianCalendar gcal = new GregorianCalendar();
XMLGregorianCalendar xgcal = DatatypeFactory.newInstance()
.newXMLGregorianCalendar(gcal);
System.out.println(xgcal);
}
}
EDIT: Slooow :-)
Một ví dụ một dòng sử dụng thư viện Joda-Time :
XMLGregorianCalendar xgc = DatatypeFactory.newInstance().newXMLGregorianCalendar(new DateTime().toGregorianCalendar());
Tín dụng cho Nicolas Mommaerts từ nhận xét của mình trong câu trả lời được chấp nhận .
Chỉ cần nghĩ rằng tôi sẽ thêm giải pháp của mình bên dưới, vì các câu trả lời ở trên không đáp ứng được nhu cầu chính xác của tôi. Lược đồ Xml của tôi yêu cầu tách biệt các yếu tố Ngày và Giờ, không phải là trường DateTime đơn lẻ. Trình xây dựng XMLGregorianCalWiki tiêu chuẩn được sử dụng ở trên sẽ tạo ra trường DateTime
Lưu ý rằng có một vài gothca, chẳng hạn như phải thêm một vào tháng (vì java tính từ 0 tháng).
GregorianCalendar cal = new GregorianCalendar();
cal.setTime(yourDate);
XMLGregorianCalendar xmlDate = DatatypeFactory.newInstance().newXMLGregorianCalendarDate(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH)+1, cal.get(Calendar.DAY_OF_MONTH), 0);
XMLGregorianCalendar xmlTime = DatatypeFactory.newInstance().newXMLGregorianCalendarTime(cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE), cal.get(Calendar.SECOND), 0);
Tôi hy vọng mã hóa của tôi ở đây là đúng; D Để làm cho nó nhanh hơn, chỉ cần sử dụng lệnh gọi getInstance () xấu xí của GregorianCalWiki thay vì gọi hàm tạo:
import java.util.GregorianCalendar;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;
public class DateTest {
public static void main(final String[] args) throws Exception {
// do not forget the type cast :/
GregorianCalendar gcal = (GregorianCalendar) GregorianCalendar.getInstance();
XMLGregorianCalendar xgcal = DatatypeFactory.newInstance()
.newXMLGregorianCalendar(gcal);
System.out.println(xgcal);
}
}
GregorianCalendar.getInstance()
là tương đương với Calendar.getInstance()
. Không Calendar.getInstance()
thể làm cho nó nhanh hơn, bởi vì nó sử dụng tương tự new GregorianCalendar()
, nhưng trước đó nó cũng kiểm tra ngôn ngữ mặc định và có thể tạo lịch Nhật Bản hoặc Buddish, vì vậy đối với một số người dùng may mắn thì sẽ như ClassCastException
vậy!
Giả sử bạn đang giải mã hoặc mã hóa xml và sử dụng JAXB
, thì có thể thay thế hoàn toàn ràng buộc dateTime và sử dụng một cái gì đó khác hơn `XMLGregorianCalWiki 'cho mỗi ngày trong lược đồ.
Theo cách đó bạn có thể có JAXB
thực hiện các công cụ lặp đi lặp lại trong khi bạn có thể dành thời gian để viết mã tuyệt vời mang lại giá trị.
Ví dụ cho jodatime DateTime
: (Làm điều này với java.util.Date cũng sẽ hoạt động - nhưng với một số hạn chế nhất định. Tôi thích jodatime và nó được sao chép từ mã của tôi để tôi biết nó hoạt động ...)
<jxb:globalBindings>
<jxb:javaType name="org.joda.time.LocalDateTime" xmlType="xs:dateTime"
parseMethod="test.util.JaxbConverter.parseDateTime"
printMethod="se.seb.bis.test.util.JaxbConverter.printDateTime" />
<jxb:javaType name="org.joda.time.LocalDate" xmlType="xs:date"
parseMethod="test.util.JaxbConverter.parseDate"
printMethod="test.util.JaxbConverter.printDate" />
<jxb:javaType name="org.joda.time.LocalTime" xmlType="xs:time"
parseMethod="test.util.JaxbConverter.parseTime"
printMethod="test.util.JaxbConverter.printTime" />
<jxb:serializable uid="2" />
</jxb:globalBindings>
Và bộ chuyển đổi:
public class JaxbConverter {
static final DateTimeFormatter dtf = ISODateTimeFormat.dateTimeNoMillis();
static final DateTimeFormatter df = ISODateTimeFormat.date();
static final DateTimeFormatter tf = ISODateTimeFormat.time();
public static LocalDateTime parseDateTime(String s) {
try {
if (StringUtils.trimToEmpty(s).isEmpty())
return null;
LocalDateTime r = dtf.parseLocalDateTime(s);
return r;
} catch (Exception e) {
throw new IllegalArgumentException(e);
}
}
public static String printDateTime(LocalDateTime d) {
try {
if (d == null)
return null;
return dtf.print(d);
} catch (Exception e) {
throw new IllegalArgumentException(e);
}
}
public static LocalDate parseDate(String s) {
try {
if (StringUtils.trimToEmpty(s).isEmpty())
return null;
return df.parseLocalDate(s);
} catch (Exception e) {
throw new IllegalArgumentException(e);
}
}
public static String printDate(LocalDate d) {
try {
if (d == null)
return null;
return df.print(d);
} catch (Exception e) {
throw new IllegalArgumentException(e);
}
}
public static String printTime(LocalTime d) {
try {
if (d == null)
return null;
return tf.print(d);
} catch (Exception e) {
throw new IllegalArgumentException(e);
}
}
public static LocalTime parseTime(String s) {
try {
if (StringUtils.trimToEmpty(s).isEmpty())
return null;
return df.parseLocalTime(s);
} catch (Exception e) {
throw new IllegalArgumentException(e);
}
}
Xem ở đây: làm thế nào để thay thế XmlGregorianCalWiki theo ngày?
Nếu bạn hài lòng khi chỉ ánh xạ tới tức thời dựa trên múi giờ + dấu thời gian và múi giờ ban đầu không thực sự phù hợp, thì java.util.Date
có lẽ cũng tốt.
Kiểm tra mã này: -
/* Create Date Object */
Date date = new Date();
XMLGregorianCalendar xmlDate = null;
GregorianCalendar gc = new GregorianCalendar();
gc.setTime(date);
try{
xmlDate = DatatypeFactory.newInstance().newXMLGregorianCalendar(gc);
}
catch(Exception e){
e.printStackTrace();
}
System.out.println("XMLGregorianCalendar :- " + xmlDate);
Bạn có thể xem ví dụ đầy đủ ở đây
ZonedDateTime
lớp và các phương thức chuyển đổi mới được thêm vào các lớp kế thừa. Chi tiết trong câu trả lời này của Ole VV