chore: use jiff instead of chrono where possible

`chrono` can't fully be removed until `sunrise` supports `jiff`.
Also updates `sunrise` and adapts to changes by handling polar days.
This commit is contained in:
Vukašin Vojinović 2026-02-19 18:25:19 +01:00 committed by Michael Murphy
parent 2019f1c33c
commit 91e14abe49
7 changed files with 124 additions and 43 deletions

63
Cargo.lock generated
View file

@ -1482,7 +1482,7 @@ dependencies = [
[[package]]
name = "cosmic-comp-config"
version = "1.0.0"
source = "git+https://github.com/pop-os/cosmic-comp?branch=action-on-typing#df7819b2b24bb6b66ca97a2456572410f3e05521"
source = "git+https://github.com/pop-os/cosmic-comp#ef14b7e29469c4dc066e1c8cf096868d08213caf"
dependencies = [
"cosmic-config",
"input",
@ -1703,6 +1703,7 @@ dependencies = [
"indexmap 2.13.0",
"itertools 0.14.0",
"itoa",
"jiff",
"libcosmic",
"locale1",
"locales-rs",
@ -4268,6 +4269,47 @@ version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84de9d95a6d2547d9b77ee3f25fa0ee32e3c3a6484d47a55adebc0439c077992"
[[package]]
name = "jiff"
version = "0.2.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c867c356cc096b33f4981825ab281ecba3db0acefe60329f044c1789d94c6543"
dependencies = [
"jiff-static",
"jiff-tzdb-platform",
"log",
"portable-atomic",
"portable-atomic-util",
"serde_core",
"windows-sys 0.61.2",
]
[[package]]
name = "jiff-static"
version = "0.2.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7946b4325269738f270bb55b3c19ab5c5040525f83fd625259422a9d25d9be5"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
]
[[package]]
name = "jiff-tzdb"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68971ebff725b9e2ca27a601c5eb38a4c5d64422c4cbab0c535f248087eda5c2"
[[package]]
name = "jiff-tzdb-platform"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8"
dependencies = [
"jiff-tzdb",
]
[[package]]
name = "jni"
version = "0.21.1"
@ -6053,6 +6095,21 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3"
[[package]]
name = "portable-atomic"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
[[package]]
name = "portable-atomic-util"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a9db96d7fa8782dd8c15ce32ffe8680bbd1e978a43bf51a34d39483540495f5"
dependencies = [
"portable-atomic",
]
[[package]]
name = "potential_utf"
version = "0.1.4"
@ -7261,9 +7318,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
[[package]]
name = "sunrise"
version = "2.1.0"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0733c9f1eaa06ed6d103d88e21f784449d08a6733c2ca2b39381cbcbcfe89272"
checksum = "15eecb5ec59a060dd9fe8f88c48b701abecd6be9d9c28d55081b80bdda73981c"
dependencies = [
"chrono",
]

View file

@ -4,7 +4,7 @@ default-members = ["cosmic-settings"]
resolver = "3"
[workspace.package]
rust-version = "1.85"
rust-version = "1.90"
[workspace.dependencies]
cosmic-randr = { git = "https://github.com/pop-os/cosmic-randr" }
@ -22,7 +22,6 @@ git = "https://github.com/pop-os/cosmic-bg"
[workspace.dependencies.cosmic-comp-config]
git = "https://github.com/pop-os/cosmic-comp"
branch = "action-on-typing"
features = ["output"]
[workspace.dependencies.cosmic-idle-config]

View file

@ -11,7 +11,8 @@ anyhow = "1.0"
ashpd = { version = "0.12", default-features = false, features = [
"tokio",
], optional = true }
chrono = "0.4.42"
chrono = "0.4"
jiff = "0.2"
clap = { version = "4.5.54", features = ["derive"] }
color-eyre = "0.6.5"
cosmic-bg-config.workspace = true
@ -69,7 +70,7 @@ serde = { version = "1.0.228", features = ["derive"] }
slab = "0.4.11"
slotmap = "1.1.1"
static_init = "1.0.4"
sunrise = "2.1.0"
sunrise = "3.0.0"
timedate-zbus = { git = "https://github.com/pop-os/dbus-settings-bindings", optional = true }
tokio = { workspace = true, features = ["fs", "io-util", "process", "sync"] }
tracing = "0.1.44"

View file

@ -1,5 +1,5 @@
use chrono::{Duration, TimeDelta};
use futures::{FutureExt, Stream, StreamExt, future::join_all};
use jiff::{Span, SpanRelativeTo, SpanRound, ToSpan, Unit};
use upower_dbus::{BatteryState, BatteryType, DeviceProxy};
use zbus::{Connection, zvariant::ObjectPath};
@ -233,7 +233,7 @@ pub struct Battery {
pub is_present: bool,
pub percent: f64,
pub is_charging: bool,
pub remaining_duration: Duration,
pub remaining_duration: Span,
}
#[derive(Default, Debug, Clone)]
@ -302,7 +302,7 @@ async fn enumerate_devices<'a>() -> Result<Vec<upower_dbus::DeviceProxy<'a>>, zb
impl Battery {
pub async fn from_device(proxy: &DeviceProxy<'_>) -> Self {
let mut remaining_duration: Duration = Duration::default();
let mut remaining_duration = Span::default();
let (is_present, percentage, battery_state) = futures::join!(
proxy.is_present().map(Result::unwrap_or_default),
@ -319,15 +319,11 @@ impl Battery {
&& (percent - 100.0_f64).abs() < f64::EPSILON;
if !is_charging {
if let Ok(time) = proxy.time_to_empty().await
&& let Ok(dur) = Duration::from_std(std::time::Duration::from_secs(time as u64))
{
remaining_duration = dur;
if let Ok(time) = proxy.time_to_empty().await {
remaining_duration = time.seconds();
}
} else if let Ok(time) = proxy.time_to_full().await
&& let Ok(dur) = Duration::from_std(std::time::Duration::from_secs(time as u64))
{
remaining_duration = dur;
} else if let Ok(time) = proxy.time_to_full().await {
remaining_duration = time.seconds();
}
let battery_percent = if percent > 95.0 {
@ -378,15 +374,22 @@ impl Battery {
Battery::default()
}
pub fn remaining_time(&self) -> String {
if self.remaining_duration <= TimeDelta::zero() {
if !self.remaining_duration.is_positive() {
return String::new();
}
let total_seconds = self.remaining_duration.num_seconds();
let balanced = self
.remaining_duration
.round(
SpanRound::new()
.largest(Unit::Day)
.relative(SpanRelativeTo::days_are_24_hours()),
)
.unwrap();
let days = total_seconds / 86400;
let hours = total_seconds % 86400 / 3600;
let minutes = (total_seconds % 3600) / 60;
let days = balanced.get_days();
let hours = balanced.get_hours();
let minutes = balanced.get_minutes();
let mut time: Vec<String> = Vec::new();
if days > 0 {
@ -546,7 +549,7 @@ mod tests {
for case in cases {
let (actual, expected) = case;
let battery = Battery {
remaining_duration: Duration::new(actual, 0).unwrap(),
remaining_duration: actual.seconds(),
is_charging: false,
..Default::default()
};

View file

@ -3,7 +3,6 @@ mod backend;
use self::backend::{GetCurrentPowerProfile, SetPowerProfile};
use backend::{Battery, ConnectedDevice, PowerProfile};
use chrono::TimeDelta;
use cosmic::iced::{self, Alignment, Length};
use cosmic::iced_widget::{column, row};
use cosmic::widget::{self, radio, settings, text};
@ -423,7 +422,7 @@ fn connected_devices() -> Section<crate::pages::Message> {
widget::icon::from_name(connected_device.battery.icon_name.clone());
let battery_percent_and_time = widget::text(
if connected_device.battery.remaining_duration > TimeDelta::zero() {
if connected_device.battery.remaining_duration.is_positive() {
format!(
"{}% - {}",
connected_device.battery.percent,

View file

@ -1,7 +1,6 @@
// Copyright 2023 System76 <info@system76.com>
// SPDX-License-Identifier: GPL-3.0-only
use chrono::{Datelike, Timelike};
use cosmic::{
Apply, Element, Task,
app::ContextDrawer,
@ -564,10 +563,11 @@ fn format_date(date: &DateTime<Gregorian>, military: bool, show_seconds: bool) -
}
fn update_local_time() -> DateTime<Gregorian> {
let now = chrono::Local::now();
let now = jiff::Zoned::now();
DateTime {
date: Date::try_new_gregorian(now.year(), now.month() as u8, now.day() as u8).unwrap(),
date: Date::try_new_gregorian(now.year() as i32, now.month() as u8, now.day() as u8)
.unwrap(),
time: Time::try_new(now.hour() as u8, now.minute() as u8, now.second() as u8, 0).unwrap(),
}
}

View file

@ -1,6 +1,7 @@
use std::any::TypeId;
use ashpd::desktop::location::{Location, LocationProxy};
use chrono::NaiveDate;
use cosmic::iced::{
Subscription,
futures::{SinkExt, StreamExt, channel::mpsc::Sender, future},
@ -46,28 +47,49 @@ async fn inner(mut tx: Sender<bool>) -> anyhow::Result<()> {
};
let coord = Coordinates::new(loc.latitude(), loc.longitude()).unwrap();
let now = chrono::Local::now();
let date = now.date_naive();
let now_in_seconds = now.timestamp();
let now = jiff::Zoned::now();
let now_in_seconds = now.timestamp().as_second();
let northern_tilt = now.month() >= 3 && now.month() <= 9;
// TODO: remove chrono if sunrise adds support for jiff - https://github.com/nathan-osman/rust-sunrise/pull/20
let date = NaiveDate::from_ymd_opt(now.year() as i32, now.month() as u32, now.day() as u32)
.expect("jiff date is valid");
let current_solar_day = SolarDay::new(coord, date);
let sunrise = current_solar_day
.event_time(SolarEvent::Sunrise)
.timestamp();
let sunset = current_solar_day.event_time(SolarEvent::Sunset).timestamp();
let daytime = now_in_seconds >= sunrise && now_in_seconds <= sunset;
.map(|s| s.timestamp());
let sunset = current_solar_day
.event_time(SolarEvent::Sunset)
.map(|s| s.timestamp());
let daytime = match (sunrise, sunset) {
(Some(sunrise), Some(sunset)) => now_in_seconds >= sunrise && now_in_seconds <= sunset,
// transition into polar day
(Some(sunrise), None) => now_in_seconds >= sunrise,
// transition out of polar day
(None, Some(sunset)) => now_in_seconds <= sunset,
// polar day
(None, None) => {
(coord.lat() > 0.0 && northern_tilt) || (coord.lat() < 0.0 && !northern_tilt)
}
};
tx.send(daytime).await?;
let sleep = if daytime {
sunset - now_in_seconds
} else if now_in_seconds < sunset {
sunrise - now_in_seconds
let next_event = if daytime {
sunset
} else if now_in_seconds < sunrise.unwrap_or(0) {
sunrise
} else {
let tmrw = now + chrono::Duration::days(1);
let tmrw_sunrise = SolarDay::new(coord, tmrw.date_naive())
let tmrw = now.checked_add(jiff::Span::new().days(1))?;
let tmrw_sunrise =
NaiveDate::from_ymd_opt(tmrw.year() as i32, tmrw.month() as u32, tmrw.day() as u32)
.expect("jiff date is valid");
SolarDay::new(coord, tmrw_sunrise)
.event_time(SolarEvent::Sunrise)
.timestamp();
tmrw_sunrise - now_in_seconds
.map(|s| s.timestamp())
};
let sleep = next_event
.map(|ts| (ts - now_in_seconds).max(60))
.unwrap_or(3600);
next = select! {
() = tokio::time::sleep(tokio::time::Duration::from_secs(sleep as u64)) => {
Some(Event::Daytime)