Super Clock Part 1

A super clock in PHP, jQuery and CSS3

EXPERIMENTAL

Life Simulator

Text Adventure in React

Time Slash/er 6.94ms

The time in Lord Howe (Australia) is 05:03pm, enjoy!
Elvis Salaris

A TMI Super Clock experiment in PHP, jQuery and CSS

Weekends, the best time for experiments.

In this episode of "useless endeavours" I have planned to build a Super Clock with a lot of extra info and conversions plus an extreme use of CSS attempting an early teens realistic look.

I'm talking about the 21st century teens and the tendency of trying to create a feeling of realism that was always short of the target and not quite there, for some reasons we couldn't explain: glossy buttons, metals, reflections, you know what I'm talking about...

But more on that in Part 2. For now let us concentrate on the amazing world of timekeeping, dates and conversions, and the unfathomable domain of Math.

The code is on github. It contains a JavaScript file (using jQuery), a PHP file, a huge CSS3 file, a JSON file with some data scrubbed off Wikipedia and a minimal HTML file as the entry point of all this.

The application

The entry point is the HTML file that includes a CSS and a JavaScript file. The last one is written in jQuery, I couldn't use React due to the amount of modifications to the DOM that were required.

The PHP file behave as an API, albeit with a single endpoint, with a JSON response. Most calculations happen server-side and in itself shows a standard design for web applications.

The Construct

We are in the age of OOP PHP, I'm therefore building all calculations in a class. Below is the first part of the code with a couple of comments.

class SuperClock {
    // Today's date by default, it is the center of all conversions and comparisons
    private $date = null; 

    // A variable holding the final result
    private $result = null;

    // The last know new moon at the time of writing, filled in construct
    private $last_new_moon = null;

    // The moon goes around in 29.5305882 days. Here in seconds
    protected $moon_synodic_period = 2551442.82048;

    public function __construct($now = null) {
        // Either you pass a date or it will default to 'now'. This date is in UTC.
        $date = new DateTime($now, new DateTimeZone('UTC'));
        $this->date = $date;

        // Got this somewhere from the interweb
        $this->last_new_moon = new DateTime('2019-01-06 01:28', new DateTimeZone('UTC'));
    }

    /* ... */

Not much going on apart from the importance of $this->date, used everywhere, that could be dynamically set, but that needs to be in UTC for the Math to work.

Moon Phase Calculator

The moon phase calculation is quite straight forward: our Moon completes a cycle in 29 days and a half so the difference of days from our date (today by default) to the last known new moon and the modulo of it will tell us at which point of the cycle we are.

It is approximated and I'm not ashamed to say so.

    /* ... */

    // The following approximation is necessary because
    // new and full moons as well as the quarters are instants in time and not a range:
    // they would be practically undetectable otherwise.
    private function defineMoonPhases($val){
        if($val >= 0.98 || $val <= 0.02){
            return 'new_moon';
        } elseif ($val <= 0.23) {
            return 'waxing_crescent';
        } elseif ($val <= 0.27) {
            return 'first_quarter';
        } elseif ($val <= 0.48) {
            return 'waxing_gibbous';
        } elseif ($val <= 0.52) {
            return 'full_moon';
        } elseif ($val <= 0.73) {
            return 'waning_gibbous';
        } elseif ($val <= 0.77) {
            return 'last_quarter';
        } elseif ($val < 0.98) {
            return 'waning_crescent';
        }
    }

    private function getMoonPhase() {
        // the difference between DATE and the last know new moon
        $diff_from_last_new_moon = $this->date->getTimestamp() - $this->last_new_moon->getTimestamp();

        // divide the difference by a moon cycle
        $number_of_cycles = $diff_from_last_new_moon / $this->moon_synodic_period;

        // normalize the value in a range from 0 to 1
        $normalized_value = fmod($number_of_cycles, 1);

        // feeding the value to our phase finder above we should get the phase of the moon
        return $this->defineMoonPhases($normalized_value);
    }

    /* ... */

Mars Standard Time

Earth is not the center of the Universe, even if to be honest it is, as in an expanding Universe every point is the center, but I meant more metaphorically because our idea of timekeeping has always been centered around the sun and the moon, day and nights, months and seasons, a year-round travel around the sun...

Other planets have their own seasons, different length of day and year, and more moons than we could handle in a simple calendar. Defining a local time is surprisingly puzzling and unless you are a scientist you might not have a reason to do so.

Let's take Mars for example, and let's imagine a remote conference game of poker for which it is important to define a time and date for it to happen and unless martians are using two watches and calendars, you might need to use a conversion to a standard martian time.

PS: this example is absolutely preposterous: no one uses watches or calendars anymore.

A day on Mars is 39.5 minutes longer than Earth's, the year has 8 seasons and it's twice as long as our own: quite easy to miss your game.

You can find the formulae to convert Earth UTC to Mars Standard Time on Wikipedia, while the PHP I made is just below.

