Как мне обрабатывать java.sql.Dates в нескольких часовых поясах?

У меня есть база данных Oracle, которая находится в восточном часовом поясе США. Я нахожусь в центральном часовом поясе США. Мои EJB-компоненты используют java.sql.Date для полей даты. Поля в БД - Дата:

CREATE TABLE "MYSCHEMA"."TBLBURSTASSGN" 
   (    "BURSTASSGN_ID" NUMBER(*,0) NOT NULL ENABLE, 
"BURSTASSGN_TYPEVID" NUMBER(*,0), 
"BURSTASSGN_CONTACTID" NUMBER(*,0), 
"BURSTASSGN_ASSIGNMENTREF" VARCHAR2(50), 
"BURSTASSGN_STARTDATE" DATE, 
"BURSTASSGN_DUEDATE" DATE, 
"BURSTASSGN_INSTRUCTION" VARCHAR2(2048), 
"BURSTASSGN_STATUSVID" NUMBER(10,0), 
"BURSTASSGN_SUMMARYONLYYN" CHAR(1) DEFAULT 'N' NOT NULL ENABLE);

Когда я ввожу дату в EJB в Central и сохраняю ее в Eastern DB, она сохраняется правильно, например. Вхожу 31 января 2018 и хранится 2018-01-31 00:00:00. Но когда я перезагружаю приложение и оно извлекается из БД, отображается 30 января 2018 г.

Заявление

BurstAssignment burstAssignment = SessionHelper.getSession().getBurstAssignment(assignmentId);
System.out.println("StartDate " + burstAssignment.getStartDate());

SessionBean

@Override
public BurstAssignment getBurstAssignment(Integer id) {
    if (id == null) { return null; }
    return em.find(BurstAssignment.class, id);
}

EntityBean

import com.kable.newsstand.kdsejb.audit.Auditable;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Transient;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import java.sql.Date;
import javax.validation.constraints.NotNull;
import org.hibernate.annotations.Type;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.SequenceGenerator;

@Entity
@Table(name = "TBLBURSTASSGN")
public class BurstAssignment extends Auditable implements Serializable {

    private static final long serialVersionUID = 1L;

