Time Service

The Time Service provides APIs to access time in Coordinated Universal Time (UTC) and International Atomic Time (TAI) time scales with up to nanosecond precision when available. It also provides APIs for scheduling periodic and non-periodic tasks in the future, which are optimized for scheduling at up to 1KHz frequency.

TMT has standardized on the use of Precision Time Protocol (PTP) as the basis of time to achieve sub-microsecond accuracy and precision between computers. The Time Service provides each participating computer with access to time synchronized by PTP.

At the telescope site, the Global Positioning System (GPS) provides an absolute time base, and a PTP grand master clock (a hardware device) synchronized to the GPS broadcasts the PTP protocol. Each computer system participating in the PTP system is synchronized with GPS and each other using the PTP protocol. For higher accuracy in time measurements hardware time stamping is required, and those computers should be fitted with PTP capable Network Interface Cards (NIC).

In order to read the time with high precision, the Time Service relies on making native calls to the Linux kernel libraries, since Java 8 supports only millisecond precision. Java Native Access (JNA) is used internally in Time Service to make native calls that return the required precision. The implementation of Time Service Scheduler is based on the Akka Scheduler, which is designed for high-throughput tasks rather than long-term, cron-like scheduling of tasks.

Dependencies

The Time Service comes bundled with the Framework, no additional dependency needs to be added to your build.sbt file if using it. To use the Time Service without using the framework, add this to your build.sbt file:

sbt
libraryDependencies += "com.github.tmtsoftware.csw" %% "csw-time-scheduler" % "5.0.1"
Note

Applicable only to Linux Users.

If your machines are not PTP enabled/synced, you need to set the tai_offset kernel variable on your machine. This value is used in APIs like TAITime.now() or TAITime.offset and in scheduling APIs which take TAI time.

To set the tai offset, run the following command.

sudo ntptime -T 37

Install ntp, ntpdate packages to get this command on your machine.

Time Service API Flavors

There are total three APIs provided by the Time Service:

  • TMTTime API: This API provides a way to get current UTC or TAI time.
  • TMTTimeHelper API: This API provides additional time zone related functionality on top of TMTTime.
  • Scheduler API: This API provides various methods to schedule future or periodic tasks.

TMTTime API

TMTTime represents an instantaneous point in time with nanosecond precision. It is a wrapper around Instant and provides additional information about the timescale of the instant.

TMTTime supports two timescales:

  • Coordinated Universal Time ( UTCTime )
  • International Atomic Time ( TAITime )

Get Current Time

Gets the current UTC/TAI time with nanosecond precision.

Scala
source// get current UTC time
val utcTime: UTCTime = UTCTime.now()

// get current TAI time
val taiTime: TAITime = TAITime.now()
Java
source// get current UTC time
private final UTCTime utcTime = UTCTime.now();

// get current TAI time
private final TAITime taiTime = TAITime.now();

Note that time is returned as a UTCTime or TAITime object so that it is possible to determine the time scale of the time value by inspection.

Creating custom time instances

To create custom time instances, you can use the default constructors of UTC and TAI times.

Scala
source// creating a UTCTime of an hour ago
val utcTimeOfHourAgo: UTCTime = UTCTime(Instant.now().minusSeconds(3600))

// creating a TAITime of an hour ago
val taiTimeOfHourAgo: TAITime = TAITime(Instant.now().minusSeconds(3600))
Java
source//creating a UTCTime of an hour ago
private final UTCTime utcTimeOfHourAgo = new UTCTime(Instant.now().minusSeconds(3600));

//creating a TAITime of an hour ago
private final TAITime taiTimeOfHourAgo = new TAITime(Instant.now().minusSeconds(3600));

Converting from UTC to TAI Time and Vice-versa

Each time object provides a way to convert to the other.

Scala
source// UTC to TAI
val taiTime: TAITime = utcTime.toTAI

// TAI to UTC
val utcTime0: UTCTime = taiTime.toUTC
Java
source// UTC to TAI
TAITime taiTime = utcTime.toTAI();

// TAI to UTC
UTCTime utcTime = taiTime.toUTC();

TMTTimeHelper API

This API provides additional time zone related functionality on top of TMTTime. It allows users to get a Java ZonedDateTime representation of a TMTTime.

At Local Time Zone

Gets the given TMTTime at the local time zone. The local time zone is fetched from the calling system’s default time zone.

Scala
source// Get UTCTime at local timezone
val utcLocalTime: ZonedDateTime = TMTTimeHelper.atLocal(utcTime)

// Get TAITime at local timezone
val taiLocalTime: ZonedDateTime = TMTTimeHelper.atLocal(taiTime)
Java
source// Get UTCTime at local timezone
ZonedDateTime utcLocalTime = TMTTimeHelper.atLocal(utcTime);

// Get TAITime at local timezone
ZonedDateTime taiLocalTime = TMTTimeHelper.atLocal(taiTime);

At Hawaii (HST) Timezone

Gets the given TMTTime at the Hawaii time zone.

Scala
source// Get UTCTime at Hawaii (HST) timezone
val utcHawaiiTime: ZonedDateTime = TMTTimeHelper.atHawaii(utcTime)