    /* ... */

    private function getCoordinatedMarsTime() {
        // We need Julian dates, leap seconds and Terrestrial Time 
        $jd_utc = $this->date->getTimestamp() / 86400 + 2440587.5;

        // Leap seconds! Fascinating stuff...
        $tai_utc = 37;

        // Mars Standard Date or number of days since December 29th 1873
        $msd = ($jd_utc + $tai_utc / 86400 - 2405522.0025054) / 1.0274912517;

        // And finally the time! The @ sign allows you to use timestamps to create date objects in PHP
        $mst = new DateTime('@' . ceil(fmod($msd, 1) * 24 * 60 * 60));

        return ['date' => $msd, 'time' => $mst->format('H:i:s')];
    }

    /* ... */

Runic Calendar

This was extremely funny to make as my research brought me into fascinating artifacts, ancient traditions, cultures and well you know, runes are super cool not only if you are a nerd.

Most types of runes have their own ASCII code, but what I needed was the Younger Futhark, used in a 1638's Swedish calendar that became the starting point of my calculations.

This calendar is based on a Metonic Cycle that shows how moon phases will fall on the same days every 19 years (or so).

In such calendars, devised in the Middle-Ages, the year always starts on Monday (or better ᚠ FÉ), weekdays are identified with the first seven letter of the 16-runes alphabet while to count our position in the 19-years cycle, three additional runes were used.

PS: HTML codes for these runes are available with &#x16CODE; where code is a hexadecimal 2 digit number. For instance ᚠ (FÉ) is &#x16a0;

    /* ... */  

    private function runesCalendar(){
        // Younger Futharkh (add &#x16[..];)
        $alphabet = [
            ['name' => 'fé',        'code' => 'a0'],
            ['name' => 'úr',        'code' => 'a2'],
            ['name' => 'thurs',     'code' => 'a6'],
            ['name' => 'as',        'code' => 'ac'],
            ['name' => 'reið',      'code' => 'b1'],
            ['name' => 'kaun',      'code' => 'b4'],
            ['name' => 'hagall',    'code' => 'bc'],
            ['name' => 'nauðr',     'code' => 'be'],
            ['name' => 'ísa',       'code' => 'c1'],
            ['name' => 'ár',        'code' => 'c5'],
            ['name' => 'sól',       'code' => 'cb'],
            ['name' => 'týr',       'code' => 'cf'],
            ['name' => 'björk',     'code' => 'd2'],
            ['name' => 'maðr',      'code' => 'd8'],
            ['name' => 'lögr',      'code' => 'da'],
            ['name' => 'yr',        'code' => 'e6']
        ];

        // additional 3 runes to get to a 19-years Metonic cycle
        $additional = [
            ['name' => 'arlaug',    'code' => 'ee'],
            ['name' => 'tvimadur',  'code' => 'ef'],
            ['name' => 'belgthor',  'code' => 'f0']
        ];

        // we need all 19 runes for the year's golden numbers
        $golden_numbers = array_merge($alphabet, $additional);

        // My starting point is a Swedish calendar from the year 1638
        $sinceAD1638 = $this->date->format('Y') - 1638;

        // Modulo to get our point in the Metonic Cycle
        $point_in_cycle = $sinceAD1638 % 19;

        // The day of the year of $this->date
        $since_beginning_of_the_year = $this->date->format('z');

        // Modulo of seven to get our rune for the weekday
        $day_in_cycle = $since_beginning_of_the_year % 7;

        return [
            'year' => $golden_numbers[ $point_in_cycle ],
            'day' => $golden_numbers[ $day_in_cycle ]
        ];
    }

    /* ... */

Greek and Babylonian names of the months

Found these on Wikipedia: they are approximated.

    /* ... */

    private function monthsNames(){
        $months = [
            ['greek_en' => 'Gamelion',        'greek_gr' => 'Γαμηλιών',     'babylonian' => 'Šabaṭu'],
            ['greek_en' => 'Anthesterion',    'greek_gr' => 'Ἀνθεστηριών',  'babylonian' => 'Addaru'],
            ['greek_en' => 'Elaphebolion',    'greek_gr' => 'Ἑλαφηβολιών',  'babylonian' => 'Nisānu'],
            ['greek_en' => 'Mounichion',      'greek_gr' => 'Μουνιχιών',    'babylonian' => 'Āru'],
            ['greek_en' => 'Thargelion',      'greek_gr' => 'Θαργηλιών',    'babylonian' => 'Simanu'],
            ['greek_en' => 'Skirophorion',    'greek_gr' => 'Σκιροφοριών',  'babylonian' => 'Dumuzu'],
            ['greek_en' => 'Hekatombaion',    'greek_gr' => 'Ἑκατομβαιών',  'babylonian' => 'Abu'],
            ['greek_en' => 'Metageitnion',    'greek_gr' => 'Μεταγειτνιών', 'babylonian' => 'Ulūlu'],
            ['greek_en' => 'Boedromion',      'greek_gr' => 'Βοηδρομιών',   'babylonian' => 'Tišritum'],
            ['greek_en' => 'Pyanepsion',      'greek_gr' => 'Πυανεψιών',    'babylonian' => 'Samnu'],
            ['greek_en' => 'Maimakterion',    'greek_gr' => 'Μαιμακτηριών', 'babylonian' => 'Kislimu'],
            ['greek_en' => 'Poseideon',       'greek_gr' => 'Ποσειδεών',    'babylonian' => 'Ṭebētum'],
        ];

        return $months[$this->date->format('n') - 1];
    }

