Facade

A dual world time zone analog watch face for most Garmin watches.

Analog watch face with support for dual world wide time zones, features include:

Note the red or green triangle on the outer edge of the dial. This triangle marks the hour hand position for the second time zone (or the first time zone if there is no second timezone selected). This triangle will be red to indicate AM and green to indicate PM. For example, the clock face on the left shows the local time of 10:10AM, the clock face in the middle shows the local time of 10:10PM, and the clock face on the right shows the local time of 11:33AM when it's 5:33PM in Central European Time.

The main timzone is always displayed by the analog clock in the center of the display with hour, minute, and second hands. If a second timezone is selected then the hour of that timezone is shown by the triangle on the edge of the face. If the second timezone is not Local then the 3-5 letter abbreviation for that timezone is shown at the bottom of the watch face. A triangle on the outer edge shows the hour of the second timezone. If the second timezone has a 0 minute offset then the main minute hand is correct for both the main and second timezones. If the second timezone has a non-0 minute offset then a yellow dot is displayed on the outer edge (you can interpret the second minute by observing the distance the second hour triange is between two hours but that is rather difficult, the yellow dot is a lot easier).

Daylight Savings Time (DST) is automatically detected for USA/CANADA/EU. The indicated time will be moved forward during DST and the timezone abbreviation displayed with be the one for DST. For other timezones there is a setting that can be set (using Connect IQ or Garmin Express, this is no available on the watch itself as it would make the watch settings too The Main DST and Second DST settings can be set to one of 3 different values:

Timezones can be selected from one of 327 countries.

The user can select which metric to display in the 4 different corners of the watch face.

You can obtain this watchface from the Connect IQ store. or by going to this direct link.

Facade is licensed under the GPL V3.0. The latest source tree is available for browsing here and tarballs for the latest and older versions are located at the following links:

Configuration

The watch face can be configured from the Connect IQ store, the Garmin Express app, or the watch itself. The most convenient way to configure things is from the on device settings (screenshots of these settings are pictured above) on the watch itself (these instructions are for the Venu, your mileage may vary for other models): Note that there is no way to cancel changes, the only way out of the configuration menu is to press the back button which will save the current settings.

Timezone configuration

Tap either the Timezone 1 and Timezone 2 buttons (which show the currently selected timezones as sub-labels) to bring up the timezone select menu. This menu shows 28 selections: Trying to navigate through 327 different timezones on a watch face is still cumbersome but, hopefully (especialy with the cache), it is still doable on this watch.

Seconds

Tapping on the Seconds button brings up the Second indicator page. From here you can customize how seconds are displayed on the watch:

Controls

Tapping this button brings up a page with buttons that control the operation of the watch face:

Metrics

Metrics display, at a glance, certain information maintained by the watch. Most of the metrics are just a number to represent the metric and an icon below that number to indicate which metric is being display. Each corner of the watch can be independently configured to show nothing or one of the 10 available metrics.

Moon Phase Indicator

The moon phase is displayed as one of 30 different icons going from new moon to full moon and back. The new moon and full moon icons are in white to distinguish them from all the other phases. Waxing phases are in green while waning phases are in tan.

Moon phase approximation

(Basically an apology for why Facade is only approximately correct about moon phases.)

There are two issues that make identifying the correct moon phase extremely hard:

This makes computing the precise moon phase extremely difficult and way beyond the capabilities of Facade.

Having said that, Facade attempts to be fairly accurate. It calculates the current lunar day by comparing the current date to the reference date of the new moon on January 6, 2000 (I wanted to use the reference date of Januaray 1, 1900 but the Garmin date/time routines don't like dates before the Unix epoch of January 1, 1972), modulo 29.5(30588853). This calculation seems to be fairly accurate (the same calculation is certainly correct for the 44,206 days from the new moon on January 1, 1900 to the new moon on January 12, 2021). This results in a lunar day of 1 - 29 (actually, some months it turns out to be 1-30 because of roundoff with the lunar period).

The lunar day is then converted into one of 30 different icons (representing the moon waxing from new moon to full moon and then waning back to a new moon). Note that the icons for the day of the new and full moon are obvious since they are in white. Conversely, the icons for the gibbous moon a few days before and after the full moon look very similar to a full moon, this is where the tan and green colors are handy.

Building from soure

Building from source is just a matter of installing the source tree in Monkey C Visual Code Studio and then do a build, that should work just fine.

The problem comes in if you need to change the timezone data like adding a new timezone or deleting one. By definition the Garmin architecture requires at least 2 linked source files, the settings.xml file that shows the timezones presented to the user in the settings app and the actual timezone data that is encoded in the Tz.mc file.2

Basic design

The basic design is to do the obvious, the settings.xml entries have an index and then that index is used to select the appropriate entry from a table in the Tz.mc file.

There are 2 problems with this basic design:

The second problem was exacerbated by the original design which was to encode the timezone info into an array where each element of the array had 5 items to identify the timezone - minute offset, standard time abbreviation, daylinght savings abbreviation, automatic timezone algorithm, and full name, e.g.

var TzData = [
    [ +(0*60+0), "", "", -1, "Local" ],
    [ -(5*60+0), "EST", "EDT", 0, "USA, East" ],
    .
    .
    .
];
This design, which was obvious and made things simple at run time, blew the memory available on devices like a Forerunner 55.

Chunk design

The original solution to the memory problem was solved by breaking up the timezone data into 25 entry chunks and then storing each chunk is a separate MC file, the TzData0.mc to TzData13.mc files. This way the code only has to have in memory 25 timezone entries at a time rather than 327. The code in Tz.mc did the translation from one of the 327 timezone indices to an appropriate index in the appropriate TzData file. With this design the code barely fits in a Forerunner 55 (with only 4K of memory to spare).

String design

Unfortunately, expanding the moon phase from 8 to 30 icons expanded the memory and the code no longer fit on a Forerunner 55 (and no amount of futzing with the TzData chunk size made it fit). A new design is required.

Rather than storing separate data items for each timezone entry the code now just stores a long string for each timezone, where each data item (fullname, minute offset, standard time abbreviation, daylinght savings abbreviation, and automatic timezone algorithm) is separated by a semicolon within that string, e.g.

var TzData = [
    "Local;0;;;-1",
    "USA, East;-300;EST;EDT;0",
    .
    .
    .
];
The downside to this design is that we now have to parse the appropriate string at runtime but the upside is a consderable savings in memory. This design saved approximately 20K of memory and the code with 30 moon icons now fits on a Forerunner 55 with plenty of room to spare - the core/speed 3 trace off was definitely worth it.

Chunked string design

Adding support for on watch timezone settings consumed more memory 4and, you guessed it, blew the memory on a Forerunner 55, yet another design required. Fortunately, the chunked design comes to the rescue. The current design now puts all of the timezone data strings into 27 different files indexed by the first letter of the country name (26 letters in alphabet plus we put Local and USA timezones in the 0th entry). With this design we only need at most 40 timezone data strings in memory at one time and now the program now fits on a Forerunner 55.

Helper program

Although the new design obviates the need for multiple TzData files there is still a problem that we have to keep multiple related files in sync with each other - the settings.xml used to select timezones, the TzOffsets.mc used to identify which country names begin with the same letter, and Tz?.mc which contains all of the actual timezone info. The solution is to maintain a canonical timezone data base file, support/country.txt and use the Pearl script support/tz.pl to extrace everything we need from the data file. Running the command tz.pl support/country.txt will generate a settings.xml file, TzOffsets.mc file, and all of the Tz?.mc files. After generating these files you have to manually (sorry about that, there doesn't seem to be a way to do includes in settings files) add the generated lines in settings.xml to the appropriate places in the actual resources/settings/settings.xml file. Then copy TzOffsets.mc into source/TzOffsets.mc, copy all of the Tz?.mc files into source/TzData and you should be ready to build.
1. The backing store can exist in two different areas - system memory or a part of the users data space. If your watch provides system backing store then it will work just fine. If your watch only provides user space backing store then I can pretty much guarantee that enabling this option will blow memory and crash your watch (check out the Building from source for more details on the limited memory available in Garmin watches). Since I can't test this watch face on every single Garmin watch it is possible that there might be performance glitches that could be fixed by using the backing store on your specific model. Note that it is safe try to enable backing store even on watches that don't have enough memory to use it. To use the backing store the code temporarity disables this option, allocates the backing store, if the allocation works then it re-enables this option and continues. If the allocation fails then this option is disabled and restarting the watch will run the watch face without backing store. I have to do it this way because there's no way to know before hand if there's enough memory for the backing store and there's no way to catch/recover from the memory fault it allocatiing the backing store fails.
2. What I really wanted to do was to define a unique table of timezone data (one entry per unique timezone info rather than the current design of one entry per country) and then have the entries in the settings.xml file index into the appropriate entry in that table. Since there are multiple countries that share the same timezone this would reduce the table size needed in the code by about a factor of 2. Unfortunately, that design is not possible because arrays in the settings.xml file have to have unique indices.
3. For the youngsters reading this we used to refer to memory as core (Google memory core to see why) back in the dark ages of computers. Yes, I'm old enough to remember those days.
4. Can I just have a minor rant here that, in the 21st century, on a device with 32M of memory (I'm talking to you Forerunner 55) I still have to fight with a system the only lets me have 96K of data space. And on a Venu 3 with 8G of memory I only have 128K of data to work with.