]> git.earman.xyz Git - sensor-watch.git/commitdiff
cache DST offsets so we're not constantly converting time zones
authorjoeycastillo <joeycastillo@utexas.edu>
Sat, 5 Oct 2024 13:33:12 +0000 (09:33 -0400)
committerjoeycastillo <joeycastillo@utexas.edu>
Sat, 5 Oct 2024 14:36:58 +0000 (10:36 -0400)
movement.c

index 8c160505e03a3dc842536bf5c5f49ffca8a17eab..b139b021eaae24ef9c23ae58f5397037e4bc4d97 100644 (file)
@@ -40,6 +40,7 @@
 #include "filesystem.h"
 #include "shell.h"
 #include "utz.h"
+#include "zones.h"
 
 #include "movement_config.h"
 
@@ -56,6 +57,9 @@ const int32_t movement_le_inactivity_deadlines[8] = {INT_MAX, 600, 3600, 7200, 2
 const int16_t movement_timeout_inactivity_deadlines[4] = {60, 120, 300, 1800};
 movement_event_t event;
 
+int8_t _movement_dst_offset_cache[NUM_ZONE_NAMES] = {0};
+#define TIMEZONE_DOES_NOT_OBSERVE (-127)
+
 const char movement_valid_position_0_chars[] = " AaBbCcDdEeFGgHhIiJKLMNnOoPQrSTtUuWXYZ-='+\\/0123456789";
 const char movement_valid_position_1_chars[] = " ABCDEFHlJLNORTtUX-='01378";
 
@@ -84,6 +88,39 @@ static udatetime_t _movement_convert_date_time_to_udate(watch_date_time date_tim
     };
 }
 
+static bool _movement_update_dst_offset_cache(void) {
+    uzone_t local_zone;
+    udatetime_t udate_time;
+    bool dst_changed = false;
+    watch_date_time system_date_time = watch_rtc_get_date_time();
+
+    printf("current zone: %d\n", movement_state.settings.bit.time_zone);
+
+    for (uint8_t i = 0; i < NUM_ZONE_NAMES; i++) {
+        unpack_zone(&zone_defns[i], "", &local_zone);
+        watch_date_time date_time = watch_utility_date_time_convert_zone(system_date_time, 0, local_zone.offset.hours * 3600 + local_zone.offset.minutes * 60);
+
+        if (!!local_zone.rules_len) {
+            // if local zone has DST rules, we need to see if DST applies.
+            udate_time = _movement_convert_date_time_to_udate(date_time);
+            uoffset_t offset;
+            get_current_offset(&local_zone, &udate_time, &offset);
+            int8_t new_offset = (offset.hours * 60 + offset.minutes) / 15;
+            if (_movement_dst_offset_cache[i] != new_offset) {
+                _movement_dst_offset_cache[i] = new_offset;
+                dst_changed = true;
+            }
+            printf("zone %d: %d\n", i, new_offset);
+        } else {
+            // otherwise set the cache to a constant value that indicates no DST check needs to be performed.
+            _movement_dst_offset_cache[i] = TIMEZONE_DOES_NOT_OBSERVE;
+            printf("zone %d: %d\n", i, TIMEZONE_DOES_NOT_OBSERVE);
+        }
+    }
+
+    return dst_changed;
+}
+
 static inline void _movement_reset_inactivity_countdown(void) {
     movement_state.le_mode_ticks = movement_le_inactivity_deadlines[movement_state.settings.bit.le_interval];
     movement_state.timeout_ticks = movement_timeout_inactivity_deadlines[movement_state.settings.bit.to_interval];
@@ -107,6 +144,13 @@ static inline void _movement_disable_fast_tick_if_possible(void) {
 }
 
 static void _movement_handle_top_of_minute(void) {
+    watch_date_time date_time = watch_rtc_get_date_time();
+
+    // update the DST offset cache every 15 minutes, since someplace in the world could change.
+    if (date_time.unit.minute % 15 == 0) {
+        _movement_update_dst_offset_cache();
+    }
+
     for(uint8_t i = 0; i < MOVEMENT_NUM_FACES; i++) {
         // For each face that offers an advisory...
         if (watch_faces[i].advise != NULL) {
@@ -327,25 +371,15 @@ uint8_t movement_claim_backup_register(void) {
 }
 
 int32_t movement_get_current_timezone_offset_for_zone(uint8_t zone_index) {
-    watch_date_time date_time = watch_rtc_get_date_time();
-    udatetime_t udate_time = _movement_convert_date_time_to_udate(watch_rtc_get_date_time());
-    uzone_t local_zone;
-
-    // first unpack the zone to get the baseline offset
-    unpack_zone(&zone_defns[zone_index], "", &local_zone);
+    int8_t cached_dst_offset = _movement_dst_offset_cache[zone_index];
 
-    // offset the UTC time by that amount to get the local standard time
-    date_time = watch_utility_date_time_convert_zone(date_time, 0, local_zone.offset.hours * 3600 + local_zone.offset.minutes * 60);
-
-    // if local zone has DST rules, we need to see if DST applies.
-    if (local_zone.rules_len) {
-        udate_time = _movement_convert_date_time_to_udate(date_time);
-        uoffset_t offset;
-        get_current_offset(&local_zone, &udate_time, &offset);
-        return offset.hours * 3600 + offset.minutes * 60;
+    if (cached_dst_offset == TIMEZONE_DOES_NOT_OBSERVE) {
+        // if time zone doesn't observe DST, we can just return the standard time offset from the zone definition.
+        return (int32_t)zone_defns[zone_index].offset_inc_minutes * OFFSET_INCREMENT * 60;
+    } else {
+        // otherwise, we've precalculated the offset for this zone and can return it.
+        return (int32_t)cached_dst_offset * OFFSET_INCREMENT * 60;
     }
-
-    return local_zone.offset.hours * 3600 + local_zone.offset.minutes * 60;
 }
 
 int32_t movement_get_current_timezone_offset(void) {
@@ -378,6 +412,11 @@ void movement_set_local_date_time(watch_date_time date_time) {
     int32_t current_offset = movement_get_current_timezone_offset();
     watch_date_time utc_date_time = watch_utility_date_time_convert_zone(date_time, current_offset, 0);
     watch_rtc_set_date_time(utc_date_time);
+
+    // this may seem wasteful, but if the user's local time is in a zone that observes DST,
+    // they may have just crossed a DST boundary, which means the next call to this function
+    // could require a different offset to force local time back to UTC. Quelle horreur!
+    _movement_update_dst_offset_cache();
 }
 
 bool movement_button_should_sound(void) {
@@ -550,6 +589,9 @@ void app_setup(void) {
             is_first_launch = false;
         }
 
+        // populate the DST offset cache
+        _movement_update_dst_offset_cache();
+
         // set up the 1 minute alarm (for background tasks and low power updates)
         watch_date_time alarm_time;
         alarm_time.reg = 0;