    /* ... */

The Mayan calendar

The Mayan calendar is extraordinary. Our South American ancestors devised a simple system to measure the passing of time that could be used for centuries.

Here's how it works:

  • 1 K'in is a day
  • 20 K'in make a Winal (or a 20-days month)
  • 18 Winal make a Tun: it's made of 360 days, 18 twenty-days months, almost one year
  • 20 Tun make a K'atun
  • 20 K'atun make a Baktun, almost 400 years
  • 13 Baktun make a Long Count

For Mayans time began on the 1th day of August of the year 3114 BC.

The world ended on December 20th, 2012 when the Mayan calendar turned 12 Baktun, 19 K'atun, 19 Tun, 17 Winal, 19 K'in also known as the end of the first Long Count. The world started again the following day on 13.0.0.0.0

    /* ... */

    private function mayanCalendar(){
        // We need August 11th, 3114BC in Julian Days
        $beginning_of_time = gregoriantojd(8,11,-3114);

        // Mayan cycles in days
        $baktun = 144000;
        $katun = 7200;
        $tun = 360;
        $winal = 20;

        // We need $this->date converted into Julian Days
        $julian_day = gregoriantojd(
            $this->date->format('n'),
            $this->date->format('j'),
            $this->date->format('Y')
        );

        // Days since the beginning of time
        $difference = $julian_day - $beginning_of_time;

        // Modulo by cycle and keep the integer with floor
        $baktun_qty = floor($difference / $baktun);
        $difference = $difference % $baktun;

        // Modulo by cycle and keep the integer with floor
        $katun_qty = floor($difference / $katun);
        $difference = $difference % $katun;

        // Modulo by cycle and keep the integer with floor
        $tun_qty = floor($difference / $tun);
        $difference = $difference % $tun;

        // Modulo by cycle and keep the integer with floor
        $winal_qty = floor($difference / $winal);
        $difference = $difference % $winal;

        // The reminder should only be days
        $kin_qty = $difference;

        return [
            ['name' => 'Baktun', 'value' => $baktun_qty],
            ['name' => 'K\'atun', 'value' => $katun_qty],
            ['name' => 'Tun', 'value' => $tun_qty],
            ['name' => 'Winal', 'value' => $winal_qty],
            ['name' => 'K\'in', 'value' => $kin_qty]
        ];
    }

    /* ... */

Roman and Jewish dates

Not much to see here, apart from having to change the encoding to UTF-8 and getting the Julian Date of $this->date. This conversion is part of PHP core.

    /* ... */   

    private function jewishCalendar(){
        $julian_day = gregoriantojd(
            $this->date->format('n'),
            $this->date->format('j'),
            $this->date->format('Y')
        );

        return mb_convert_encoding(
            jdtojewish($julian_day, true),
            "UTF-8",
            "ISO-8859-8"
        );
    }

    /* ... */

The Roman date comes directly from the internet. What a beauty.

    /* ... */

    private function numberToRomanRepresentation($number) {
        $map = array('M' => 1000, 'CM' => 900, 'D' => 500, 'CD' => 400, 'C' => 100, 'XC' => 90, 'L' => 50, 'XL' => 40, 'X' => 10, 'IX' => 9, 'V' => 5, 'IV' => 4, 'I' => 1);
        $returnValue = '';
        while ($number > 0) {
            foreach ($map as $roman => $int) {
                if($number >= $int) {
                    $number -= $int;
                    $returnValue .= $roman;
                    break;
                }
            }
        }
        return $returnValue;
    }

    private function romanDate(){
        $day = $this->numberToRomanRepresentation($this->date->format('j'));
        $month = $this->numberToRomanRepresentation($this->date->format('n'));
        $year = $this->numberToRomanRepresentation($this->date->format('Y'));

        return $year . '-' . $month . '-' . $day;
    }

    /* ... */

2016

I hated 2016, some of the greatest people of our generation left us on that year. Here's a little commemoration with data from Wikipedia stored in a local JSON file.

I'm sorry, I cannot joke about this in any way.

