【问题标题】:Timer countdown is negative计时器倒计时为负数
【发布时间】:2017-04-04 02:43:18
【问题描述】:

我有一个用于创建 24 小时倒计时的代码。

此代码检查“日期”文件是否存在,如果不存在,则创建一个,其中包含每天 24 小时的日期和时间。然后它获取当前时间并比较两者,以创建从当前日期到文档中日期的倒计时。

这使得即使代码“关闭”也可以保存计时器并检查它已经走了多远。唯一的问题是有时计时器会变成负数。就像我从一开始就运行代码一样,没有在星期一创建“日期”文件,就在午夜之前,让我们说星期一晚上十一点半。然后,如果我在当前日期已过午夜时停止代码并再次运行它,那么它实际上是星期二,但在它达到实际目标计时器之前仍然缺少 23 小时。如果是这种情况,倒计时中剩余的时间为负数。就像它会显示“-1 天 23 小时 60 分钟和 60 秒剩余”一样。但是,如果作为示例,它在周二午夜过后从头开始运行,然后在同一天 30 分钟后重新启动,则没有问题。

我希望你能理解问题是什么,通过文字表达有点困难。但我附上了我的整个代码,这正是我正在使用的代码,其中有这个问题。该代码对发生的每个动作都有 cmets,因此应该很容易理解。

static File dFileD = new File("date.txt");
    static String date = "";

  public static void main(String[] args) throws ParseException {

      Timer tickTock = new Timer();
      TimerTask tickTockTask = new TimerTask(){

          public void run(){
              try {
                timFunRun(); //Timer calls method to start the countdown
            } catch (ParseException e) {
                e.printStackTrace();
            }
          }
      };

      tickTock.schedule(tickTockTask, 1000, 1000);

  }

  static void timFunRun() throws ParseException {

      if (!dFileD.exists()){ //if it doesn't exist, first part

          //Get current date and time
          Calendar startDat = Calendar.getInstance();
          System.out.println("Current date: " + startDat.getTime());

          //Get that current date and time and then add 1 day
          Calendar todAdd = Calendar.getInstance();
          todAdd.add(Calendar.DATE, 1);
          System.out.println("Date in 1 day: " + todAdd.getTime());

          //Create a format for sending date to text file
          SimpleDateFormat formDat = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z");
          String formatted = formDat.format(todAdd.getTime());
          System.out.println("Formatted: " + formatted);

          try{
              PrintWriter dW = new PrintWriter("date.txt");
              dW.println(formatted);
              dW.close();
          } catch (IOException e) {
          }

          System.out.println(formDat.parse(formatted));

      } else { //if it does exist, second part

          //Get current date and time
          Calendar currentDeT = Calendar.getInstance();
          System.out.println("Current date: " + currentDeT.getTime());
          SimpleDateFormat formDat2 = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z");

          Date dateFroText = null; //Get the "goal" date
          try {
              Scanner dateRead = new Scanner(dFileD);
              while (dateRead.hasNextLine()) {
                  date = dateRead.nextLine();
                  dateFroText = formDat2.parse(date);
                  System.out.println("Date from text new format: " + dateFroText);
              }
              dateRead.close();
            } catch (Exception e) {
                System.out.println("Error!");
            }

          if (dateFroText != null){ //method to compare the current date and the goal date
              Calendar dateFromTxtCal = Calendar.getInstance();
              dateFromTxtCal.setTime(dateFroText);
          int yearDiff = dateFromTxtCal.get(Calendar.YEAR) - currentDeT.get(Calendar.YEAR);
          int dayDiff = ((yearDiff*365) + dateFromTxtCal.get(Calendar.DAY_OF_YEAR)) - currentDeT.get(Calendar.DAY_OF_YEAR);
          dayDiff--;
          int hourDiffer = dateFromTxtCal.get(Calendar.HOUR_OF_DAY)+23 - currentDeT.get(Calendar.HOUR_OF_DAY);
          int minuDiff = dateFromTxtCal.get(Calendar.MINUTE)+60 - currentDeT.get(Calendar.MINUTE);
          int secoDiff = dateFromTxtCal.get(Calendar.SECOND)+60 - currentDeT.get(Calendar.SECOND);
          System.out.println(dayDiff + " days " + hourDiffer + " hours " + minuDiff +" minutes " + secoDiff + "seconds remaining");
          }

      }

  }

