【问题标题】:save ZonedDateTime in MySQL and Glassfish在 MySQL 和 Glassfish 中保存 ZonedDateTime
【发布时间】:2015-08-09 21:27:41
【问题描述】:

这是我在这个论坛上的第一个问题,请耐心等待。

Oracle 说“ZonedDateTime 是具有完全限定时区的日期和时间。这可以解决任何时间点的偏移量。经验法则是,如果您想在不依赖上下文的情况下表示日期和时间对于特定服务器,您应该使用 ZonedDateTime。”这正是我想要做的,因为应用程序正在处理全局交互,但 MySQL 似乎只将 DATETIME 保存为 TIMESTAMP,但它显然将其保存为 UTC,以便它可以转换为任何时区。我们将在其上运行的服务器将在多个时区运行,我们不知道哪个服务器将运行在哪里,因为云提供商将根据需求和维护动态移动它们。

因此,在此应用程序中维护日期/时间/区域似乎完全适合新的 ZonedDateTime 构造,但我一直在试图将 PrimeFaces 和其他组件代码仍然提供的遗留 Date 之间的所有内容保持一致时反复混淆和 MySQL 想要处理最终将在 2038 年过期的时间戳。

我们不想使用任何外部日期库,例如 Joda 或 Apache。

我的问题相当直截了当,但答案对我来说似乎难以捉摸,而且细微差别似乎很多:将 java ZonedDateTime 保存到 MySQL 数据库的最佳实践是什么,将其读回以便工作用户可以通过 java 全局执行即时计算,这对本地用户来说是正确的,并且无论 Glassfish 服务器或 MySQL 服务器的位置可能彼此和每天都处于不同的时区,都将是正确的天?

【问题讨论】:

    标签: java mysql primefaces jsf-2.2 glassfish-4


    【解决方案1】:

    我认为这种方式让我们大吃一惊:当 MySQL 保存为时间戳时,它会将日期保存为 UTC,所以只要我这样做,MySQL 的位置就无关紧要。

    Glassfish 可以通过查询服务器告诉您它在哪里,但它也可以为家庭办公室设置一个属性,为您提供一个在服务器所在的任何地方都一致的操作基础。你可以在 web.xml 中做到这一点

    <context-param>
        <param-name>GLASSFISH_HOME_TIME_ZONE</param-name>
        <param-value>America/New_York</param-value>
    </context-param>
    

    数据 bean 需要完成大部分工作,以便它与所有数据使用中的数据保持一致。未更新到 ZonedDateTime 或仅部分更新的组件库的问题通常会使用 getter 调用数据,因此使用重载应该允许组件库找到它喜欢的特定方法。我创建了一个看起来像这样的数据 bean:

    公共类 DataBean {

    private final ZoneId GLASSFISH_HOME_TIME_ZONE = ZoneId.of(FacesContext.getCurrentInstance().getExternalContext().getInitParameter( "GLASSFISH_HOME_TIME_ZONE"));
    private ZonedDateTime dateToUseInGlassfish = null;
    
    public DataBean (
        Timestamp dateFromMySQL) 
    {
        if ( dateFromMySQL == null ) {
            this.dateToUseInGlassfish = null;
        } else {
            this.dateToUseInGlassfish = LocalDateTime.ofInstant(dateFromMySQL.toInstant(), GLASSFISH_HOME_TIME_ZONE ).atZone( GLASSFISH_HOME_TIME_ZONE );
        }
    }
    
    /** Formatter for Date/Time */
    private final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("MM/dd/yyyy ' at ' h:mm a z");
    
    /** Formatter for Date only */
    private final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("MM/dd/yyyy");
    
    /** Get the date string formatted with date and time */
    public String getDateToUseInGlassfishDateTimeFormatted() {
        if ( dateToUseInGlassfish == null ) { return null; }
        String formattedDate = dateTimeFormatter.format( dateToUseInGlassfish );
        return formattedDate;
    }
    
    /** Get the date string formatted with date only */
    public String getgetDateToUseInGlassfishDateFormatted() {
        if ( dateToUseInGlassfish == null) { return null; }
        String formattedDate = dateFormatter.format( dateToUseInGlassfish );
        return formattedDate;
    }
    
    /** Get the date ZDT formatted (for calculations) */
    public ZonedDateTime getgetDateToUseInGlassfish() {
        return dateToUseInGlassfish;
    }
    
    /** Get the date as Date (for component libraries that automatically fetch then throw up with ZDT) */
    public Date getDateToUseInGlassfishDate() {
        if ( dateToUseInGlassfish == null) { return null; }
        return Date.from( dateToUseInGlassfish.toInstant());
    }
    
    /** Set the date from ZDT (results from calculations stored in bean) */
    public void setDateToUseInGlassfish( ZonedDateTime dateToUseInGlassfish ) {
        this.dateToUseInGlassfish = dateToUseInGlassfish;
    }
    
    /** Set the date from Date with an automatic convert to ZDT */
    public void setDateToUseInGlassfish( Date dateToUseInGlassfish ) {
        if (dateToUseInGlassfish == null) {
            this.dateToUseInGlassfish = null;
        } else {
            this.dateToUseInGlassfish = LocalDateTime.ofInstant( Instant.ofEpochMilli( dateToUseInGlassfish.getTime()), GLASSFISH_HOME_TIME_ZONE ).atZone( GLASSFISH_HOME_TIME_ZONE );
        }
    }
    

    从 MySQL 获取日期作为时间戳是获取它作为 UTC 时间点,它看起来像这样:

    ResultSet resultSet = preparedSelectQuoteSql.executeQuery()) {
        while (resultSet.next()) {
            quoteBean = new QuoteBean(
                resultSet.getTimestamp("MySQLDateColumn")
            );
        }
    }
    

    将其从 ZonedDateTime 插入/更新到 MySQL 到 MySQL 将自动转换为 UTC 的时间戳,这样我们就可以让 MySQL 在任何我们希望它存在的地方运行并及时读回相同的 Instant:

    if ( insertValue instanceof ZonedDateTime ) {
        if ( insertValue != null ) {
            Timestamp convertedDate = Timestamp.from( ((ZonedDateTime) insertValue).toInstant() );
            preparedStatement.setTimestamp( paramNumber, convertedDate );
        } else {
            preparedStatement.setNull ( paramNumber, Types.TIMESTAMP );
        }
    }
    

    我认为这行得通,但我欢迎批评。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-10-09
      • 2016-10-30
      • 1970-01-01
      • 2019-03-12
      • 2012-01-04
      • 1970-01-01
      • 2017-12-09
      • 2018-02-18
      相关资源
      最近更新 更多