Time Algorithms

A time algorithm is an algorithm associated with parsing, manipulating, computing, and more generally working with, time formats. To begin, we'll start by presenting the two principle source of issues for time algorithms: (1) units of time, and (2) linguistic fence post problems.

Units of Time

Here is an overview of the various ways we can measure time:

NameLengthRemark
nanosecond1 ns, 0.000 000 001 sOne billionth of a second
microsecond1 Β΅s, 0.000 001 sOne millionth of a second
millisecond1 ms, 0.001 sOne thousandth of a second
jiffy1/60 s or 1/50 sUsed in electronics; also, "I'll be back in a jiffy"
second1 sSI base unit for time
minute60 s
hour1 hr, 60 min, 120 s
day1 d, 24 hr, 1440 min, 86400 s
week1 w, 7 d, 168 hr, 10080 min, 604800 s
month1 mo, [28, 31] d
semester18 w
year1 yr, [365, 366] d
common year1 yr, 52 w, 365 d
leap year1 yr 1 d, 52 w + 2 d, 366 d
olympiad4 yr
lustrum5 yrDuring the Roman empire, the interval between censuses
decade10 yr
indiction15 yrDuring the Roman empire, the interval between tax assessments
jubilee50 yr
century100 yr
millennium1000 yr
megaannum1 Ma, 1000 millenia1 million years
galactic year1 gyThe amount of time it takes the solar system to orbit the Milky Way Galaxy's center. Roughly 230 million years.

The table above is just a subset of the units of time used by humans. This presents the first difficulty with handling time data: the sheer number of time units to consider. For a time algorithm to be correct, it work with time units correctly.

Linguistic Fence Posts

The second problem presented is th linguistic fence post. This problem stems from the way we use language. For example, when we say:

"𝐴 is due within 2 days of x{x}."

There are two possible meanings.

  1. If we count x{x} as date0:{\text{date}_0:}

    date0,date1,date2 \text{date}_0, \text{date}_1, \cancel{\text{date}_2}

    meaning, 𝐴 is due on x+1.{x + 1.} This is the day after x.{x.}

  2. If, however, we don't count x:{x:}

    date0,date1,date2 \cancel{\text{date}_0}, \text{date}_1, \text{date}_2

    meaning, 𝐴 is due on x+2.{x + 2.} This is two days after x.{x.} $$

Moreover, depending on the context, we might have additional parameters: Do we count weekends? Do we count holidays? Is the day 24 hours or business days (8 hours)? The same problem exists when we say:

"𝐴 is due in 2 days."

Suppose that we're told the above on date y.{y.}

  1. If we count y:{y:}

    date0,date1,date2 \text{date}_0, \text{date}_1, \cancel{\text{date}_2}

    meaning, 𝐴 is due on y+1.{y + 1.} This is the day after y.{y.}

  2. If we don't count y:{y:}

    date0,date1,date2 \cancel{\text{date}_0}, \text{date}_1, \text{date}_2

    meaning, 𝐴 is due on y+2.{y + 2.} This is two days after x.{x.} $$

Unfortunately, the only way to be absolutely certain of the intended meaning is through specification.

Wrap-around Counting

A common operation in time algorithms is modular arithmetic. Accordingly, it's imperative that we have a strong understanding of the operation.

First, modular arithmetic is a system of arithmetic for the integers. The arithmetic we're taught in elementary school is a system of arithmetic for the reals (or, at least a subset of the reals β€” it's unlikely that we're having children add gargantuan numbers). In real arithmetic, the result of an operation could be any real number: either 0, negative, or positive.

In modular arithmetic, the result of an operation will "wrap around" a certain value once it reaches a particular integer called the modulus. Let's illustrate with the classic example, the wall clock.

A wall clock displays 12 hours:

123456789101112

Let's say the current time is 8:00am. We'll mark at as x.

12345678x9101112

3 hours later, we land at 11:

12345678x91011x+312

In another 3 hours, (11+3), we land at 2:

12x+6345678x91011x+312

Thus, we have: 8+6=2.{8 + 6 = 2.} This is what we mean by modular arithmetic. For the wall clock, the modulus is 12.{12.} Moreover, we say that this is mod-12 arithmetic, or arithmetic mod-12: For all arithmetic operations, once a result's value hits 12,{12,} its value starts back at the initial value.

