java – ScheduledExecutorService每晚12点UTC时间执行

我想每天正好上午12点启动ScheduledExecutorService,Schedule必须在今天22/02/2017 00:00:00(UTC时间)开始,任何人都可以告诉我我的代码是否正确?

DateTime today = new DateTime().withTimeAtStartOfDay(); 
        DateTime startOfTommorrow = today.plusDays(1).withTimeAtStartOfDay();

        Long midnight = startOfTommorrow.getMillis();
        long midnights = (midnight / 1000)  / 60;
        final DateFormat nextDateTymFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        System.out.println("***********************************");
        System.out.println("Schedule Updater "+nextDateTymFormat.format(new Date()));
        System.out.println("today "+today);
        System.out.println("startOfTommorrow "+startOfTommorrow);
        System.out.println("midnight Long "+midnight);
        System.out.println("***********************************");
        vitalScheduleThread.scheduleAtFixedRate(new Runnable() {

            @Override
            public void run() {

                System.out.println("Hello vitalSchService !!"+nextDateTymFormat.format(new Date()));

                Thread.currentThread().setName("vitalSchService");

                //sendMail();
                vitalSchedule.process(springContext);
            }
        }, midnight , 86400000 , TimeUnit.MILLISECONDS

);

最佳答案 TL;博士

OffsetDateTime now = OffsetDateTime.now( ZoneOffset.UTC ) ;  // Capture the current moment.

….scheduleAtFixedRate(
    new Runnable() { … } ,           // Define task to be executed as a `Runnable`.
    Duration.between(                // Determine amount of time for initial delay until first execution of our Runnable.
        now ,                        // Current moment.
        now.toLocalDate().plusDays( 1 ).atStartOfDay( ZoneOffset.UTC )  // Determine the first moment of tomorrow in our target time zone (UTC). Used as the exclusive end of our Half-Open span of time.
    ) ,
    TimeUnit.DAYS.toMillis( 1 ) ,    // Amount of time between subsequent executions of our Runnable. Use self-documenting code rather than a “magic number” such as `86400000`. 
    TimeUnit.MILLISECONDS            // Specify the granularity of time used in previous pair of arguments.
)                                    // Returns a `ScheduledFuture` which you may want to cache.

细节

明确指定区域

您假设JVM的当前time zone是您所需的UTC.在调用日期时间方法时,您省略了可选的时区参数.省略意味着JVM的当前默认时区在运行时隐式且无声地应用.该默认值可能随时发生变化.该JVM中任何应用程序的任何线程中的任何代码都可以在运行时更改默认值(!).

不要隐式依赖JVM的当前默认时区,而是始终明确指定所需/预期的区域.在您的情况下,我们需要ZoneOffset.UTC.而不是假设/希望部署JVM的当前默认值设置为UTC,并保持UTC,请明确指定使用constant.

您似乎正在使用优秀的Joda-Time库.该项目现在处于维护模式,团队建议迁移到java.time类.与Joda-Time相同的基本概念启发了java.time.

首先得到UTC所见的当前时刻.

OffsetDateTime now = OffsetDateTime.now( ZoneOffset.UTC );

从中提取仅日期值.添加一个以获得明天的日期.

LocalDate today = now.toLocalDate();
LocalDate tomorrow = today.plusDays( 1 );

“午夜”一词可能含糊不清,令人困惑.相反,要关注“一天中的第一时刻”的概念.

我们的目标是延迟您第一次执行执行服务的时间.所以我们需要从现在到明天的第一个时刻的时间跨度.

在确定时间跨度时,使用半开方法,当开头是包含时,结尾是独占的.因此,我们的时间跨度从现在(当前时刻)开始,直到明天的第一个时刻,但不包括在内.

让java.time确定明天的第一个时刻.在UTC中,该日始终从00:00开始.但在某些日期的某些时区不是这样,那天可能会在01:00开始.所以,作为一种习惯,总是让java.time确定一天的第一时刻.
    OffsetDateTime tomorrowStart = OffsetDateTime.of(明天,LocalTime.MIN,ZoneOffset.UTC);

计算从现在到明天的第一个时刻之间的经过时间. Duration类表示未附加到时间轴的时间跨度.

Duration d = Duration.between( now ,  tomorrowStart );
long millisUntilTomorrowStart = d.toMillis();

而不是像86400000这样神秘的数字文字,而是使用自我记录调用.

TimeUnit.DAYS.toMillis( 1 )

所以你的ScheduledExecutorService看起来像这样:

….scheduleAtFixedRate(
    new Runnable() { … } ,          // Task to be executed repeatedly, defined as a Runnable.
    millisUntilTomorrowStart ,      // Initial delay, before first execution. Use this to get close to first moment of tomorrow in UTC per our code above.
    TimeUnit.DAYS.toMillis( 1 ) ,   // Amount of time in each interval, between subsequent executions of our Runnable.
    TimeUnit.MILLISECONDS           // Unit of time intended by the numbers in previous two arguments.
)

对于整天递增,您不需要使用如毫秒的精细粒度.由于各种原因,执行程序无法以完美的时序运行.所以我可能会在几分钟内计算出来.但并不重要.

非常重要:您需要将Runnable的run方法的代码包含在陷阱中以用于任何异常.如果任何类型的异常要到达执行者,执行者将默默地停止.没有进一步的任务安排,也没有警告.搜索Stack Overflow以获取更多信息,包括我的回答.

你没有解释你调用scheduleAtFixedRate的对象是什么.所以这是代码的主要部分,在你发布更多信息之前我们无法帮助你.我担心你把它命名为“Thread”.该对象必须是ScheduledExecutorService的实现,而不是线程.

提示:避免在午夜时分运行.许多事情往往发生在午夜的计算机上.例如,闰秒调整,许多Unix清理实用程序和日常活动,例如可能由天真管理员安排的备份.等待五到十五分钟的事情可以避免麻烦和神秘的问题.

关于java.time

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

Joda-Time项目现在是maintenance mode,建议迁移到java.time课程.

要了解更多信息,请参阅Oracle Tutorial.并搜索Stack Overflow以获取许多示例和解释.规格是JSR 310.

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

从哪里获取java.time类?

> Java SE 8,Java SE 9及更高版本

>内置.
>部分标准Java API,带有捆绑实现.
> Java 9增加了一些小功能和修复.

> Java SE 6Java SE 7

>大部分java.time功能都被反向移植到Java 6& 7月在ThreeTen-Backport.

> Android

>更新版本的Android捆绑java.time类的实现.
>对于早期的Android(< 26),ThreeTenABP项目采用ThreeTen-Backport(如上所述).见How to use ThreeTenABP….

ThreeTen-Extra项目使用其他类扩展了java.time.该项目是未来可能添加到java.time的试验场.您可以在这里找到一些有用的类,例如Interval,YearWeek,YearQuarter和more.

点赞