Clock Club

Examples on clocks and time and time setting

Projects in this Repository
Bibliography
Timepieces
Organizations
Software and Hardware Tools
Parts and Suppliers
Notes

View the Project on GitHub ITPNYU/clock-club

Tracking Time In A Networked System

To keep track of time in a connected system, you need coordinated time. Networked computers all use Coordinated Universal Time (UTC) these days, and most operating systems count time using the Unix Epoch Time, which counts the number of seconds since January 1, 1970. The international standard for representing time as a string is the ISO8601 standard, in which dates look like this: 2021-06-09T14:51:31Z. The advantage of using these standards is that when you’re transmitting time information from client to server, both sides can translate each others’ times, and many programming environments include tools for calculating time differences in UTC.

Epoch Converter gives you calculators for understanding the relationship between Unix timekeeping and everyday timekeeping. When you’re tracking the duration of an event, you often need to calculate hours, days, weeks, months, and years in terms of seconds or milliseconds Epoch Converter offers the following:

These numbers are key to calculating any time difference, as you’ll see below.

Time and Date in JavaScript

In JavaScript, the Date API, which is based on UTC, lets you get time, calculate differences between time, convert a time to your local timezone, etc. It can be used on the client side or the server side.

You can get the Unix epoch time using the JS Date API in a few ways:

let myDate = new Date();

This will return a Date object, which will generally be represented by a string, like so: Thu Apr 28 2022 09:42:51 GMT-0400 (Eastern Daylight Time) Because it’s a Date object, you can run the Date API functions on it. For example:

There are many other commands in the Date API, allowing you to represent date and time in just about any way you want. You can also create Date objects and set the date using setter commands. Every getter command, such as .getHours(), has an equivalent setter (in this case, .setHours()).

You can also get the Unix time as a number of milliseconds like so:

let myDate = Date.now();

The JS Date API uses milliseconds as its basis, not seconds, so to convert to epoch time, you need to divide by 1000.

If you have two dates and you want to know the difference between them, apply the calculations above. For example:

var now = new Date('3/12/97 1:23:45');
var then = new Date('3/31/93 13:23:00');

How far apart are these two date-times? It depends on your time scale. Start with the number of seconds in each time unit:

const secondsInAnHour = 3600;
const secondsInADay = 86400;
const secondsInAWeek = 604800;
const secondsInAMonth = 2629743;
const secondsInAYear = 31556926;

Then calculate the difference using those:

// JS gives you the values in milliseconds, so:
var difference = now - then;
// the total difference between now and then, in seconds (124545645):
var secondsDiff = (now - then) / 1000;
// in minutes (2075760.75):
var minutesDiff = secondsDiff / 60; 
// in hours (34596.0125):
var hoursDiff = secondsDiff / secondsInAnHour; 
// in days (1441.5005208333334):
var daysDiff = secondsDiff / secondsInADay; 
// in weeks (205.92864583333332):
var weeksDiff = secondsDiff / secondsInAWeek; 
// in months (47.36038654727857):
var monthsDiff = secondsDiff / secondsInAMonth; 
// in years (3.9466976282797632):
var yearsDiff = secondsDiff / secondsInAYear; 

What if you want to know the number of years, months, days, hours, minutes, and seconds between the two? (It’s 3 years, 11 months, 10 days, 12 hours, 0 minutes, and 45 seconds). For that, you need to break the difference down into parts. To do that for any given time unit, take the remainder (the modulo value) of the value in seconds divided by the number of seconds in that unit. Then divide by the length in seconds of a unit you want. Finally, round it to get rid of the fraction, using Math.floor().

Here it is, one unit at a time:

var years = Math.floor(secondsDiff / secondsInAYear);
var months = Math.floor((secondsDiff % secondsInAYear) / secondsInAMonth);
var weeks = Math.floor((secondsDiff % secondsInAYear) / secondsInAWeek);
var days = Math.floor((secondsDiff % secondsInAMonth) / secondsInADay);
var hours = Math.floor((secondsDiff % secondsInADay) / secondsInAnHour);
var minutes = Math.floor((secondsDiff % secondsInAnHour) / 60);
var seconds = Math.floor((secondsDiff % 60));

When you’ve got all of that, print it out:

console.log(years + ' years '
+ months + ' months '
+ days + ' days '
+ hours + ' hours ' 
+ minutes + ' minutes '
+ seconds + ' seconds');

You should get 3 years 11 months 10 days 12 hours 0 minutes 45 seconds.

Here is a node server that gets the time from its host computer using the JavaScript Date commands.

Time Logging on a Microcontroller

When you’re datalogging, it’s often necessary to attach a time stamp to each set of sensor readings, and you need coordinated time for that. The simplest solution if you’re logging sensor data to a web server via WiFi is to let the server timestamp each reading. However, there may be reasons to time stamp locally by the microcontroller as well. If that is the case, then you want to attach a real-time clock to the microcontroller, or use a controller with one built-in, like the Nano 33 IoT or the MKR boards. The RTCZero library lets you access that realtime clock, and the WiFi libraries let you set the clock by making a network time server request, using the command WiFi.getTime().

On the Arduino SAMD boards (Nano 33 IoT, BLE, MKR boards), there is a Real-time Clock that allows you to keep time in hours, minutes and seconds. the RTCZero library allows you to access this feature of the microcontroller. There several examples for setting the time using this library in this repository.

Time Intervals

Sometimes you need something to happen periodically. The Arduino RTCZero library has an excellent alarm feature for this, but it’s limited to one alarm per minute at best. Sometimes, though, you need to check every few seconds. You might think to check this by saying:

  if (thisSecond - lastReadTime >= interval) {
    // the interval has passed.
  }

But this doesn’t work consistently. Why? The seconds roll over.

How far apart are the times 11:59:53 and 12:00:03? They’re ten seconds apart. The formula above would give you -50 which is not right. If the current second is less than interval seconds from the minute, you need to account for that. Here’s a way to do that:

  // check for change:
  int change = thisSecond - lastSecond;
  // if the change is negative, you're close to the minute:
  if (change < 0) {
    change = (60 - lastSecond) + thisSecond;
  }
  if (change >= interval) {
    Serial.println("Interval has passed");
    // update lastSecond:
    lastSecond = thisSecond;
  }

This example shows how to implement this.

Another way to handle this is by tracking the elapsed time and making it roll over every interval, like so:

  // get the current seconds:
  int thisSecond = rtc.getSeconds();
  // if there's a difference:
  if (lastSecond != thisSecond) {
    // increment elapsed time
    elapsedTime++;
    // modulo it with the interval so it rolls over every interval:
    elapsedTime %= interval;
    // when the interval passes, update:
    if (elapsedTime == 0) {
        Serial.println("Interval has passed");
    }
    // update lastSecond:
    lastSecond = thisSecond;
  }

The RTCInterval example shows how to implement this.

Tracking Uptime

It’s often useful to track how long your microcontroller has been running. In this example, you can see that in action. It uses some some time difference calculations similar to the JavaScript ones above in the getUptime function (line 122). Because you’re working in integers, the math can be simpler:

unsigned long upTime = rtc.getEpoch() - startTime;
int upSecs = upTime % 60;
int upMins = (upTime % 3600L) / 60;
int upHours = (upTime % 86400L) / 3600;
int upDays = (upTime % 31556926L) / 86400L;

The L on the end of the constants is a C language formatting element indicating that they should be stored as long integers.