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