【发布时间】:2016-04-25 07:24:45
【问题描述】:
我一直在 Java EE 应用程序中使用 Joda Time 进行日期时间操作,其中关联客户端提交的日期时间字符串表示在提交到数据库之前已使用以下转换例程进行转换,即在JSF 转换器中的getAsObject() 方法。
org.joda.time.format.DateTimeFormatter formatter = org.joda.time.format.DateTimeFormat.forPattern("dd-MMM-yyyy hh:mm:ss a Z").withZone(DateTimeZone.UTC);
DateTime dateTime = formatter.parseDateTime("05-Jan-2016 03:04:44 PM +0530");
System.out.println(formatter.print(dateTime));
给出的本地时区比UTC / GMT 早5 小时30 分钟。因此,转换为UTC 应从给定的日期时间中减去 5 小时 30 分钟,这使用 Joda 时间正确发生。它按预期显示以下输出。
05-Jan-2016 09:34:44 AM +0000
► 时区偏移+0530 代替+05:30 已被采用,因为它依赖于<p:calendar>,它以这种格式提交时区偏移。似乎不可能改变<p:calendar> 的这种行为(否则这个问题本身就不需要)。
但是,如果尝试使用 Java 8 中的 Java Time API,同样的事情会被破坏。
java.time.format.DateTimeFormatter formatter = java.time.format.DateTimeFormatter.ofPattern("dd-MMM-yyyy hh:mm:ss a Z").withZone(ZoneOffset.UTC);
ZonedDateTime dateTime = ZonedDateTime.parse("05-Jan-2016 03:04:44 PM +0530", formatter);
System.out.println(formatter.format(dateTime));
它意外显示以下错误输出。
05-Jan-2016 03:04:44 PM +0000
显然,转换的日期时间不符合它应该转换的UTC。
它需要进行以下更改才能正常工作。
java.time.format.DateTimeFormatter formatter = java.time.format.DateTimeFormatter.ofPattern("dd-MMM-yyyy hh:mm:ss a z").withZone(ZoneOffset.UTC);
ZonedDateTime dateTime = ZonedDateTime.parse("05-Jan-2016 03:04:44 PM +05:30", formatter);
System.out.println(formatter.format(dateTime));
依次显示以下内容。
05-Jan-2016 09:34:44 AM Z
Z 已替换为 z,+0530 已替换为 +05:30。
为什么这两个 API 在这方面有不同的行为在这个问题中被完全忽略了。
尽管<p:calendar> 内部使用SimpleDateFormat 和java.util.Date,但Java 8 中的<p:calendar> 和Java Time 可以考虑采用哪种中间方法以一致且连贯地工作?
JSF 中不成功的测试场景。
转换器:
@FacesConverter("dateTimeConverter")
public class DateTimeConverter implements Converter {
@Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (value == null || value.isEmpty()) {
return null;
}
try {
return ZonedDateTime.parse(value, DateTimeFormatter.ofPattern("dd-MMM-yyyy hh:mm:ss a Z").withZone(ZoneOffset.UTC));
} catch (IllegalArgumentException | DateTimeException e) {
throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, null, "Message"), e);
}
}
@Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (value == null) {
return "";
}
if (!(value instanceof ZonedDateTime)) {
throw new ConverterException("Message");
}
return DateTimeFormatter.ofPattern("dd-MMM-yyyy hh:mm:ss a z").withZone(ZoneId.of("Asia/Kolkata")).format(((ZonedDateTime) value));
// According to a time zone of a specific user.
}
}
XHTML 有<p:calendar>。
<p:calendar id="dateTime"
timeZone="Asia/Kolkata"
pattern="dd-MMM-yyyy hh:mm:ss a Z"
value="#{bean.dateTime}"
showOn="button"
required="true"
showButtonPanel="true"
navigator="true">
<f:converter converterId="dateTimeConverter"/>
</p:calendar>
<p:message for="dateTime"/>
<p:commandButton value="Submit" update="display" actionListener="#{bean.action}"/><br/><br/>
<h:outputText id="display" value="#{bean.dateTime}">
<f:converter converterId="dateTimeConverter"/>
</h:outputText>
时区完全透明地依赖于用户当前的时区。
bean 只有一个属性。
@ManagedBean
@ViewScoped
public class Bean implements Serializable {
private ZonedDateTime dateTime; // Getter and setter.
private static final long serialVersionUID = 1L;
public Bean() {}
public void action() {
// Do something.
}
}
这将以意想不到的方式工作,如倒数第二个示例/前三个代码 sn-ps 中的中间所示。
具体来说,如果你输入05-Jan-2016 12:00:00 AM +0530,它会重新显示05-Jan-2016 05:30:00 AM IST,因为原来转换器中05-Jan-2016 12:00:00 AM +0530到UTC的转换失败了。
从偏移量为+05:30 的本地时区转换为UTC,然后从UTC 转换回该时区显然必须重新显示通过日历组件输入的相同日期时间,这是基本功能给定的转换器。
更新:
与java.sql.Timestamp 和java.time.ZonedDateTime 相互转换的JPA 转换器。
import java.sql.Timestamp;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
@Converter(autoApply = true)
public final class JodaDateTimeConverter implements AttributeConverter<ZonedDateTime, Timestamp> {
@Override
public Timestamp convertToDatabaseColumn(ZonedDateTime dateTime) {
return dateTime == null ? null : Timestamp.from(dateTime.toInstant());
}
@Override
public ZonedDateTime convertToEntityAttribute(Timestamp timestamp) {
return timestamp == null ? null : ZonedDateTime.ofInstant(timestamp.toInstant(), ZoneOffset.UTC);
}
}
【问题讨论】:
标签: jsf primefaces calendar timezone java-time