Having seen the basic example, let's add a little more detail.

Week Day Determining

Consider the following problem:

problem. Today is Tuesday. What day will it be in 17 days?

Let's start by assigning each day of the week an index, starting at 0:

SU0MO1TU2WE3TH4FR5SA6

Here, the modulus is 7. If we viewed the diagram above as an array, this corresponds to the array's length:

SU0MO1TU2WE3TH4FR5SA6
L=7 \texttt{L} = 7

To determine the weekday 17 days from Tuesday, we first determine Tuesday's index. Here, it's 2.

i=2 \texttt{i} = 2

Then, we add the 17 days to 2:

i+17=19 \texttt{i} + 17 = 19

Next, we divide this result by the modulus (the array's length) and obtain the remainder:

i+17L=197=2rem5\begin{aligned} \dfrac{\texttt{i} + 17}{\texttt{L}} &= \dfrac{19}{7} \\[1em] &= 2 \texttt{rem} 5 \end{aligned}

What does this tell us? It tells us that we've gone through 2 cycles plus 5 positions. That is, we've wrapped around the circulary array 2 timesputting us at SU, then 5 extra, which is FRI.

Here's a simple implementation of a function for answering the problem using TypeScript:

type Weekday =
  | 'Sunday'   | 'Monday' | 'Tuesday'  | 'Wednesday'
  | 'Thursday' | 'Friday' | 'Saturday';

function WhichDay(currentDay: Weekday, days: number) {
  const weekdays = [
    'Sunday'   , 'Monday' , 'Tuesday'  , 'Wednesday',
    'Thursday' , 'Friday' , 'Saturday' ,
  ];
  const L = 7;
  let DayIndex = 0;
  switch (currentDay) {
    case 'Monday'    :  DayIndex = 1;  break;
    case 'Tuesday'   :  DayIndex = 2;  break;
    case 'Wednesday' :  DayIndex = 3;  break;
    case 'Thursday'  :  DayIndex = 4;  break;
    case 'Friday'    :  DayIndex = 5;  break;
    case 'Saturday'  :  DayIndex = 6;  break;
		default          :  throw new Error("Unrecognized weekday.");
  }
  const sum = DayIndex + days;
  const index = sum % L;
  return weekdays[index];
};

Examining this function, it should be apparent why the % operator in programming languages is so useful. The % operator, in mathematical terms, is the natural number remainder operation:

definition. Where a{a} and b{b} are natural numbers, the operation:

aΒ %Β b a \tmod b

returns the remainder of ab.{\dfrac{a}{b}.}

To truly under what this definition means, we delve briefly into Euclidean division.

Divisibility

Whenever we divide an integer a{a} by an integer b,{b,} we have two possible outcomes:

  1. a{a} can be divided into b{b} evenly (no remainder exists).
  2. a{a} cannot be divided into b{b} evenly (there exists a remainder).

In logic terms:

aΓ·b={βˆƒremβˆ„rem a \div b = \begin{cases} \exists \texttt{rem} \\ \nexists \texttt{rem} \end{cases}

where rem{\texttt{rem}} is a remainder. For example, 4 can be divided into 2 evenly. 120 can be divided into 40 evenly. 2, however, cannot be divided into 3 evenly, nor can 3 be divided into 2 evenly.

Quotient

Euclidean division is a mathematical operation based on this idea. First, we introduce two operations:

quotient. Let a∈Z{a \in \uint} and b∈Z+.{b \in \pint.} Then:

adivb=⌊abβŒ‹ a \text{div} b = \floor{\dfrac{a}{b}}

The quotient operation works as follows: First, suppose our friend Billy is a track and field star competing in a special circular track race. The track has a special property. It has numbered sections starting at 0 and ending at some natural number b{b}, the length of the track:

0123...b-1b

The race is won by whoever crosses b{b} the most times. Unfortunately, Billy has been running in other races and he's pretty tired. He tells us that he only has enough energy to cover a{a} total segments. The quotient tells us that, if Billy can run past a{a} segments, he can cross b{b} a total of adivb{a \text{div} b} times.

For example, suppose the track is 3{3} segments long. If Billy only has enough energy to cover 5{5} segments, then we have a=5{a = 5} and b=3.{b = 3.} Thus, starting at the segument numbered 0:

  1. Bill passes segment1. He has enough energy for 4 more segments.
  2. Billy passes segment2. He has enough energy for 3 more segments.
  3. Billy passes b{b}. He's crossed b{b} once. Now he only has enough energy for 2 more segments.
  4. Billy segment0. Only 1 more segment.
  5. Billy passes segment1. Bill collapses at segment2.

Billy only crossed b{b} once. The entire drama is expressed by writing: 5div3=1.{5 \text{div} 3 = 1.}

Let's be clear that this is not division operation. This is the quotient operation. It is a different operation. In fact, it's so different that the word "quotient" in this context is a verb. When we compute the quotient of two integers a{a} and b,{b,} we quotientize a{a} and b.{b.} This is standard mathematical terminology in group theory.

To see why this verb is necessary, here is the definition of real number division:

division. Let a,b∈R,{a, b \in \reals,} where bβ‰ 0.{b \neq 0.} Then:

aΓ·b=aβ‹…1b a \div b = a \cdot \dfrac{1}{b}

This is a very different definition from the previous. Real number division is an operation that can take real number inputs β€” that includes fractional numbers like 2.138 and irrational numbers like Ο€.{\pi.} Quotient, however, is an operation that can only take integer inputs. It does not work for fractional numbers or irrational numbers. For example, compare the outputs for the following functions:

The functions a{a} and b{b} divide x{x} by a constant. The functions f{f} and g{g} quotientize x{x} by a constant.

Importantly, a{a} can be negative, but b{b} cannot. What does a{a} being negative mean? Well, it just means that Billy runs backwards along the track instead of forwards. When we write βˆ’5div3=βˆ’2,{-5 \text{div} 3 = -2,} we're saying, if Billy has enough energy to cover 5 segments running backwards along the track, he completes βˆ’2{-2} runs along the track:

  1. Starts at segment0 (5 remaining).
  2. Billy runs backwards, and crosses b{b}. That's one cross.
  3. Past segment2. (3 remaining).
  4. Past segment1. (2 remaining).
  5. Past segment0. (1 remaining).
  6. Past b.{b.} Billy's crossed b{b} twice. 0 remaining.
  7. Billy collapses at segment1.

Remainder

The third operation we introduce is the remainder operation:

remainder. Let a∈Z{a \in \uint} and b∈Z+.{b \in \pint.} Then:

aΒ remΒ b=aβˆ’b(aΒ divΒ b)=aβˆ’b(⌊abβŒ‹)\begin{aligned} a~\text{rem}~b &= a - b(a~\text{div}~b) \\[1em] &= a - b\ar{\floor{\dfrac{a}{b}}} \end{aligned}

Relying on our friend Billy once more, the remainder operation tells us which segment Billy stops at. Recall that the segments are all labeled with natural numbers. Thus, the remainder can never be negative. It's always either 0 or a positive number β€” a natural number.

A few words of caution. The remainder operator is also called the modulo operator, with some textbooks opting to use the notation aβ€Šmodβ€Šb.{a \bmod b.} From personal experience, this is a very bad idea. As we'll see later, this notation looks too similar to the modular arithmetic notation, and can very easily lead to confusion. Accordingly, we will not use this notation, nor will we call it the modulo operator. It's the remainder operator, written as aremb.{a \text{rem} b.}

Next, many programming languages provide the rem{\text{rem}} operator as %. That is, aΒ %Β b{a \tmod b} is equivalent to aremb.{a \text{rem} b.} It is not, however, equivalent. Some programming languages will compute aβˆ’b(⌈a/bβŒ‰){a - b(\ceil{a/b})} rather than aβˆ’b(⌊a/bβŒ‹){a - b(\floor{a/b})} when a<0.{a < 0.} That is, they use the ceiling function for negatives. This is not inline with the mathematical definition. Some languages even allow b≀0,{b \leq 0,} when again, such a definition does not make much sense from a mathematical standpoint. For the rem{\text{rem}} operation, b{b} is always positive.

Euclidean Division

At this point, we can now introduction Euclidean division:

euclidean division. Let a∈Z{a \in \uint} and b∈Z+.{b \in \pint.} Then there exists two unique integers, q∈Z{q \in \uint} and r∈Z+,{r \in \pint,} such that:

a=bq+rΒ Β Β andΒ Β Β 0≀r<b a = bq + r~~~\text{and}~~~0 \leq r < b

This proposition is also called the division algorithm. What are the numbers q{q} and r{r}? Well, if we have an integer a{a} and positive integer b,{b,} q{q} and r{r} are given by:

q=adivbr=aremb \begin{aligned} q &= a \text{div} b \\ r &= a \text{rem} b \end{aligned}

The Euclidean algorithm introduces us to the notion of divisibility.

Divisibility

Divisibility is defined as follows:

divisibility. Let a,b∈Z,{a,b \in \uint,} where bβ‰ 0.{b \neq 0.} If there exists a q∈Z{q \in \uint} such that a=bq,{a = bq,} then we say that b{b} divides > a{a} and write:

b ∣ a b \dv a

Else, we say that b{b} does not divide a,{a,} and write:

b∀a b \nmid a

Casting this definition in Euclidean division terms:

lemma. Let a,b∈Z{a,b \in \uint} and bβ‰ 0.{b \neq 0.} b{b} divides a{a} iff:

arem∣b∣=0 a \text{rem} |b| = 0

Note the use of the absolute value. Remember that the remainder operator is defined only for b∈Z+.{b \in \pint.} Alternatively, we can also write:

Let a,b∈Z{a,b \in \uint} and bβ‰ 0.{b \neq 0.} b{b} divides a{a} iff:

ab∈Z \dfrac{a}{b} \in \uint

From the definition of divisibility, we have the notion of a multiple:

definition. Let a,b∈Z.{a,b \in \uint.} Then a{a} is a multiple of b{b} iff b∣a.{b \mid a.}

As well as the notion of a factor:

definition. Let a,b∈Z.{a,b \in \uint.} Then b{b} is a factor of b{b} iff b∣a.{b \mid a.}

There are several useful properties of divisibility to keep in mind:

properties. Let a,b,c∈Z{a, b, c \in \uint} where aβ‰ 0.{a \neq 0.} Then:

  1. If:
    • (a ∣ b){(a \dv b)}
    • β‡’{\nc} (a ∣ bc){(a \dv bc)} for any integer c.{c.}
  2. If:
    • (a ∣ b){(a \dv b)} and
    • (a ∣ c){(a \dv c)}
    • β‡’{\nc} (a ∣ b+c){(a \dv b + c)}
  3. If:
    • (a ∣ b){(a \dv b)} and
    • (b ∣ c){(b \dv c)}
    • β‡’{\nc} (a ∣ c).{(a \dv c).}

Modular Arithmetic

Now that we have the basic constructs of divisibility, we can now examine a more formal treatment of modular arithmetic. First, we begin with a definition:

definition. Let a,b∈Z{a, b \in \uint} and m∈Z+.{m \in \pint.} If:

m ∣ (aβˆ’b) m \dv (a - b)

then a{a} is congruent to b{b} modulo m,{m,} and we write:

a≑bΒ (β€Šmodβ€ŠΒ m) a \equiv b~(\bmod~m)

Put differently:

Let a,b,m∈Z{a,b,m \in \uint} and 0<m.{0 < m.} If there exists a q∈Z{q \in Z} such that:

aβˆ’b=mq a-b = mq

then:

a≑bΒ (β€Šmodβ€ŠΒ m) a \equiv b~(\bmod~m)

Or, alternatively:

Let a,b,m∈Z{a,b,m \in \uint} and 0<m.{0 < m.} Iff:

aβˆ’bm∈Z \dfrac{a-b}{m} \in \uint

then:

a≑bΒ (β€Šmodβ€ŠΒ m) a \equiv b~(\bmod~m)

For example, from the definition above, we know that 9≑3Β (β€Šmodβ€ŠΒ 2){9 \equiv 3~(\bmod~2)}. Why? Because:

9βˆ’32=62=3 \dfrac{9-3}{2} = \dfrac{6}{2} = 3

and 3{3} is an integer.