Я использовал Joda Time для манипулирования датой и временем в приложении Java EE, в котором строковое представление даты и времени, отправленное соответствующим клиентом, было преобразовано с использованием следующей процедуры преобразования перед отправкой его в базу данных, т.е. в методе getAsObject()
в конвертер JSF.
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));
Указанный местный часовой пояс на 5 часов и 30 минут опережает UTC
/ GMT
. Следовательно, преобразование в UTC
должно вычесть 5 часов и 30 минут из заданной даты и времени, что происходит правильно с использованием времени Joda. Он отображает следующий вывод, как и ожидалось.
05-Jan-2016 09:34:44 AM +0000
► Было принято смещение часового пояса +0530
вместо +05:30
, поскольку оно зависит от <p:calendar>
, который отправляет смещение пояса в этом формате. Не представляется возможным изменить такое поведение <p:calendar>
(иначе сам этот вопрос был бы не нужен).
Однако то же самое не работает, если попытаться использовать Java Time API в Java 8.
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>
и Java Time в Java 8 работали согласованно и согласованно, хотя <p:calendar>
внутри использует SimpleDateFormat
вместе с java.util.Date
?
Неудачный тестовый сценарий в 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>
Часовой пояс полностью зависит от текущего часового пояса пользователя.
Компонент, не имеющий ничего, кроме одного свойства.
@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.
}
}
Это будет работать неожиданным образом, как показано в предпоследнем примере/посередине первых трех фрагментов кода.
В частности, если вы введете 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
обратно в этот часовой пояс, очевидно, должно повторно отображать ту же дату-время, что и введенное через компонент календаря, который является элементарной функциональностью данного преобразователя.
Обновление:
Конвертер JPA, конвертирующий в java.sql.Timestamp
и java.time.ZonedDateTime
и обратно.
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);
}
}