    /* ... */

    private function diedOnThisDate(){
        $list = json_decode(file_get_contents('wikipedia.json'), true);

        return $list[$this->date->format('n') . '-' . $this->date->format('j')];
    }

    /* ... */

Equinox / Solstice

This title looks like a Myth & Roid song. Well, here's the code.

    /* ... */

    private function calculateEqSo(){
        // this is an approximation: these event could be off by one day-ish
        $ws = new DateTime($this->date->format('Y') . '-12-21 12:00');
        $se = new DateTime($this->date->format('Y') . '-03-21 12:00');
        $ss = new DateTime($this->date->format('Y') . '-06-21 12:00');
        $ae = new DateTime($this->date->format('Y') . '-09-21 12:00');

        // labels for the events
        $equi_solc = [
            'winter_solstice' => $ws,
            'spring_equinox' => $se,
            'summer_solstice' => $ss,
            'autumn_equinox' => $ae
        ];

        // HTML chars for the events
        $equi_solc_sings = [
            'winter_solstice'   => '♑',
            'spring_equinox'    => '♈',
            'summer_solstice'   => '♋',
            'autumn_equinox'    => '♎'
        ];

        // Creating a new array with differences in days between $this->date and the events
        $mapped_values = array_map(
            function($item){
                $diff = $item->diff($this->date);
                return ($diff->invert ? -1 : 1) * $diff->days;
            },
            $equi_solc
        );

        // find the smallest absolute value
        $closest = array_search(min(array_map('abs', $mapped_values)), array_map('abs', $mapped_values));

        // returning the closest event, its sign and the amount of days 
        // (not the absolute value though, we want to know if it is passed or in the future)
        return ['event' => $closest, 'days' => $mapped_values[$closest], 'sign' => $equi_solc_sings[$closest]];
    }

    /* ... */

World Population

It might be that there's a sucker (or a fool) born every minute and that wouldn't be bad at all since about 2 people and a half are born every second.

I am very much scared of statisticians and their ability to count half born people (or half people born?): they disgust me.

This is at best an approximation, but the best that we could do. I believe it doesn't need comments.

    /* ... */

    private function peopleBorn(){
        $start = new DateTime('2019-01-01 00:01');
        $in_2019 = 7714576923;
        $every_day = 81757598 / 365.25;
        $every_second = $every_day / 86400;

        $current_total = ceil(($this->date->getTimestamp() - $start->getTimestamp()) * $every_second + $in_2019);
        return ['every_second' => $every_second, 'current_total' => $current_total];
    }

    /* ... */ 

The Fast and the Curious

Here's the speed of fast objects in Kilometers per second. It's just an array, I won't tell you more about it.

    /* ... */ 

    private function speedOfStuff(){
        return [
            'hennessey_venom_f5' => 0.1345591667,
            'moon_revolution' => 1.0230556,
            'earth_revolution' => 29.78,
            'mercury_revolution' => 47.87,
            'halley_comet_speed' => 70.56,
            'juno_spacecraft' => 73.8,
            'speed_to_andromeda' => 110,
            'galactic_year' => 230,
            'solar_wind' => 450,
            'milky_way' => 552,
        ];
    }

    /* ... */ 

RETURN

It's time to transform the data into something usable, then init the class and output $this->result as a JSON response.

PS: $this->result is not vital, we could simply return the array, but in OOP is good practice to store it so that future method could get access to all the calculations.

    /* ... */

    public function getInfoAboutToday() {
        $this->result = [
            'time_on_earth' => [
                'date' => $this->date->format('Y-m-d'),
                'time' => $this->date->format('H:i:s')
            ],
            'time_on_mars' => $this->getCoordinatedMarsTime(),
            'moon_phase' => $this->getMoonPhase(),
            'day_of_the_year' => $this->date->format('z'),
            'months_names' => $this->monthsNames(),
            'mayan_calendar' => $this->mayanCalendar(),
            'jewish_calendar' => $this->jewishCalendar(),
            'roman_date' => $this->romanDate(),
            'rune_calendar' => $this->runesCalendar(),
            'closest_equi_solc' => $this->calculateEqSo(),
            'world_population' => $this->peopleBorn(),
            'speed_of_stuff' => $this->speedOfStuff(),
            'on_this_day' => $this->diedOnThisDate(),
        ];

        return $this->result;
    }
    /* TODAY @end */
}

$sc = new SuperClock();
header('Content-type: application/json');
echo json_encode($sc->getInfoAboutToday());

Time to close

The Math needed for this code was as entertaining as the research behind it, the rune stuff super cool and the mayan apocalyptic! But the most fun came with the client-side code were my love for CSS blossomed again in this funny project meant to sharpen my skills, experiment and learn all I could.

The code is hosted on github.

Part 2 will tell you all about that.