Skip to content

Commit 6111780

Browse files
committed
- Set system timezone instead of changing offset logic.
1 parent 92c25e7 commit 6111780

File tree

3 files changed

+73
-33
lines changed

3 files changed

+73
-33
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ rustbus = "0.19.3"
3131
sha256 = "1.5.0"
3232
sysinfo = "0.35.2"
3333
tzf-rs = { version = "1.1.3", default-features = false }
34+
localzone = { version = "0.3.1", features = ["auto_validation"] }
3435

3536
[target.'cfg(target_env = "musl")'.dependencies]
3637
mimalloc = "0.1.43"

src/device_config.rs

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,11 @@ use core::fmt;
22
use std::collections::HashMap;
33
// Read camera config file
44
use crate::detection_mask::DetectionMask;
5+
use crate::set_system_timezone;
56
use byteorder::{LittleEndian, WriteBytesExt};
67
use chrono::{
7-
DateTime, Duration, FixedOffset, Local, NaiveDate, NaiveDateTime, NaiveTime, Offset, TimeZone,
8-
Utc,
8+
DateTime, Duration, FixedOffset, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc,
99
};
10-
use chrono_tz::Tz;
1110
use log::{error, info, warn};
1211
use louvre::triangulate;
1312
use notify::event::{AccessKind, AccessMode};
@@ -24,9 +23,9 @@ use std::sync::mpsc::{Receiver, Sender, TryRecvError};
2423
use std::{fs, process};
2524
use sun_times::sun_times;
2625
use toml::Value;
27-
use toml::value::Offset as TomlDateTimeOffset;
26+
use toml::value::Offset;
2827

29-
static TZ_FINDER: LazyLock<tzf_rs::DefaultFinder> = LazyLock::new(tzf_rs::DefaultFinder::new);
28+
pub static TZ_FINDER: LazyLock<tzf_rs::DefaultFinder> = LazyLock::new(tzf_rs::DefaultFinder::new);
3029