// Get TAITime at Hawaii (HST) timezone
val taiHawaiiTime: ZonedDateTime = TMTTimeHelper.atHawaii(taiTime)
Java
source// Get UTCTime at Hawaii (HST) timezone
ZonedDateTime utcHawaiiTime = TMTTimeHelper.atHawaii(utcTime);

// Get TAITime at Hawaii (HST) timezone
ZonedDateTime taiHawaiiTime = TMTTimeHelper.atHawaii(taiTime);

At Custom Timezone

Gets the given TMTTime at the specified time zone.

Scala
source// Get UTCTime at specified timezone
val utcKolkataTime: ZonedDateTime = TMTTimeHelper.atZone(utcTime, ZoneId.of("Asia/Kolkata"))

// Get TAITime at specified timezone
val taiKolkataTime: ZonedDateTime = TMTTimeHelper.atZone(taiTime, ZoneId.of("Asia/Kolkata"))
Java
source// Get UTCTime at specified timezone
ZonedDateTime utcKolkataTime = TMTTimeHelper.atZone(utcTime, ZoneId.of("Asia/Kolkata"));

// Get TAITime at specified timezone
ZonedDateTime taiKolkataTime = TMTTimeHelper.atZone(taiTime, ZoneId.of("Asia/Kolkata"));

Scheduler API

This API provides various methods to schedule periodic or non-periodic, one-shot tasks in the future.

For component developers, the scheduler API is provided as a TimeServiceScheduler object in the CswContext object injected into the ComponentHandlers class provided by the framework.

If you are not using csw-framework, you can create TimeServiceScheduler using TimeServiceSchedulerFactory as follows:

Scala
source// create time service scheduler using the factory method
implicit val actorSystem: typed.ActorSystem[_]         = ctx.system
implicit val scheduler: Scheduler                      = actorSystem.scheduler
implicit val executionContext: ExecutionContext        = actorSystem.executionContext
private val timeServiceScheduler: TimeServiceScheduler = new TimeServiceSchedulerFactory().make()
Java
source// create time service scheduler using the factory method
TimeServiceScheduler scheduler = new TimeServiceSchedulerFactory(actorSystem.scheduler()).make( actorSystem.executionContext());

For all scheduler calls, an instance of Cancellable is returned which can be used to cancel the execution of the future tasks.

Schedule Once

Schedules a task to execute once at the given start time. The startTime is a TMTTime and can be either a UTCTime or TAITime.

Scala
sourcetimeServiceScheduler.scheduleOnce(utcTime) {
  // do something
}
Java
sourceRunnable task = () -> {/* do something*/};
scheduler.scheduleOnce(utcTime, task);
Warning

Note that callbacks are asynchronous and can be potentially executed in an unknown thread. Therefore, if there is a need to mutate state when the time expires, it is recommended to send a message to an Actor, and keep any mutable state within the actor where it can be managed safely. This is true for any CSW API with a callback. The schedule once with ActorRef can often be used in this scenario.

Schedule Once With ActorRef

Schedules sending of the message to the provided actorRef at the given start time. The startTime can be either UTCTime or TAITime.

Scala
sourceobject SchedulingHandler {
  def behavior: Behavior[UTCTime] =
    Behaviors.setup { ctx =>
      // setup required for the actor

      Behaviors.receiveMessage { case _ => // handle the message to execute the task on scheduled time and return new behavior
        Behaviors.same
      }
    }
}

private val actorRef: ActorRef = ctx.spawnAnonymous(SchedulingHandler.behavior).toClassic

timeServiceScheduler.scheduleOnce(utcTime, actorRef, UTCTime.now())
Java
sourcestatic class SchedulingHandler {

    public static Behavior<UTCTime> behavior() {
        // handle the message to execute the task on scheduled time
        return null;
    }
}

Cancellable schedule() {
    ActorRef actorRef = Adapter.toClassic(ctx.asJava().spawnAnonymous(SchedulingHandler.behavior()));

    return scheduler.scheduleOnce(utcTime, actorRef, UTCTime.now());
}

Schedule Periodically

Schedules a task to execute periodically at the given interval. The first task is executed once immediately without any initial delay followed by periodic executions. In case you do not want to start scheduling immediately, you can use the overloaded method for schedulePeriodically() with startTime as shown in the next example.

Scala
sourcetimeServiceScheduler.schedulePeriodically(Duration.ofMillis(50)) { /* do something*/ }
Java
sourceRunnable task = () -> {/* do something*/};
scheduler.schedulePeriodically(Duration.ofMillis(50), task);

Schedule Periodically with Start Time

Schedules a task to execute periodically at the given interval. The task is executed once at the given start time followed by execution of task at each interval. The startTime can be either UTCTime or TAITime.

Scala
sourcetimeServiceScheduler.schedulePeriodically(utcTime, Duration.ofMillis(50)) { /* do something*/ }
Java
sourceRunnable task = () -> {/* do something*/};
scheduler.schedulePeriodically(utcTime, Duration.ofMillis(50), task);

As with the schedule once API, there is also a periodic schedule API that takes a message and ActorRef.

Technical Description

See Time Service Technical Description.

Source code for TMTTime examples

Source code for Scheduler examples