Skip to content

Commit ac5bf8c

Browse files
Merge PR #470 - implement automatic DST toggling
Implements logic to automatically offset daylight saving time settings when calculating timezone offsets. This should make the DST functions work automatically with no need for user input in most cases. Reviewed-by: Matheus Afonso Martins Moreira <[email protected]> GitHub-Pull-Request: #470
2 parents 5a8a49a + 5ae88e4 commit ac5bf8c

32 files changed

+295
-165
lines changed

movement/movement.c

+99-88
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ of debounce time.
4444
#include "filesystem.h"
4545
#include "movement.h"
4646
#include "shell.h"
47+
#include "watch_utility.h"
4748

4849
#ifndef MOVEMENT_FIRMWARE
4950
#include "movement_config.h"
@@ -130,6 +131,11 @@ of debounce time.
130131
#define MOVEMENT_DEFAULT_BIRTHDATE_DAY 0
131132
#endif
132133

134+
// Default to having DST get set
135+
#ifndef MOVEMENT_DEFAULT_DST_ACTIVE
136+
#define MOVEMENT_DEFAULT_DST_ACTIVE true
137+
#endif
138+
133139
#if __EMSCRIPTEN__
134140
#include <emscripten.h>
135141
#endif
@@ -141,7 +147,8 @@ const int32_t movement_le_inactivity_deadlines[8] = {INT_MAX, 600, 3600, 7200, 2
141147
const int16_t movement_timeout_inactivity_deadlines[4] = {60, 120, 300, 1800};
142148
movement_event_t event;
143149

144-
const int16_t movement_timezone_offsets[] = {
150+
#define NUM_TIME_ZONES 41
151+
const int16_t movement_timezone_offsets[NUM_TIME_ZONES] = {
145152
0, // 0 : 0:00:00 (UTC)
146153
60, // 1 : 1:00:00 (Central European Time)
147154
120, // 2 : 2:00:00 (South African Standard Time)
@@ -198,94 +205,50 @@ const int16_t movement_timezone_offsets[] = {
198205
* having to separately change the hour and timezone info
199206
* in the time set face.
200207
*/
201-
const uint8_t movement_dst_jump_table[] = {
202-
1, // 0 UTC + 1 = CET
203-
2, // 1 CET + 1 = SAST
204-
3, // 2 SAST + 1 = AST
205-
5, // 3 AST + 1 = GST
206-
6, // 4 IST + 1 = AT
207-
7, // 5 GST + 1 = PST
208-
8, // 6 AT + 1 = IST
209-
10, // 7 PST + 1 = KT
210-
11, // 8 IST + 1 = MT
211-
9, // 9 Nepal has no equivalent DST timezone, but they don't observe DST anyway
212-
12, // 10 KT + 1 = TST
213-
11, // 11 Myanmar has no equivalent DST timezone, but they don't observe DST anyway
214-
13, // 12 TST + 1 = CST
215-
15, // 13 CST + 1 = JST
216-
14, // 14 ACWST has no equivalent DST timezone, but they don't observe DST anyway
217-
17, // 15 JST + 1 = AEST
218-
18, // 16 ACST + 1 = LHST
219-
19, // 17 AEST + 1 = SIT
220-
18, // 18 LHST has no equivalent DST timezone, but they don't observe DST anyway
221-
20, // 19 SIT + 1 = NZST
222-
22, // 20 NZST + 1 = TT
223-
23, // 21 CST + 1 = CDT
224-
24, // 22 TT + 1 = LIT
225-
23, // 23 CDT is already a daylight timezone
226-
24, // 24 LIT has no equivalent DST timezone, but they don't observe DST anyway
227-
26, // 25 BIT + 1 = NT
228-
27, // 26 NT + 1 = HAST
229-
29, // 27 HAST + 1 = AST
230-
28, // 28 MIT has no equivalent DST timezone, but they don't observe DST anyway
231-
30, // 29 AST + 1 = PST
232-
31, // 30 PST + 1 = MST
233-
32, // 31 MST + 1 = CST
234-
33, // 32 CST + 1 = EST
235-
35, // 33 EST + 1 = AST
236-
36, // 34 VST + 1 = NST
237-
37, // 35 AST + 1 = BT
238-
38, // 36 NST + 1 = NDT
239-
39, // 37 BT + 1 = 39
240-
38, // 38 NDT is already a daylight timezone
241-
40, // 39 FNT + 1 = AST
208+
const int16_t movement_timezone_dst_offsets[NUM_TIME_ZONES] = {
209+
60, // 0 UTC + 1 = CET
210+
120, // 1 CET + 1 = SAST
211+
189, // 2 SAST + 1 = AST
212+
240, // 3 AST + 1 = GST
213+
270, // 4 IST + 1 = AT
214+
300, // 5 GST + 1 = PST
215+
330, // 6 AT + 1 = IST
216+
360, // 7 PST + 1 = KT
217+
390, // 8 IST + 1 = MT
218+
345, // 9 Nepal has no equivalent DST timezone, but they don't observe DST anyway
219+
420, // 10 KT + 1 = TST
220+
390, // 11 Myanmar has no equivalent DST timezone, but they don't observe DST anyway
221+
480, // 12 TST + 1 = CST
222+
540, // 13 CST + 1 = JST
223+
525, // 14 ACWST has no equivalent DST timezone, but they don't observe DST anyway
224+
600, // 15 JST + 1 = AEST
225+
630, // 16 ACST + 1 = LHST
226+
660, // 17 AEST + 1 = SIT
227+
630, // 18 LHST has no equivalent DST timezone, but they don't observe DST anyway
228+
720, // 19 SIT + 1 = NZST
229+
780, // 20 NZST + 1 = TT
230+
825, // 21 CST + 1 = CDT
231+
840, // 22 TT + 1 = LIT
232+
825, // 23 CDT is already a daylight timezone
233+
840, // 24 LIT has no equivalent DST timezone, but they don't observe DST anyway
234+
-660, // 25 BIT + 1 = NT
235+
-600, // 26 NT + 1 = HAST
236+
-540, // 27 HAST + 1 = AST
237+
-570, // 28 MIT has no equivalent DST timezone, but they don't observe DST anyway
238+
-480, // 29 AST + 1 = PST
239+
-420, // 30 PST + 1 = MST
240+
-360, // 31 MST + 1 = CST
241+
-300, // 32 CST + 1 = EST
242+
-240, // 33 EST + 1 = AST
243+
-210, // 34 VST + 1 = NST
244+
-180, // 35 AST + 1 = BT
245+
-150, // 36 NST + 1 = NDT
246+
-120, // 37 BT + 1 = 39
247+
-150, // 38 NDT is already a daylight timezone
248+
-60, // 39 FNT + 1 = AST
242249
0 // 40 AST + 1 = UTC
243250
};
244251

245-
const uint8_t movement_dst_inverse_jump_table[] = {
246-
40, // 0
247-
0, // 1
248-
1, // 2
249-
2, // 3
250-
4, // 4
251-
3, // 5
252-
4, // 6
253-
5, // 7
254-
6, // 8
255-
9, // 9
256-
7, // 10
257-
8, // 11
258-
10, // 12
259-
12, // 13
260-
14, // 14
261-
13, // 15
262-
16, // 16
263-
15, // 17
264-
16, // 18
265-
17, // 19
266-
19, // 20
267-
21, // 21
268-
20, // 22
269-
21, // 23
270-
24, // 24
271-
25, // 25
272-
25, // 26
273-
26, // 27
274-
28, // 28
275-
27, // 29
276-
29, // 30
277-
30, // 31
278-
31, // 32
279-
32, // 33
280-
34, // 34
281-
33, // 35
282-
34, // 36
283-
35, // 37
284-
36, // 38
285-
37, // 39
286-
39 // 40
287-
};
288-
289252
const char movement_valid_position_0_chars[] = " AaBbCcDdEeFGgHhIiJKLMNnOoPQrSTtUuWXYZ-='+\\/0123456789";
290253
const char movement_valid_position_1_chars[] = " ABCDEFHlJLNORTtUX-='01378";
291254

@@ -323,6 +286,31 @@ static inline void _movement_disable_fast_tick_if_possible(void) {
323286
}
324287
}
325288

289+
static bool _check_and_act_on_daylight_savings(void) {
290+
if (!movement_state.settings.bit.dst_active) return false;
291+
watch_date_time date_time = watch_rtc_get_date_time();
292+
// No need for all of the unix time calculations for times not at the beginning or end of the hour
293+
if (date_time.unit.minute > 1 && date_time.unit.minute < 59) return false;
294+
uint8_t dst_result = get_dst_status(date_time);
295+
bool dst_skip_rolling_back = get_dst_skip_rolling_back();
296+
297+
if (dst_skip_rolling_back && (dst_result == DST_ENDED)) {
298+
clear_dst_skip_rolling_back();
299+
}
300+
else if (dst_result == DST_ENDING && !dst_skip_rolling_back) {
301+
date_time.unit.hour = (date_time.unit.hour + 24 - 1) % 24;
302+
watch_rtc_set_date_time(date_time);
303+
set_dst_skip_rolling_back();
304+
return true;
305+
}
306+
else if (dst_result == DST_STARTING) {
307+
date_time.unit.hour = (date_time.unit.hour + 1) % 24;
308+
watch_rtc_set_date_time(date_time);
309+
return true;
310+
}
311+
return false;
312+
}
313+
326314
static void _movement_handle_background_tasks(void) {
327315
for(uint8_t i = 0; i < MOVEMENT_NUM_FACES; i++) {
328316
// For each face, if the watch face wants a background task...
@@ -332,6 +320,7 @@ static void _movement_handle_background_tasks(void) {
332320
watch_faces[i].loop(background_event, &movement_state.settings, watch_face_contexts[i]);
333321
}
334322
}
323+
_check_and_act_on_daylight_savings();
335324
movement_state.needs_background_tasks_handled = false;
336325
}
337326

@@ -529,6 +518,12 @@ uint8_t movement_claim_backup_register(void) {
529518
return movement_state.next_available_backup_register++;
530519
}
531520

521+
int16_t get_timezone_offset(uint8_t timezone_idx, watch_date_time date_time) {
522+
if (movement_state.settings.bit.dst_active && dst_occurring(date_time))
523+
return movement_timezone_dst_offsets[timezone_idx];
524+
return movement_timezone_offsets[timezone_idx];
525+
}
526+
532527
void app_init(void) {
533528
#if defined(NO_FREQCORR)
534529
watch_rtc_freqcorr_write(0, 0);
@@ -551,6 +546,20 @@ void app_init(void) {
551546
movement_state.birthdate.bit.year = MOVEMENT_DEFAULT_BIRTHDATE_YEAR;
552547
movement_state.birthdate.bit.month = MOVEMENT_DEFAULT_BIRTHDATE_MONTH;
553548
movement_state.birthdate.bit.day = MOVEMENT_DEFAULT_BIRTHDATE_DAY;
549+
movement_state.settings.bit.dst_active = MOVEMENT_DEFAULT_DST_ACTIVE;
550+
551+
#ifdef MAKEFILE_TIMEZONE
552+
timezone_offsets = dst_occurring(watch_rtc_get_date_time()) ? movement_timezone_dst_offsets : movement_timezone_offsets;
553+
for (int i = 0; i < NUM_TIME_ZONES; i++) {
554+
if (timezone_offsets[i] == MAKEFILE_TIMEZONE) {
555+
movement_state.settings.bit.time_zone = i;
556+
break;
557+
}
558+
}
559+
#else
560+
movement_state.settings.bit.time_zone = 35; // Atlantic Time as default
561+
#endif
562+
554563
movement_state.light_ticks = -1;
555564
movement_state.alarm_ticks = -1;
556565
movement_state.next_available_backup_register = 4;
@@ -559,11 +568,13 @@ void app_init(void) {
559568
filesystem_init();
560569

561570
#if __EMSCRIPTEN__
571+
const int16_t* timezone_offsets;
562572
int32_t time_zone_offset = EM_ASM_INT({
563573
return -new Date().getTimezoneOffset();
564574
});
565-
for (int i = 0, count = sizeof(movement_timezone_offsets) / sizeof(movement_timezone_offsets[0]); i < count; i++) {
566-
if (movement_timezone_offsets[i] == time_zone_offset) {
575+
timezone_offsets = dst_occurring(watch_rtc_get_date_time()) ? movement_timezone_dst_offsets : movement_timezone_offsets;
576+
for (int i = 0; i < NUM_TIME_ZONES; i++) {
577+
if (timezone_offsets[i] == time_zone_offset) {
567578
movement_state.settings.bit.time_zone = i;
568579
break;
569580
}

movement/movement.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,7 @@ typedef struct {
130130
} movement_event_t;
131131

132132
extern const int16_t movement_timezone_offsets[];
133-
extern const uint8_t movement_dst_jump_table[];
134-
extern const uint8_t movement_dst_inverse_jump_table[];
133+
extern const int16_t movement_timezone_dst_offsets[];
135134
extern const char movement_valid_position_0_chars[];
136135
extern const char movement_valid_position_1_chars[];
137136

@@ -321,5 +320,6 @@ void movement_play_alarm(void);
321320
void movement_play_alarm_beeps(uint8_t rounds, BuzzerNote alarm_note);
322321

323322
uint8_t movement_claim_backup_register(void);
323+
int16_t get_timezone_offset(uint8_t timezone_idx, watch_date_time date_time);
324324

325325
#endif // MOVEMENT_H_

movement/movement_config.h

+7
Original file line numberDiff line numberDiff line change
@@ -111,4 +111,11 @@ const watch_face_t watch_faces[] = {
111111
#define MOVEMENT_DEFAULT_BIRTHDATE_MONTH 0
112112
#define MOVEMENT_DEFAULT_BIRTHDATE_DAY 0
113113

114+
/* Set if using DST
115+
* Valid values are:
116+
* false: Don't allow the watch to use DST
117+
* true: Allow the watch to use DST
118+
*/
119+
#define MOVEMENT_DEFAULT_DST_ACTIVE true
120+
114121
#endif // MOVEMENT_CONFIG_H_

movement/watch_faces/clock/beats_face.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ bool beats_face_loop(movement_event_t event, movement_settings_t *settings, void
6161
case EVENT_ACTIVATE:
6262
case EVENT_TICK:
6363
date_time = watch_rtc_get_date_time();
64-
centibeats = clock2beats(date_time.unit.hour, date_time.unit.minute, date_time.unit.second, event.subsecond, movement_timezone_offsets[settings->bit.time_zone]);
64+
centibeats = clock2beats(date_time.unit.hour, date_time.unit.minute, date_time.unit.second, event.subsecond, get_timezone_offset(settings->bit.time_zone, date_time));
6565
if (centibeats == state->last_centibeat_displayed) {
6666
// we missed this update, try again next subsecond
6767
state->next_subsecond_update = (event.subsecond + 1) % BEAT_REFRESH_FREQUENCY;
@@ -76,7 +76,7 @@ bool beats_face_loop(movement_event_t event, movement_settings_t *settings, void
7676
case EVENT_LOW_ENERGY_UPDATE:
7777
if (!watch_tick_animation_is_running()) watch_start_tick_animation(432);
7878
date_time = watch_rtc_get_date_time();
79-
centibeats = clock2beats(date_time.unit.hour, date_time.unit.minute, date_time.unit.second, event.subsecond, movement_timezone_offsets[settings->bit.time_zone]);
79+
centibeats = clock2beats(date_time.unit.hour, date_time.unit.minute, date_time.unit.second, event.subsecond, get_timezone_offset(settings->bit.time_zone, date_time));
8080
sprintf(buf, "bt %4lu ", centibeats / 100);
8181

8282
watch_display_string(buf, 0);

movement/watch_faces/clock/day_night_percentage_face.c

+3-2
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ void day_night_percentage_face_setup(movement_settings_t *settings, uint8_t watc
6262
if (*context_ptr == NULL) {
6363
*context_ptr = malloc(sizeof(day_night_percentage_state_t));
6464
day_night_percentage_state_t *state = (day_night_percentage_state_t *)*context_ptr;
65-
watch_date_time utc_now = watch_utility_date_time_convert_zone(watch_rtc_get_date_time(), movement_timezone_offsets[settings->bit.time_zone] * 60, 0);
65+
watch_date_time date_time = watch_rtc_get_date_time();
66+
watch_date_time utc_now = watch_utility_date_time_convert_zone(date_time, get_timezone_offset(settings->bit.time_zone, date_time) * 60, 0);
6667
recalculate(utc_now, state);
6768
}
6869
}
@@ -77,7 +78,7 @@ bool day_night_percentage_face_loop(movement_event_t event, movement_settings_t
7778

7879
char buf[12];
7980
watch_date_time date_time = watch_rtc_get_date_time();
80-
watch_date_time utc_now = watch_utility_date_time_convert_zone(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60, 0);
81+
watch_date_time utc_now = watch_utility_date_time_convert_zone(date_time, get_timezone_offset(settings->bit.time_zone, date_time) * 60, 0);
8182

8283
switch (event.event_type) {
8384
case EVENT_ACTIVATE:

movement/watch_faces/clock/mars_time_face.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ static void _h_to_hms(mars_clock_hms_t *date_time, double h) {
7070
static void _update(movement_settings_t *settings, mars_time_state_t *state) {
7171
char buf[11];
7272
watch_date_time date_time = watch_rtc_get_date_time();
73-
uint32_t now = watch_utility_date_time_to_unix_time(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60);
73+
uint32_t now = watch_utility_date_time_to_unix_time(date_time, get_timezone_offset(settings->bit.time_zone, date_time) * 60);
7474
// TODO: I'm skipping over some steps here.
7575
// https://www.giss.nasa.gov/tools/mars24/help/algorithm.html
7676
double jdut = 2440587.5 + ((double)now / 86400.0);

movement/watch_faces/clock/world_clock2_face.c

+4-3
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ void world_clock2_face_activate(movement_settings_t *settings, void *context)
154154
movement_request_tick_frequency(4);
155155
break;
156156
}
157+
state->tz = get_timezone_offset(settings->bit.time_zone, watch_rtc_get_date_time());
157158
refresh_face = true;
158159
}
159160

@@ -183,8 +184,8 @@ static bool mode_display(movement_event_t event, movement_settings_t *settings,
183184

184185
/* Determine current time at time zone and store date/time */
185186
date_time = watch_rtc_get_date_time();
186-
timestamp = watch_utility_date_time_to_unix_time(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60);
187-
date_time = watch_utility_date_time_from_unix_time(timestamp, movement_timezone_offsets[state->current_zone] * 60);
187+
timestamp = watch_utility_date_time_to_unix_time(date_time, state->tz * 60);
188+
date_time = watch_utility_date_time_from_unix_time(timestamp, state->tz * 60);
188189
previous_date_time = state->previous_date_time;
189190
state->previous_date_time = date_time.reg;
190191

@@ -289,7 +290,7 @@ static bool mode_settings(movement_event_t event, movement_settings_t *settings,
289290
watch_clear_indicator(WATCH_INDICATOR_PM);
290291
refresh_face = false;
291292
}
292-
result = div(movement_timezone_offsets[state->current_zone], 60);
293+
result = div(state->tz, 60);
293294
hours = result.quot;
294295
minutes = result.rem;
295296

movement/watch_faces/clock/world_clock2_face.h

+1
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ typedef struct {
104104
world_clock2_mode_t current_mode;
105105
uint8_t current_zone;
106106
uint32_t previous_date_time;
107+
int16_t tz;
107108
} world_clock2_state_t;
108109

109110
void world_clock2_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void **context_ptr);

movement/watch_faces/clock/world_clock_face.c

+6-5
Original file line numberDiff line numberDiff line change
@@ -60,15 +60,16 @@ static bool world_clock_face_do_display_mode(movement_event_t event, movement_se
6060
watch_date_time date_time;
6161
switch (event.event_type) {
6262
case EVENT_ACTIVATE:
63-
if (settings->bit.clock_mode_24h && !settings->bit.clock_24h_leading_zero) watch_set_indicator(WATCH_INDICATOR_24H);
63+
state->tz = get_timezone_offset(settings->bit.time_zone, watch_rtc_get_date_time());
64+
if (settings->bit.clock_mode_24h) watch_set_indicator(WATCH_INDICATOR_24H);
6465
watch_set_colon();
6566
state->previous_date_time = 0xFFFFFFFF;
6667
// fall through
6768
case EVENT_TICK:
6869
case EVENT_LOW_ENERGY_UPDATE:
6970
date_time = watch_rtc_get_date_time();
70-
timestamp = watch_utility_date_time_to_unix_time(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60);
71-
date_time = watch_utility_date_time_from_unix_time(timestamp, movement_timezone_offsets[state->settings.bit.timezone_index] * 60);
71+
timestamp = watch_utility_date_time_to_unix_time(date_time, state->tz * 60);
72+
date_time = watch_utility_date_time_from_unix_time(timestamp, state->tz * 60);
7273
previous_date_time = state->previous_date_time;
7374
state->previous_date_time = date_time.reg;
7475

@@ -176,8 +177,8 @@ static bool _world_clock_face_do_settings_mode(movement_event_t event, movement_
176177
sprintf(buf, "%c%c %3d%02d ",
177178
movement_valid_position_0_chars[state->settings.bit.char_0],
178179
movement_valid_position_1_chars[state->settings.bit.char_1],
179-
(int8_t) (movement_timezone_offsets[state->settings.bit.timezone_index] / 60),
180-
(int8_t) (movement_timezone_offsets[state->settings.bit.timezone_index] % 60) * (movement_timezone_offsets[state->settings.bit.timezone_index] < 0 ? -1 : 1));
180+
(int8_t) (state->tz / 60),
181+
(int8_t) (state->tz % 60) * (state->tz < 0 ? -1 : 1));
181182
watch_set_colon();
182183
watch_clear_indicator(WATCH_INDICATOR_PM);
183184

0 commit comments

Comments
 (0)