【问题讨论】:

  • 首先使用 Java 8 或 JodaTime 中提供的较新的日期/时间 API,如果您使用的是早期版本的 Java,它们能够计算两个时间点之间的持续时间
  • 好主意,@MadProgrammer。我不想破译问题中代码末尾附近发生的时间算术。看起来很复杂。只是,我听说如果你不能使用 Java 8 并且你愿意依赖第三方库,你应该更喜欢 Basil Bourque’s answer 中提到的 ThreeTen Backport 而不是 JodaTime。
  • @OleV.V.这不是一个坏建议,我更喜欢 JodaTime,因为它是一个更丰富的 API,但这就是我

标签: java date timer calendar countdown


【解决方案1】:

避免使用旧的日期时间类

你工作太辛苦了。而且您正在使用麻烦的旧日期时间类,现在这些类已被 java.time 类淘汰和取代。

在 UTC 工作

另外,如果您只关心接下来的 24 小时,则不需要时区。只需使用 UTC。

Instant

Instant 类表示UTC 中时间轴上的时刻,分辨率为nanoseconds(最多九 (9) 位小数)。

Instant instantNow = Instant.now();

Duration

与时间线无关的时间跨度由DurationPeriod 类表示。

Duration duration = Duration.ofHours( 24 );

您可以执行日期时间数学运算,将Duration 添加到Instant。 java.time 类使用不可变对象,因此此类操作的结果是一个新对象,其值基于原始对象。

Instant instantLater = instantNow.plus( duration );

ISO 8601

要将日期时间值(例如文本)序列化,请使用标准的ISO 8601 格式。 java.time 类在生成和解析字符串时默认使用 ISO 8601。

String output = instantNow.toString(); 

2017-04-03T03:18:48.183Z

计算剩余时间

要获得剩余时间,让 java.time 计算一下。

Duration remaining = Duration.between( Instant.now() , instantLater );

要以标准 ISO 8601 格式报告该问题,请致电 toStringformat for durationsPnYnMnDTnHnMnSP 标志着开始(Period),T 将任何年-月-日期与小时-分钟-秒分开。

String outputRemaining = remaining.toString();

PT23H34M22S

要生成更长的字符串,在 Java 9 中为每个单位(小时、分钟、秒)调用 to…Part 方法。奇怪的是,Java 8 的原始 java.time.Duration 类中省略了这些方法。您可以查看 Java 9 的源代码来编写类似的代码。

或者更简单地说,操作标准字符串。删除PT。将H 替换为hours 等等。先做S,以避免其他两个词中的复数s。诚然,这是一种 hack,但它之所以有效,是因为英文拼写为 hours-minutes-seconds。

String output = remaining.toString()
                         .replace( "PT" , "" )
                         .replace( "S" , " seconds " )
                         .replace( "H" , " hours " )
                         .replace( "M" , " minutes " ) ;

23 小时 34 分 22 秒

ZonedDateTime

如果您想在用户期望/预期的时区显示目标日期时间,请应用ZoneId 以获取ZonedDateTime 对象。

ZoneId z = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = instant.atZone( z );

要生成标准格式的字符串,请调用toString。实际上,ZonedDateTime 类通过在方括号中附加时区名称来扩展标准。

要生成其他格式的字符串,请在 Stack Overflow 上搜索 DateTimeFormatter 类的许多示例。

转换为java.util.Date

Timer 类尚未更新为与 java.time 类型一起使用。因此,通过添加到旧类的新方法转换回 Date 对象,在本例中为 from

java.util.Date date = java.util.Date.from( instantLater );

或者使用Duration 对象的毫秒数。

long milliseconds = duration.toMillis() ;

ScheduledExecutorService

仅供参考,TimerTimeTask 类已被 Executors 框架取代。专门针对您的目的,ScheduledExecutorService。在 Stack Overflow 上搜索许多示例和讨论。


关于java.time

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

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

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

从哪里获得 java.time 类?

ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore

【讨论】:

  • 非常感谢您提供的大量信息,我已接受您的回答,认为它很可能会解决所有问题。完成后我会回复你。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-08-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-04
  • 1970-01-01
相关资源
最近更新 更多