3130
fn default_constant_recorder() -> bool {
3231
false
@@ -289,8 +288,8 @@ where
289288
let time = date_time.time.expect("should have time");
290289
let offset = date_time.offset.expect("should have offset");
291290
let offset_minutes = match offset {
292-
TomlDateTimeOffset::Z => 0,
293-
TomlDateTimeOffset::Custom { minutes } => minutes,
291+
Offset::Z => 0,
292+
Offset::Custom { minutes } => minutes,
294293
} as i32;
295294
let fixed_offset = if offset_minutes < 0 {
296295
FixedOffset::east_opt(offset_minutes * 60)
@@ -342,13 +341,12 @@ struct HourMin {
342341
min: u8,
343342
}
344343

345-
fn timezone_offset_seconds(lat: f32, lng: f32) -> i32 {
346-
let tz_from_lat_lng = TZ_FINDER.get_tz_name(lat as f64, lng as f64);
347-
let tz = Tz::from_str(tz_from_lat_lng)
348-
.unwrap_or_else(|_| panic!("Failed to parse timezone from lat lng {tz_from_lat_lng}"));
349-
let now_utc = Local::now().naive_utc();
350-
let offset = tz.offset_from_utc_datetime(&now_utc);
351-
offset.fix().local_minus_utc()
344+
fn timezone_offset_seconds() -> i32 {
345+
// IMPORTANT: This relies on the system timezone being set correctly to the same locale as the
346+
// devices' GPS coordinates to work out correct absolute start/end recording window times.
347+
let now = Local::now();
348+
let local_tz = now.timezone();
349+
local_tz.offset_from_utc_datetime(&now.naive_utc()).local_minus_utc()
352350
}
353351
#[derive(PartialEq, Clone)]
354352
pub struct AbsRelTime {
@@ -375,15 +373,14 @@ impl Debug for AbsRelTime {
375373
}
376374

377375
impl AbsRelTime {
378-
pub fn time_offset(&self, lat: f32, lng: f32) -> (bool, i32) {
376+
pub fn time_offset(&self) -> (bool, i32) {
379377
// Absolute or relative time in seconds in the day
380378
if let Some(abs_time) = &self.absolute_time {
381379
// NOTE: We need to convert this to UTC offsets, since that's what our timestamp is.
382380
let seconds_past_midnight =
383381
(abs_time.hour as i32 * 60 * 60) + (abs_time.min as i32 * 60);
384382
//info!("Seconds past midnight local {}", seconds_past_midnight);
385-
386-
let tz_offset = timezone_offset_seconds(lat, lng);
383+
let tz_offset = timezone_offset_seconds();
387384
// info!(
388385
// "TZ offset {}, seconds past UTC midnight {}",
389386
// tz_offset,
@@ -683,20 +680,22 @@ impl DeviceConfig {
683680
}
684681

685682
pub fn next_recording_window(&self, now_utc: &NaiveDateTime) -> (NaiveDateTime, NaiveDateTime) {
686-
let location =
687-
self.location.as_ref().expect("Relative recording windows require a location");
688-
let (lat, lng) = self.lat_lng();
689683
let (is_absolute_start, mut start_offset) =
690-
self.recording_window.start_recording.time_offset(lat, lng);
691-
let (is_absolute_end, mut end_offset) =
692-
self.recording_window.stop_recording.time_offset(lat, lng);
684+
self.recording_window.start_recording.time_offset();
685+
let (is_absolute_end, mut end_offset) = self.recording_window.stop_recording.time_offset();
693686
if is_absolute_end && end_offset < 0 {
694687
end_offset += 86_400;
695688
}
696689
if is_absolute_start && start_offset < 0 {
697690
start_offset += 86_400;
698691
}
699692
let (window_start, window_end) = if !is_absolute_start || !is_absolute_end {
693+
let location =
694+
self.location.as_ref().expect("Relative recording windows require a location");
695+
let (lat, lng) = (
696+
location.latitude.expect("Relative recording windows require a valid latitude"),
697+
location.longitude.expect("Relative recording windows require a valid longitude"),
698+
);
700699
let altitude = location.altitude;
701700
let yesterday_utc = *now_utc - Duration::days(1);
702701
let (_, yesterday_sunset) = sun_times(
@@ -877,8 +876,8 @@ impl DeviceConfig {
877876
buf.write_u8(has_loc_accuracy).unwrap();
878877
buf.write_f32::<LittleEndian>(accuracy).unwrap();
879878
let (abs_rel_start, abs_rel_end) = self.recording_window();
880-
let (start_is_abs, start_seconds_offset) = abs_rel_start.time_offset(latitude, longitude);
881-
let (end_is_abs, end_seconds_offset) = abs_rel_end.time_offset(latitude, longitude);
879+
let (start_is_abs, start_seconds_offset) = abs_rel_start.time_offset();
880+
let (end_is_abs, end_seconds_offset) = abs_rel_end.time_offset();
882881
buf.write_u8(if start_is_abs { 1 } else { 0 }).unwrap();
883882
buf.write_i32::<LittleEndian>(start_seconds_offset).unwrap();
884883
buf.write_u8(if end_is_abs { 1 } else { 0 }).unwrap();
@@ -917,6 +916,15 @@ pub fn watch_local_config_file_changes(
917916
if config != current_config {
918917
current_config = config;
919918
warn!("Config updated");
919+
920+
let (lat, lng) = current_config.lat_lng();
921+
if let Err(e) =
922+
set_system_timezone(TZ_FINDER.get_tz_name(lat as f64, lng as f64))
923+
{
924+
error!("{e}");
925+
process::exit(1);
926+
}
927+
920928
let _ = config_tx.send(current_config.clone());
921929
}
922930
}

src/main.rs

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,32 +22,32 @@ mod utils;
2222

2323
use rppal::gpio::Gpio;
2424

25+
use localzone::get_local_zone;
2526
use std::fs;
2627
use std::process;
28+
use std::process::Command;
2729
use std::sync::Arc;
2830
use std::sync::atomic::AtomicBool;
2931
use std::sync::mpsc::channel;
3032
use std::thread::sleep;
31-
3233
use std::{thread, time::Duration};
3334
use thread_priority::ThreadBuilderExt;
3435
use thread_priority::*;
3536

36-
use log::{error, info, warn};
37-
use rustbus::connection::Timeout;
38-
use rustbus::{DuplexConn, get_system_bus_path};
39-
use simplelog::*;
40-
use sysinfo::Disks;
41-
4237
use crate::camera_transfer_state::enter_camera_transfer_loop;
4338
use crate::dbus_attiny_i2c::exit_if_attiny_version_is_not_as_expected;
4439
use crate::dbus_managementd::setup_dbus_managementd_recording_service;
45-
use crate::device_config::DeviceConfig;
4640
use crate::device_config::watch_local_config_file_changes;
41+
use crate::device_config::{DeviceConfig, TZ_FINDER};
4742
use crate::frame_socket_server::spawn_frame_socket_server_thread;
4843
use crate::mode_config::ModeConfig;
4944
use crate::program_rp2040::check_if_rp2040_needs_programming;
5045
use crate::recording_state::RecordingState;
46+
use log::{error, info, warn};
47+
use rustbus::connection::Timeout;
48+
use rustbus::{DuplexConn, get_system_bus_path};
49+
use simplelog::*;
50+
use sysinfo::Disks;
5151

5252
const AUDIO_SHEBANG: u16 = 1;
5353

@@ -147,6 +147,13 @@ fn main() {
147147
let _dbus_audio_thread = setup_dbus_managementd_recording_service(&recording_state);
148148

149149
let current_config = device_config.unwrap();
150+
151+
let (lat, lng) = current_config.lat_lng();
152+
if let Err(e) = set_system_timezone(TZ_FINDER.get_tz_name(lat as f64, lng as f64)) {
153+
error!("{e}");
154+
process::exit(1);
155+
}
156+
150157
let initial_config = current_config.clone();
151158
let (device_config_change_channel_tx, device_config_change_channel_rx) = channel();
152159
let _file_watcher =
@@ -227,3 +234,27 @@ fn main() {
227234
process::exit(1);
228235
}
229236
}
237+
238+
pub fn set_system_timezone(timezone: &str) -> Result<(), String> {
239+
let local_tz = get_local_zone();
240+
if local_tz.is_none() {
241+
return Err("Error getting system time zone".to_string());
242+
}
243+
let local_tz = local_tz.unwrap();
244+
if timezone == local_tz {
245+
info!("System timezone already set to {timezone}");
246+
return Ok(());
247+
}
248+
match Command::new("sudo").arg("timedatectl").arg("set-timezone").arg(timezone).output() {
249+
Ok(output) => {
250+
if output.status.success() {
251+
info!("System timezone successfully set to: {}", timezone);
252+
Ok(())
253+
} else {
254+
let stderr = String::from_utf8_lossy(&output.stderr);
255+
Err(format!("Failed to set system timezone: {}", stderr))
256+
}
257+
}
258+
Err(e) => Err(format!("Error executing timedatectl: {}", e)),
259+
}
260+
}

0 commit comments

Comments
 (0)