从特定时间更新每日日期计数器

时间:2019-01-27 14:48:26

标签: java date java-date java-calendar

我有两个约会日期,分别是“ 2018-01-01”开始日期和“ 2018-01-31”结束日期

我想以一种这样的方式做出自己的逻辑,即每天的开始日期都会增加,直到达到2018-02-28。以下是我尝试过的代码段。每天如何更改此作为“开始日期”的修补程序。

public void positionLogo() {
    double spacerMargin = widthProperty().doubleValue() - getChildrenUnmodifiable().stream().mapToDouble( node -> node.getLayoutBounds().getWidth() ).sum();
    clearConstraints( m_logo );
    setMargin(m_logo, new Insets( 2, 0, 0, spacerMargin));
}

PS:此执行是实时的,在每天运行的调度程序中,并对照给定日期的结局日期进行检查。剩下多少天。

谢谢

3 个答案:

答案 0 :(得分:2)

Runnable

将您需要完成的工作定义为Runnable

然后使用Scheduled ExecutorService每分钟左右运行一次,将当前日期与目标日期进行比较。如果倒计时已增加,请在GUI中更新显示。如果没有,则什么也不做,让Runnable再过一分钟再运行一次。

请参见Oracle Tutorial on Executors

ScheduledExecutorService ses = Executors.newSingleThreadScheduledExecutor();  // Use a single thread, as we need this to run sequentially, not in parallel.
ses.scheduleWithFixedDelay( r , 0L , 1L , TimeUnit.MINUTES ); // Pass the `Runnable`, an initial delay, a count to wait between runs, and the unit of time for those two other number arguments.

每次安排一个新的Runnable而不是自动重复会更有效,因此可以将延迟时间设置为计算出的直到下一个午夜的时间。这样执行者将整天睡觉,而不是每分钟跑步。但是,如果用户的时钟更新为明显不同的当前时间,或者如果用户的当前默认时区发生了更改(如果您使用默认值而不是明确设置一个默认时区),则该方法可能会陷入困境。鉴于此Runnable所需要做的事情很少(只需检查当前日期并计算剩余天数),就没有实际的理由不仅仅让它每两分钟运行一次(但是您对用户进行新更新的最小容忍时间是多长时间-应用管理的业务策略)。

LocalDate

LocalDate类表示没有日期,没有time zoneoffset-from-UTC的仅日期值。

时区对于确定日期至关重要。在任何给定时刻,日期都会在全球范围内变化。例如,Paris France午夜之后的几分钟是新的一天,而Montréal Québec仍然是“昨天”。

如果未指定时区,则JVM隐式应用其当前的默认时区。该默认值可能在运行时(!)期间change at any moment,因此您的结果可能会有所不同。最好将您的期望/期望时区明确指定为参数。

Continent/Region的格式指定proper time zone name,例如America/MontrealAfrica/CasablancaPacific/Auckland。切勿使用2-4个字母的缩写,例如ESTIST,因为它们不是真正的时区,不是标准化的,甚至不是唯一的(!)。

ZoneId z = ZoneId.of( "Asia/Kolkata" );  // Or ZoneId.systemDefault() to rely on the JVM’s current default time zone.
LocalDate today = LocalDate.now( z );

示例

这里是完整的示例。有关更多信息,请搜索堆栈溢出。 ScheduledExecutorService的使用已经涉及很多次了。

package work.basil.example;

import java.time.Instant;
import java.time.LocalDate;
import java.time.Month;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.Objects;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class DailyCountdown implements Runnable {
    private LocalDate dueDate;
    private Long daysRemaining;

    public DailyCountdown ( LocalDate dueDate ) {
        this.dueDate = dueDate;
    }

    @Override
    public void run () {
        try {
            System.out.println( "DEBUG - Running the DailyCountdown::run method at " + Instant.now() );
            ZoneId z = ZoneId.of( "America/Montreal" );  // Or ZoneId.systemDefault() to rely on the JVM’s current default time zone.
            LocalDate today = LocalDate.now( z );
            Long count = ChronoUnit.DAYS.between( today , this.dueDate );
            if ( Objects.isNull( this.daysRemaining ) ) {
                this.daysRemaining = ( count - 1 );
            }
            if ( this.daysRemaining.equals( count ) ) {
                // Do nothing.
            } else {
                // … Schedule on another thread for the GUI to update with the new number.
                this.daysRemaining = count;
            }
        } catch ( Exception e ) {
            // Log this unexpected exception, and notify sysadmin.
            // Any uncaught exception reaching the scheduled executor service would have caused it to silently halt any further scheduling.
        }
    }

    public static void main ( String[] args ) {
        // Put this code where ever appropriate, when setting up your GUI after the app launches.
        Runnable r = new DailyCountdown( LocalDate.of( 2018 , Month.FEBRUARY , 15 ) );
        ScheduledExecutorService ses = Executors.newSingleThreadScheduledExecutor();
        ses.scheduleWithFixedDelay( r , 0L , 1L , TimeUnit.MINUTES );

        // Be sure to gracefully shutdown the ScheduledExecutorService when your program is stopping. Otherwise, the executor may continue running indefinitely on the background thread.
        try {
            Thread.sleep( TimeUnit.MINUTES.toMillis( 7 ) ); // Sleep 7 minutes to let the background thread do its thing.
        } catch ( InterruptedException e ) {
            System.out.println( "The `main` thread was woken early from sleep." );
        }
        ses.shutdown();
        System.out.println( "App is exiting at " + Instant.now() ) ;
    }
}

答案 1 :(得分:1)

Java 8的时间API,您可以轻松实现。

for (LocalDate date = startDate; date.isBefore(endDate); date = date.plusDays(1))
{
    ...
}

答案 2 :(得分:1)

您可以为此使用java.util.Timer类。

在下面的程序中,我采用了不同的方法。我没有使用开始日期。我只是每天获取当前日期,然后将其与目标日期进行比较(在此示例中为“ 2019-02-10”)。看看这是否符合您的要求。

(使用FIVE_SECONDS_IN_MILLISECONDS来测试程序。)

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Timer;
import java.util.TimerTask;

public class Scheduler {
  private static final long ONE_DAY_IN_MILLISECONDS = 1000 * 60 * 60 * 24;
  private static final long FIVE_SECONDS_IN_MILLISECONDS = 1000 * 5;

  public static void main(String[] args) {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

    TimerTask timerTask = new TimerTask() {
      @Override
      public void run() {
        Calendar c = Calendar.getInstance();
        String dateString = sdf.format(c.getTime());
        System.out.println(dateString);
        if (dateString.equals("2019-02-10")) {
          System.out.println("Date reached!");
        }
      }
    };

    Timer timer = new Timer();
    timer.schedule(timerTask, 0, ONE_DAY_IN_MILLISECONDS/*FIVE_SECONDS_IN_MILLISECONDS*/);
  }
}