Java 如何在JPA中保持原始时区
特别是在SQL server中(我假设还有其他类型),有一种datetimeoffset类型可以使用GMT以外的偏移量存储时间戳(类似于“1998-01-01 15:00:00.0000000+06:00”) 但是,试图将日历持久化到数据库会自动将其转换为GMT(尽管我不知道是谁在做JPA、Hibernate还是sqljdbc),所以它看起来像“1998-01-01 9:00:00.0000000+00:00”Java 如何在JPA中保持原始时区,java,hibernate,jpa,Java,Hibernate,Jpa,特别是在SQL server中(我假设还有其他类型),有一种datetimeoffset类型可以使用GMT以外的偏移量存储时间戳(类似于“1998-01-01 15:00:00.0000000+06:00”) 但是,试图将日历持久化到数据库会自动将其转换为GMT(尽管我不知道是谁在做JPA、Hibernate还是sqljdbc),所以它看起来像“1998-01-01 9:00:00.0000000+00:00” 有没有办法防止某些属性出现这种情况?尽管可能有其他使用SQL数据库的解决方案,但我始
有没有办法防止某些属性出现这种情况?尽管可能有其他使用SQL数据库的解决方案,但我始终遵循以下方法: 在数据库或文件中,我总是以UTC存储时间,没有例外。 如果必须保留时区(例如UI),则我会将UTC偏移量存储在一个额外字段中 这样我就有了正确的时间,如果需要的话,时区也会保留下来。找到了一种方法
package testers.jpa.beans;
import java.io.Serializable;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.persistence.Transient;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@Entity
@Table(name = "dbo.tester")
//Ignore the actual calendar object here because the jsonner strips the timezone info from it
@JsonIgnoreProperties(value = { "date" })
@NamedQuery(name = "NameAndOffset.purge", query = "DELETE FROM NameAndOffset")
public class NameAndOffset implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private Calendar date;
public NameAndOffset() {
}
public NameAndOffset(String name, Calendar date) {
this.name = name;
this.date = date;
}
@Id
@Basic
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/**
* Return a string representation of the {@link NameAndOffset#date} object.<br/>
* This will be used by JPA and the Jsonner to prevent losing the timezone
* information<br/>
* <br/>
* For this to work properly you must tell both the Json mapper and JPA to
* ignore anything else that relates to the {@link NameAndOffset#date} field
*
* @return A String representation of the {@link NameAndOffset#date} field,
* formatted for SQL Server's datetimeoffset data type
*/
@Basic
@Column(name = "date")
public String getDateTimeOffset() {
return new OffsetDateFormat().formatCalendar(date);
}
public void setDateTimeOffset(String date) throws ParseException {
this.date = new OffsetDateFormat().parseCalendar(date);
}
//Ignore the actual calendar object here because JPA strips the timezone info from it
@Transient
public Calendar getDate() {
return date;
}
public void setDate(Calendar date) throws ParseException {
this.date = date;
}
class OffsetDateFormat extends SimpleDateFormat {
private static final long serialVersionUID = 1L;
private static final String OFFSET_FORMAT = "yyyy-MM-dd HH:mm:ss.S Z";
public OffsetDateFormat() {
super(OFFSET_FORMAT);
}
public Calendar parseCalendar(String source) throws ParseException {
//pull out the colon in the offset
int timeZoneColon = source.lastIndexOf(":");
String nocolon = source.substring(0, timeZoneColon) + source.substring(timeZoneColon + 1);
Calendar cal = Calendar.getInstance();
cal.setTime(parse(nocolon));
//after parsing, the timezone of this DateFormatter changes to whatever was represented in the string
//make sure the new calendar reflects this
cal.setTimeZone(getTimeZone());
return cal;
}
public String formatCalendar(Calendar calendar) {
setTimeZone(calendar.getTimeZone());
String nocolon = format(calendar.getTime());
//add the colon
StringBuffer sb = new StringBuffer(nocolon.substring(0, nocolon.length() - 2)).append(":").append(nocolon.substring(nocolon.length() - 2));
return sb.toString();
}
}
}
packagetesters.jpa.beans;
导入java.io.Serializable;
导入java.text.ParseException;
导入java.text.simpleDataFormat;
导入java.util.Calendar;
导入javax.persistence.Basic;
导入javax.persistence.Column;
导入javax.persistence.Entity;
导入javax.persistence.Id;
导入javax.persistence.NamedQuery;
导入javax.persistence.Table;
导入javax.persistence.Transient;
导入com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@实体
@表(name=“dbo.tester”)
//忽略此处的实际日历对象,因为jsonner会从中删除时区信息
@JsonIgnoreProperties(值={“日期”})
@NamedQuery(name=“NameAndOffset.purge”,query=“从NameAndOffset删除”)
公共类名称和偏移量实现可序列化{
私有静态最终长serialVersionUID=1L;
私有字符串名称;
私人日历日期;
公共名称和偏移量(){
}
公共名称和偏移量(字符串名称、日历日期){
this.name=名称;
this.date=日期;
}
@身份证
@基本的
公共字符串getName(){
返回名称;
}
公共void集合名(字符串名){
this.name=名称;
}
/**
*返回{@link name和offset#date}对象的字符串表示形式。
*JPA和Jsonner将使用它来防止丢失时区
*信息
*
*为了让它正常工作,您必须告诉Json映射器和JPA
*忽略与{@link name和offset#date}字段相关的任何其他内容
*
*@返回{@link name和offset#date}字段的字符串表示形式,
*为SQL Server的datetimeoffset数据类型格式化
*/
@基本的
@列(name=“date”)
公共字符串getDateTimeOffset(){
返回新的OffsetDateFormat().formatCalendar(日期);
}
public void setDateTimeOffset(字符串日期)引发异常{
this.date=新的OffsetDateFormat().parseCalendar(日期);
}
//忽略此处的实际日历对象,因为JPA会从中删除时区信息
@短暂的
公共日历getDate(){
返回日期;
}
公共无效设置日期(日历日期)引发异常{
this.date=日期;
}
类OffsetDateFormat扩展了SimpleDateFormat{
私有静态最终长serialVersionUID=1L;
私有静态最终字符串偏移量_FORMAT=“yyyy-MM-dd HH:MM:ss.S Z”;
公共OffsetDateFormat(){
super(OFFSET_格式);
}
公共日历parseCalendar(字符串源)引发ParseException{
//拉出偏移量中的冒号
int timezoneclon=source.lastIndexOf(“:”);
字符串nocolon=source.substring(0,timezoneclon)+source.substring(timezoneclon+1);
Calendar cal=Calendar.getInstance();
cal.setTime(解析(nocolon));
//解析后,此DateFormatter的时区将更改为字符串中表示的任何时区
//确保新日历反映了这一点
cal.setTimeZone(getTimeZone());
返回cal;
}
公共字符串格式日历(日历日历){
setTimeZone(calendar.getTimeZone());
字符串nocolon=format(calendar.getTime());
//添加冒号
StringBuffer sb=新的StringBuffer(nocolon.substring(0,nocolon.length()-2)).append(“:”).append(nocolon.substring(nocolon.length()-2));
使某人返回字符串();
}
}
}
是否有任何原因,为什么要使用UTC(=GMT)以外的其他偏移量?是的,我想记住给我的偏移量。所以当我把它发回来的时候,它的偏移量是一样的。(我希望客户在他们的时区中看到它,而不是GMT,不是我的。)但我仍然希望保存偏移量,因为我必须根据此表中恰好位于不同时区的其他值进行计算。我理解正确了吗,您不希望使用UTC作为参考吗?因此不需要GMT+6.0?你想要另一个引用,比如EST+03:00?不,结尾的“+6:00”是需要的,但每当我坚持到数据库时,它就会改变时间并使其成为“+0:00”Alex,我不能仅仅告诉一家公司“按原则”删除一个长期SQL表。因此,我询问如何通过JPA接口正确使用SQL中提供的datetimeoffset。如果我问过桌子的正确设计,那么你的回答完全符合上下文。但就目前情况而言,你仅仅说不应该这样做,并不能解决我目前的问题。事实证明,我能够想出一个相当简单的解决方案。@mike我为所有能够定义/更改其数据结构的其他人提供了一个解决方案。这个解决方案的问题是,如果您必须使用between或任何其他查询来过滤日期,因为它在您的数据库中存储为字符串。