Darlyn
Darlyn

Reputation: 4938

Creating specific date object from milliseconds in different time

I'm working with an API that has a different time than the program's machine. I can retrieve the time of the server, which will returns milliseconds.

e.g

long serverTime = api.getTime()

Since I know the server time, can I somehow create a date object from this millisecond that would represent certain dates, but with server time?

For example, API is only available from 8:00 to 16:00 UTC (machine time is +1 hour). My use case requires me to send data using this API as soon as possible (the sooner I send data, the sooner they get processed, and the sooner other tasks can start running and so on, unfortunately, every millisecond spared is good). So far I am doing something like this

// create date time when i want to do something with API in my time
 LocalDateTime time = LocalDateTime.of(2021, 3, 26, 9, 0, 0);
 long targetMillis = time.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();

 long serverTime = api.getTime()
 // calculate diff
 long serverTimeDiff = client.getServerTime() - System.currentTimeMillis();

// sleep thread till the time is right. Sleep 1 minute less to start busy waiting to get some extra 
// spared millis
 Thread.sleep((targetMillis - serverTime) - minutesToMilliseconds(1) );
 while( System.currentTimeMillis() + serverTimeDiff < ( targetMillis - 50 ) ){
        //busy waiting
 }
  
 api.sendData(..);

However, the time diff may not be that accurate (change in few millis), is there some way how to create something like:

long serverTime = api.getTime()
Instant date = Instant.ofEpochMilli(serverTime).setHour(..).setMinute(..).setSecond(..);

So I can calculate the diff more accurately with something like

Thread.sleep((targetMillis - date.toMillis().toEpochMilli) - minutesToMilliseconds(1))

and thus possibly save some milliseconds?

Upvotes: 0

Views: 113

Answers (1)

Basil Bourque
Basil Bourque

Reputation: 338506

Your Question is quite unclear. But I am guessing that you want to start a task at 08:00 UTC.

If so, use a scheduled executor service to run your Runnable or Callable task on a background thread at the appropriate time.

Determine your next target moment, the next 8 AM in UTC. To do this, you will need the current date and current time as seen in UTC. If right now is not before 8 AM, you must add a day to the date. Then combine that date with the specified time of 8 AM to get a moment as seen in UTC (an offset of zero hours-minutes-seconds).

Calculate the amount of elapsed time until that target moment. Use that amount of time as the delay argument to the schedule method of your ScheduledExecutorService.

package work.basil;

import java.time.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * By Basil Bourque. http://www.Basil.work/ 
 * https://stackoverflow.com/q/66809336/642706
 */
public class App
{
    public static void main ( String[] args )
    {
        App app = new App();
        app.demo();
    }

    private void demo ( )
    {
        System.out.println( "INFO - demo method starting. " + Instant.now() );

        // Using lambda syntax to define a `Runnable` object as the task to be done at specified moment.
        // You could just as well write a conventional class that implements `Runnable` (or `Callable`). 
        Runnable task = ( ) -> {
            System.out.println( "Sending data at: " + Instant.now() );
        };

        ScheduledExecutorService ses = Executors.newSingleThreadScheduledExecutor();

        // Calculate time to wait until the next 8 AM.
        LocalTime targetTime = LocalTime.of( 8 , 0 );
        OffsetDateTime nowUtc = OffsetDateTime.now( ZoneOffset.UTC );
        LocalDate targetDate = nowUtc.toLocalDate();
        if ( ! nowUtc.toLocalTime().isBefore( targetTime ) )
        {
            targetDate = targetDate.plusDays( 1 );
        }
        OffsetDateTime targetUtc = OffsetDateTime.of( targetDate , targetTime , ZoneOffset.UTC );
        Duration duration = Duration.between( nowUtc , targetUtc );
        System.out.println( "INFO - Your task is waiting  = " + duration + " to run at: " + targetUtc + " which is: " + targetUtc.atZoneSameInstant( ZoneId.systemDefault() ) + "." );
        long delay = duration.toNanos();

        ses.schedule( task , delay , TimeUnit.NANOSECONDS );

        // … Eventually shutdown your executor service so its backing pool of threads do not continue indefinitely like a zombie 🧟‍.
        ses.shutdown();
        ses.awaitTermination();
        System.out.println( "INFO - demo method ending. " + Instant.now() );
    }
}

When run.

INFO - demo method starting. 2021-03-26T03:15:03.203895Z
INFO - Your task is waiting  = PT4H44M56.786492S to run at: 2021-03-26T08:00Z which is: 2021-03-26T01:00-07:00[America/Los_Angeles].

Upvotes: 1

Related Questions