javatimertasklocaldate

How to update LocalDateTime variable while using TimerTask


I'm learning Java on my own and I'm stuck with the following problem. I scheduled my code to run for every 120 seconds and my issue here is that can't find the way to update LocalDateTime variable now outside of the run() method. I have a private static class called Date and I'm using this so I'm able to use this variables in other parts of the code that need to use the date at the same time.

    public class Test{
                        
                      private static class Date {

                        //This wont update every 120 seconds
                          private static final LocalDateTime now = LocalDateTime.now();
                          private static final DateTimeFormatter archive = DateTimeFormatter.ofPattern("yyyy MM dd HH:mm");
                        
                            }
                        
                 public Test(int seconds)  {
                                timer = new Timer();
                                timer.schedule(new APICallerTask(), 0,seconds * 1000L); }
                        
                static class APICallerTask { 
                                  public void run() {

                     //This will update every 120 seconds
                       LocalDateTime now = LocalDateTime.now();
                       DateTimeFormatter archive = DateTimeFormatter.ofPattern("yyyy MM dd HH:mm");}
             }
        
         public static void main(String[] args)  {
        
               
                new Test(120);
                    }
    
      }

I'm sorry if this is a dumb question but I'm not being able to figure it out.

Any help is appreciated.

Thanks in advance.


Solution

  • As seen in correct Answer by Kanshyn, LocalDateTime is immutable. You need to store a fresh new instance when you want a new value.

    Also, LocalDateTime is the wrong class here. The LocalDateTime class lacks the context of an offset-from-UTC or a time zone.

    When tracking a moment, a specific point in the timeline, use Instant, OffsetDateTime, or ZonedDateTime.

    In your case, probably best to use Instant in your data model. When presenting to user, adjust to their expected time zone.

    Instant instant = Instant.now() ;
    ZoneId z = ZoneId.of( "Africa/Tunis" ) ;  // Or ZoneId.systemDefault(). 
    ZonedDateTime zdt = instant.atZone( z ) ;
    

    Generate text from that ZonedDateTime by using DateTimeFormatter. Automatically localize by calling ZonedDateTime.ofLocalizedDateTime. Search Stack Overflow to learn more as this has been covered many times already.

    I'm learning Java on my own and I'm stuck with the following problem.

    Study The Java™ Tutorials by Oracle Corp., free of cost.

    See the section on date-time handling with java.time here.

    I have a private static class called Date

    Java comes with two classes named Date. I suggest using another name to avoid confusion.

    static class APICallerTask {

    No need to make your task class static.

    private static class Date {

    Try to resist the urge to use that static label. As a beginner, you'll almost always be using static in a wrong or less-than-optimal fashion. Code with static is not object-oriented.

    timer = new Timer();

    The Timer and TimerTask classes are legacy, as noted in their Javadoc. Best to use the Executors framework in Java 5+. See The Java™ Tutorials.

    A ScheduledExecutorService runs tasks repeatedly.

    ScheduledExecutorService see = Executors.newSingleThreadScheduledExecutor() ; 
    ses.scheduleAtFixedRate( myRunnable , initialDelay, period, TimeUnit.SECONDS ) ;
    …
    … // Eventually shut down the executor service gracefully using boilerplate code found in Javadoc.
    

    For that shutdown boilerplate, see ExecutorService Javadoc.

    Example code

    We define an app to hold a reference to latest recorded moment. Because we expect to access this reference across threads, we use AtomicReference for thread-safety.

    Our task is a nested class, UpdateMomentTask. That class implements Runnable, which means it promises to implement a method named run. The ScheduledExecutorService calls that run method on our behalf.

    On each execution of its run method, we capture the current moment by instantiating a new Instant object. Our task then stores a reference to that object as the payload of our app’s AtomicReference variable named moment.

    Notice no static anywhere (except main).

    package work.basil.example.now;
    
    import java.time.Duration;
    import java.time.Instant;
    import java.util.concurrent.*;
    import java.util.concurrent.atomic.AtomicReference;
    
    public class App
    {
        AtomicReference < Instant > moment = new AtomicReference <>( Instant.now() );
    
        public static void main ( String[] args )
        {
            App app = new App();
            app.demo();
        }
    
        private void demo ( )
        {
            System.out.println( "Demo start: " + Instant.now() );
    
            ScheduledExecutorService ses = Executors.newSingleThreadScheduledExecutor();
            Runnable task = new UpdateMomentTask();
            ses.scheduleAtFixedRate( task , 0 , 10 , TimeUnit.SECONDS );
    
            // We sleep this main thread to give our background task time enough to do some work. 
            try { Thread.sleep( Duration.ofMinutes( 1 ).toMillis() ); } catch ( InterruptedException e ) { throw new RuntimeException( e ); }
            this.shutdownAndAwaitTermination( ses );
            System.out.println( "Last recorded moment was: " + this.moment.get().toString() );
    
            System.out.println( "Demo end: " + Instant.now() );
        }
    
        class UpdateMomentTask implements Runnable
        {
            @Override
            public void run ( )
            {
                Instant currentMoment = Instant.now();
                moment.set( currentMoment );
                System.out.println( "Set the moment to: " + currentMoment );
            }
        }
    
        // Boilerplate taken from https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/ExecutorService.html.
        // Slightly modified here.
        void shutdownAndAwaitTermination ( ExecutorService executorService )
        {
            executorService.shutdown(); // Disable new tasks from being submitted
            try
            {
                // Wait a while for existing tasks to terminate.
                if ( ! executorService.awaitTermination( 60 , TimeUnit.SECONDS ) )
                {
                    executorService.shutdownNow(); // Cancel currently executing tasks
                    // Wait a while for tasks to respond to being cancelled.
                    if ( ! executorService.awaitTermination( 60 , TimeUnit.SECONDS ) )
                    { System.err.println( "Executor service did not terminate. " + Instant.now() ); }
                }
            }
            catch ( InterruptedException ex )
            {
                // (Re-)Cancel if current thread also interrupted.
                executorService.shutdownNow();
                // Preserve interrupt status
                Thread.currentThread().interrupt();
            }
        }
    }
    

    When run:

    Demo start: 2022-09-23T20:46:06.420162Z
    Set the moment to: 2022-09-23T20:46:06.435521Z
    Set the moment to: 2022-09-23T20:46:16.433719Z
    Set the moment to: 2022-09-23T20:46:26.425594Z
    Set the moment to: 2022-09-23T20:46:36.426603Z
    Set the moment to: 2022-09-23T20:46:46.422073Z
    Set the moment to: 2022-09-23T20:46:56.427291Z
    Set the moment to: 2022-09-23T20:47:06.423843Z
    Last recorded moment was: 2022-09-23T20:47:06.423843Z
    Demo end: 2022-09-23T20:47:06.430045Z