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"
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:
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
-
source
timeServiceScheduler.scheduleOnce(utcTime) { // do something }
- Java
-
source
Runnable task = () -> {/* do something*/}; scheduler.scheduleOnce(utcTime, task);
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
-
source
object 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
-
source
static 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
-
source
timeServiceScheduler.schedulePeriodically(Duration.ofMillis(50)) { /* do something*/ }
- Java
-
source
Runnable 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
-
source
timeServiceScheduler.schedulePeriodically(utcTime, Duration.ofMillis(50)) { /* do something*/ }
- Java
-
source
Runnable 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.