    @Column(name = "BURSTASSGN_DUEDATE")
    protected Date dueDate = null;
    @Column(name = "BURSTASSGN_CONTACTID")
    protected Integer contactId = null;
    @Column(name = "BURSTASSGN_INSTRUCTION")
    protected String instruction = null;
    @Column(name = "BURSTASSGN_STARTDATE")
    protected Date startDate = null;
    @Column(name = "BURSTASSGN_ASSIGNMENTREF")
    protected String assignmentRef = null;
    @Column(name = "BURSTASSGN_TYPEVID")
    protected Integer typeVid = null;
    @Id
    @SequenceGenerator(name = "BURSTASSIGNMENT_SEQ", sequenceName = "TBLBURSTASSGN_ID_SEQ", initialValue = 1, allocationSize = 1)
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator = "BURSTASSIGNMENT_SEQ")
    @Column(name = "BURSTASSGN_ID", updatable=false, nullable=false)
    protected Integer id = null;
    @Column(name = "BURSTASSGN_STATUSVID")
    protected Integer statusVid = null;
    @Type(type = "yes_no")
    @NotNull
    @Column(name = "BURSTASSGN_SUMMARYONLYYN")
    protected Boolean summaryOnlyYN = false;

    public BurstAssignment() { super(); }

    public BurstAssignment(BurstAssignment burstAssignment) {
        setDueDate(burstAssignment.getDueDate());
        setContactId(burstAssignment.getContactId());
        setInstruction(burstAssignment.getInstruction());
        setStartDate(burstAssignment.getStartDate());
        setAssignmentRef(burstAssignment.getAssignmentRef());
        setTypeVid(burstAssignment.getTypeVid());
        setId(burstAssignment.getId());
        setStatusVid(burstAssignment.getStatusVid());
        setSummaryOnlyYN(burstAssignment.getSummaryOnlyYN());
    }

    public Date getDueDate() {
        if (dueDate == null) { return null; }
        return new Date(dueDate.getTime());
    }

    public void setDueDate(Date dueDate) {
        if (dueDate == null) { this.dueDate = null; }
        else { this.dueDate = new Date(dueDate.getTime()); }
    }

    public Integer getContactId() {
        return contactId;
    }

    public void setContactId(Integer contactId) {
        this.contactId = contactId;
    }

    public String getInstruction() {
        return instruction;
    }

    public void setInstruction(String instruction) {
        this.instruction = instruction;
    }

    public Date getStartDate() {
        if (startDate == null) { return null; }
        return new Date(startDate.getTime());
    }

    public void setStartDate(Date startDate) {
        if (startDate == null) { this.startDate = null; }
        else { this.startDate = new Date(startDate.getTime()); }
    }

    public String getAssignmentRef() {
        return assignmentRef;
    }

    public void setAssignmentRef(String assignmentRef) {
        this.assignmentRef = assignmentRef;
    }

    public Integer getTypeVid() {
        return typeVid;
    }

    public void setTypeVid(Integer typeVid) {
        this.typeVid = typeVid;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Integer getStatusVid() {
        return statusVid;
    }

    public void setStatusVid(Integer statusVid) {
        this.statusVid = statusVid;
    }

    public Boolean getSummaryOnlyYN() {
        return summaryOnlyYN;
    }

    public void setSummaryOnlyYN(Boolean summaryOnlyYN) {
        this.summaryOnlyYN = summaryOnlyYN;
    }

    @Transient
    @Override
    public String getPrimaryKeyDisplay() {
        StringBuilder sb = new StringBuilder();
        if (id == null) {
            sb.append(" id: null");
        } else {
            sb.append(" id: " + id.toString());
        }
        return sb.toString();
    }
    @Transient
    @Override
    public String toString() {
        return new ToStringBuilder(this).append("dueDate", dueDate).append("contactId", contactId).append("instruction", instruction).append("startDate", startDate).append("assignmentRef", assignmentRef).append("typeVid", typeVid).append("id", id).append("statusVid", statusVid).append("summaryOnlyYN", summaryOnlyYN).toString();
    }

    @Transient
    @Override
    public int hashCode() {
        return new HashCodeBuilder().append(dueDate).append(contactId).append(instruction).append(startDate).append(assignmentRef).append(typeVid).append(id).append(statusVid).append(summaryOnlyYN).toHashCode();
    }

    @Transient
    @Override
    public boolean equals(Object obj) {
        if (obj == null) { return false; }
        if (obj == this) { return true; }
        if (obj.getClass() != getClass()) { return false; }
        BurstAssignment that = (BurstAssignment) obj;
        return new EqualsBuilder().append(dueDate, that.dueDate).append(contactId, that.contactId).append(instruction, that.instruction).append(startDate, that.startDate).append(assignmentRef, that.assignmentRef).append(typeVid, that.typeVid).append(id, that.id).append(statusVid, that.statusVid).append(summaryOnlyYN, that.summaryOnlyYN).isEquals();
    }

}

Вариант 1: Рассудок. Сохраните все в формате UTC (при необходимости сохраните и локальное смещение до UTC). Вариант 2: Сохраните все по местному времени (и сохраните смещение по всемирному координированному времени, иначе вы застряли).

Elliott Frisch 02.05.2018 00:44

Помимо всех других проблем, всегда сохраняйте даты в формате UTC, извлекайте их в формате UTC и отображайте в зависимости от пользователя.

KevinO 02.05.2018 00:44

Возможно, вам стоит прочтите документацию, например раздел «Типы данных Datetime и Поддержка часовых поясов» закона «Руководство по поддержке глобализации баз данных Oracle». Правильная обработка часовых поясов - это тема широкий.

Andreas 02.05.2018 00:56
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
3
40
2

Ответы 2

Ваша проблема в том, что ваши геттеры и сеттеры вызывают getTime(), который преобразует Date в количество миллисекунд, а затем конструктор для Date удаляет компонент времени для даты. Если у вас разные часовые пояса, это может привести к тому, что расхождение в один час будет преобразовано в расхождение в один день.

Не слишком увлекайтесь своими геттерами и сеттерами. Они должны выглядеть вот так.

public Date getDueDate() {
    return dueDate;
}

public void setDueDate(Date dueDate) {
    this.dueDate = dueDate;
}

Я реализовал это, и результат тот же.

bcr666 02.05.2018 16:59

Я также пробовал ответить 2 здесь stackoverflow.com/questions/36641664/…, но безрезультатно.

bcr666 02.05.2018 17:00

Я исправил это для себя, изменив часовой пояс локальной виртуальной машины на восточный, чтобы он соответствовал DBTIMEZONE.

-Duser.timezone=-5:00

Обновление: мое приложение развернуто как приложение Web Start, которое я не могу заставить принять указанную выше строку в jnlp или в командной строке. Поэтому я вставил следующее в основной класс своего приложения.

{
    TimeZone.setDefault(TimeZone.getTimeZone("-5:00"));
}

Другие вопросы по теме