ScheduledExecutorService execute every night at 12 AM UTC Time
Asked Answered
T

1

6

I want to start ScheduledExecutorService exactly 12 AM daily ,Schedule has to Start at today at 22/02/2017 00:00:00 (UTC TIME),Can any one tell me Whether my code is Correct or not?

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
);
Trixi answered 21/4, 2017 at 7:42 Comment(0)
G
6

tl;dr

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.

Details

Specify zone explicitly

You are assuming the JVM’s current time zone is your desired UTC. You omit the optional time zone argument when calling the date-time methods. That omission means the JVM’s current default time zone is applied implicitly and silently at runtime. That default may change at any moment. Any code in any thread of any app within that JVM can change the default during runtime(!).

Instead of relying implicitly on the JVM’s current default time zone, always specify your desired/expected zone explicitly. In your case, we want ZoneOffset.UTC. Instead of assuming/hoping the deployment JVM’s current default is set to UTC, and stays at UTC, specify explicitly using the constant.

You seem to be using the excellent Joda-Time library. That project is now in maintenance mode, with the team advising migration to the java.time classes. Same basic concepts, as Joda-Time inspired java.time.

First get the current moment as seen in UTC.

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

Extract a date-only value from that. Add one to get tomorrow’s date.

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

The term “midnight” can be ambiguous and confusing. Instead, focus on the concept of “first moment of the day”.

We are aiming for the amount of time to delay until your first execution of your executor service. So we need the span of time between now and the first moment of tomorrow.

And when determining a span of time, use the Half-Open method when the beginning is inclusive while the ending is exclusive. So our span of time starts with now (the current moment) and runs up to, but does not include) the first moment of tomorrow.

Let java.time determine the first moment of the day tomorrow. In UTC the day always starts at 00:00. But not so in some time zones on some dates, where the day might start at a time like 01:00. So, as a habit, always let java.time determine first moment of the day. OffsetDateTime tomorrowStart = OffsetDateTime.of( tomorrow , LocalTime.MIN , ZoneOffset.UTC );

Calculate the elapsed time between now and that first moment of tomorrow. The Duration class represents such spans of time unattached to the timeline.

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

Instead of a mysterious number literal such as 86400000, use a self-documenting call.

TimeUnit.DAYS.toMillis( 1 )

So your ScheduledExecutorService call would look like this:

….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.
)

For incrementing in whole days, you needn't use such a fine granularity as milliseconds. The executors do not run with perfect timing for various reasons. So I probably would have calculated in minutes. But not important.

Very important: You need to enclose the code of your Runnable’s run method in a trap for any exception. If an Exception of any type were to reach the executor, the executor silently halts. No further scheduling of tasks, and no warning. Search Stack Overflow for more info including an Answer by me.

You do not explain what is the object on which you call scheduleAtFixedRate. So that is a major part of the code that we cannot help with until you post more info. I'm concerned you have it named “Thread”. That object must be an implementation of ScheduledExecutorService, not a thread.

Tip: Avoid running things at exactly midnight. Many things tend to happen on computers at midnight. For example, leap second adjustments, many Unix cleanup utilities, and routine activities such as backups that may have been scheduled by naïve administrators. Waiting something like five or fifteen minutes may avoid hassles and mysterious problems.


About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.

Where to obtain the java.time classes?

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

Glorygloryofthesnow answered 21/4, 2017 at 8:23 Comment(4)
Hi, Yes my server time always will be UTC,I will check with ur changes later,But Please check my code and let me know whether My Code is correct or not according to my requirement mentioned in quesionTrixi
I'm trying to warn you that you should not assume your current default time zone will always be UTC. This is a fact outside your control as a programmer. There is no need to rely on the current default zone, so why risk it? Simply pass ZoneOffset.UTC as shown in my code and you have one less issue to worry about. You said the requirement is to run at midnight UTC, and not specifying the time zone is the biggest risk I see for that going wrong.Glorygloryofthesnow
Okay Let me check thisTrixi
@basil , It seems to me that after the first execution at 12:00, if you reschedule every 24 hours and let the service run, the schedule will be off by one hour (wall clock time) when the daylight saving time occurs. e.g. the task will run at 11:00 or 13:00 every day until the DST changes again.Grumous

© 2022 - 2024 — McMap. All rights reserved.