【问题标题】:new Date returns Tomcat startup datenew Date 返回 Tomcat 启动日期
【发布时间】:2021-05-14 22:53:30
【问题描述】:

我不知道如何解决这个问题。 我有一个在 Ubuntu 服务器上运行的 Java Spring Boot 应用程序。 我有这个方法应该返回一个没有时间的日期:

    public static Calendar toCalendar(Date date){ 
    
      Calendar cal = Calendar.getInstance();
      cal.setTime(date);
      cal.set(Calendar.HOUR, 0);
      cal.set(Calendar.MINUTE, 0);
      cal.set(Calendar.SECOND, 0);
      cal.set(Calendar.HOUR_OF_DAY, 0);       
      cal.set(Calendar.MILLISECOND, 0);
      return cal;
}

好吧,如果我说:

Date        todayWithNoTime = toCalendar(new Date()).getTime();

我总是知道 Tomcat 的启动日期!不是当前日期...

有人知道我做错了什么吗?

谢谢。

【问题讨论】:

  • 无法重现 - 您确定这就是您正在运行的确切代码吗?
  • 绝对。我复制并粘贴了。
  • 我建议你不要使用Calendar 和Date。这些课程设计不良且早已过时。而是使用ZonedDateTime 和java.time, the modern Java date and time API 中的其他类。
  • 你如何确定日期是错误的?通过打印或记录返回的Calendar 对象?
  • todayWithNoTime 是字段还是局部变量?我的猜测是前者,它是在应用程序启动时初始化的。

标签: java linux date tomcat server


【解决方案1】:

tl;博士

java.time.LocalDate.now( ZoneId.of( "Africa/Tunis" ) ) 

避免使用旧的日期时间类

Calendar.getInstance() 和 new Date() 都捕捉当前时刻。所以你要么是:

  • 弄错了,可能缓存了Date/Calendar 调用的结果。见comment by Karwasz。
  • 或者,如果日期相差一天,则会看到时区效应。

更重要的是,您正在使用 可怕 日期时间类,这些类在几年前已被现代 java.time 类所取代。永远不要使用Date,永远不要使用Calendar。 Sun、Oracle 和 JCP 在采用 JSR 310 时放弃了这些类。您也应该这样做。

LocalDate

如果您想表示仅日期值、没有时间和时区,请使用LocalDate。

LocalDate localDate = LocalDate.of( 2021 , Month.FEBRUARY , 23 ) ;

要捕获当前日期,您必须指定时区。对于任何给定的时刻,日期在全球范围内因时区而异。在日本东京,日期可能是“明天”,而在美国俄亥俄州托莱多,日期可能是“昨天”。如果省略时区,则隐式应用 JVM 当前的默认时区。我建议始终保持明确。

ZoneId z = ZoneId.of( "America/Edmonton" ) ;
LocalDate today = LocalDate.now( z ) ;

通过在 Tomcat 上运行的 Servlet 代码中的该代码,您将获得当前日期。

示例小服务程序

这是一个完整的工作 Servlet。

package work.basil.example.DemoWebApp;

import java.io.*;
import java.time.LocalDate;
import java.time.ZoneId;
import javax.servlet.http.*;
import javax.servlet.annotation.*;

@WebServlet ( name = "todayServlet", value = "/today" )
public class TodayServlet extends HttpServlet
{
    public Instant whenServletInitialized ;
   
    public void init ( )
    {
        this.whenServletInitialized = Instant.now() ;
    }

    public void doGet ( HttpServletRequest request , HttpServletResponse response ) throws IOException
    {
        response.setContentType( "text/html" );

        PrintWriter out = response.getWriter();
        out.println( "<html><body>" );
        out.println( "<h1>" + "Today" + "</h1>" );
        ZoneId zoneId = ZoneId.of( "Asia/Kolkata" );  // or ZoneId.systemDefault()
        LocalDate today = LocalDate.now( zoneId );
        out.println( "<p>Current date in time zone " + zoneId.toString() + " is " + today.toString() +".</p>");
        out.println( "</body></html>" );
    }

    public void destroy ( )
    {
    }
}


关于java.time

java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.Date、Calendar 和 SimpleDateFormat。

要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310。

Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。

您可以直接与您的数据库交换 java.time 对象。使用符合JDBC 4.2 或更高版本的JDBC driver。不需要字符串,不需要java.sql.* 类。 Hibernate 5 & JPA 2.2 支持 java.time。

从哪里获取 java.time 类?

【讨论】:

    【解决方案2】:

    好吧,这是我做错了:

    这一行位于控制器类的顶部,在方法之外:

    Date        currentDate = Mydates.toCalendar(new Date()).getTime();
    

    以及稍后使用该变量的方法:

    private void updateObject(Object object) {
       ...
       object.setDate(currentDate);
       objectService.save(object);
    }
    

    那是错误的。我猜它正在缓存结果......你不觉得吗?

    现在如果我不使用变量并直接从方法内部调用函数,它就可以工作了:

    private void updateObject(Object object) {
       ...
       object.setDate(Mydates.toCalendar(new Date()).getTime());
       objectService.save(object);
    }
    

    谢谢大家的帮助:)

    最好的问候。

    【讨论】:

    • 是的,这证实了 Piotr P. Karwasz 昨天在评论中已经发现的内容。您所描述的导致 Date 被初始化为创建控制器对象的日期。我们显然无法确切地知道这种情况何时发生,但例如在启动 Tomcat 时很容易发生这种情况(除非您稍后才部署您的应用程序)。 (仍然认为您应该更喜欢 LocalDate 作为今天的日期。)
    • 没错,Piotr P. Karwasz 完全正确。再次感谢
    猜你喜欢
    • 2016-10-28
    • 1970-01-01
    • 1970-01-01
    • 2019-07-12
    • 1970-01-01
    • 1970-01-01
    • 2021-03-28
    • 2017-03-13
    • 1970-01-01
    相关资源
    最近更新 更多