1
0
mirror of https://github.com/emilk/egui.git synced 2026-08-29 04:40:03 -04:00

Replace chrono with jiff (#8008)

`jiff` is more modern, and seem to be where the ecosystem is heading.
This commit is contained in:
Emil Ernerfeldt
2026-03-24 13:58:21 +01:00
committed by GitHub
parent 5d5f0dedcc
commit a12d18d9bd
12 changed files with 88 additions and 155 deletions

View File

@@ -42,15 +42,15 @@ wayland = ["eframe/wayland"]
x11 = ["eframe/x11"]
[dependencies]
chrono = { workspace = true, features = ["js-sys", "wasmbind"] }
eframe = { workspace = true, default-features = false, features = ["web_screen_reader"] }
egui = { workspace = true, features = ["callstack", "default"] }
egui_demo_lib = { workspace = true, features = ["default", "chrono"] }
egui_demo_lib = { workspace = true, features = ["default", "jiff"] }
egui_extras = { workspace = true, features = ["default", "image"] }
image = { workspace = true, default-features = false, features = [
# Ensure we can display the test images
"png",
] }
jiff = { workspace = true, features = ["std", "tz-system", "js"] }
log.workspace = true
profiling.workspace = true

View File

@@ -9,9 +9,10 @@ pub use wrap_app::{Anchor, WrapApp};
/// Time of day as seconds since midnight. Used for clock in demo app.
pub(crate) fn seconds_since_midnight() -> f64 {
use chrono::Timelike as _;
let time = chrono::Local::now().time();
time.num_seconds_from_midnight() as f64 + 1e-9 * (time.nanosecond() as f64)
jiff::Zoned::now()
.time()
.duration_since(jiff::civil::Time::midnight())
.as_secs_f64()
}
/// Trait that wraps different parts of the demo app.

View File

@@ -26,7 +26,7 @@ rustdoc-args = ["--generate-link-to-definition"]
[features]
default = []
chrono = ["egui_extras/datepicker", "dep:chrono"]
jiff = ["egui_extras/datepicker", "dep:jiff"]
## Allow serialization using [`serde`](https://docs.rs/serde).
serde = ["egui/serde", "dep:serde", "egui_extras/serde"]
@@ -42,7 +42,7 @@ egui_extras = { workspace = true, features = ["image", "svg"] }
unicode_names2.workspace = true # this old version has fewer dependencies
#! ### Optional dependencies
chrono = { workspace = true, optional = true, features = ["js-sys", "wasmbind"] }
jiff = { workspace = true, optional = true, features = ["std", "js"] }
## Enable this when generating docs.
document-features = { workspace = true, optional = true }
serde = { workspace = true, optional = true }

View File

@@ -19,11 +19,11 @@ pub struct WidgetGallery {
color: egui::Color32,
animate_progress_bar: bool,
#[cfg(feature = "chrono")]
#[cfg(feature = "jiff")]
#[cfg_attr(feature = "serde", serde(skip))]
date: Option<chrono::NaiveDate>,
date: Option<jiff::civil::Date>,
#[cfg(feature = "chrono")]
#[cfg(feature = "jiff")]
with_date_button: bool,
}
@@ -39,19 +39,19 @@ impl Default for WidgetGallery {
string: Default::default(),
color: egui::Color32::LIGHT_BLUE.linear_multiply(0.5),
animate_progress_bar: false,
#[cfg(feature = "chrono")]
#[cfg(feature = "jiff")]
date: None,
#[cfg(feature = "chrono")]
#[cfg(feature = "jiff")]
with_date_button: true,
}
}
}
impl WidgetGallery {
#[allow(clippy::allow_attributes, unused_mut)] // if not chrono
#[allow(clippy::allow_attributes, unused_mut)] // if not jiff
#[inline]
pub fn with_date_button(mut self, _with_date_button: bool) -> Self {
#[cfg(feature = "chrono")]
#[cfg(feature = "jiff")]
{
self.with_date_button = _with_date_button;
}
@@ -140,9 +140,9 @@ impl WidgetGallery {
string,
color,
animate_progress_bar,
#[cfg(feature = "chrono")]
#[cfg(feature = "jiff")]
date,
#[cfg(feature = "chrono")]
#[cfg(feature = "jiff")]
with_date_button,
} = self;
@@ -242,9 +242,9 @@ impl WidgetGallery {
}
ui.end_row();
#[cfg(feature = "chrono")]
#[cfg(feature = "jiff")]
if *with_date_button {
let date = date.get_or_insert_with(|| chrono::offset::Utc::now().date_naive());
let date = date.get_or_insert_with(|| jiff::Zoned::now().date());
ui.add(doc_link_label_with_crate(
"egui_extras",
"DatePickerButton",
@@ -302,7 +302,7 @@ fn doc_link_label_with_crate<'a>(
}
}
#[cfg(feature = "chrono")]
#[cfg(feature = "jiff")]
#[cfg(test)]
mod tests {
use super::*;
@@ -314,7 +314,7 @@ mod tests {
pub fn should_match_screenshot() {
let mut demo = WidgetGallery {
// If we don't set a fixed date, the snapshot test will fail.
date: Some(chrono::NaiveDate::from_ymd_opt(2024, 1, 1).unwrap()),
date: Some(jiff::civil::date(2024, 1, 1)),
..Default::default()
};

View File

@@ -34,7 +34,7 @@ default = ["dep:mime_guess2"]
all_loaders = ["file", "http", "image", "svg", "gif", "webp"]
## Enable [`DatePickerButton`] widget.
datepicker = ["chrono"]
datepicker = ["jiff"]
## Add support for loading images from `file://` URIs.
file = ["dep:mime_guess2"]
@@ -83,7 +83,7 @@ profiling.workspace = true
serde = { workspace = true, optional = true }
# Date operations needed for datepicker widget
chrono = { workspace = true, optional = true, features = ["clock", "js-sys", "std", "wasmbind"] }
jiff = { workspace = true, optional = true, features = ["std", "tz-system", "js"] }
## Enable this when generating docs.
document-features = { workspace = true, optional = true }

View File

@@ -1,6 +1,6 @@
use super::popup::DatePickerPopup;
use chrono::NaiveDate;
use egui::{Area, Button, Frame, InnerResponse, Key, Order, RichText, Ui, Widget};
use jiff::civil::Date;
use std::ops::RangeInclusive;
#[derive(Default, Clone)]
@@ -11,7 +11,7 @@ pub(crate) struct DatePickerButtonState {
/// Shows a date, and will open a date picker popup when clicked.
pub struct DatePickerButton<'a> {
selection: &'a mut NaiveDate,
selection: &'a mut Date,
id_salt: Option<&'a str>,
combo_boxes: bool,
arrows: bool,
@@ -20,13 +20,13 @@ pub struct DatePickerButton<'a> {
show_icon: bool,
format: String,
highlight_weekends: bool,
start_end_years: Option<RangeInclusive<i32>>,
start_end_years: Option<RangeInclusive<i16>>,
reverse_years: bool,
year_scroll_to: Option<i32>,
year_scroll_to: Option<i16>,
}
impl<'a> DatePickerButton<'a> {
pub fn new(selection: &'a mut NaiveDate) -> Self {
pub fn new(selection: &'a mut Date) -> Self {
Self {
selection,
id_salt: None,
@@ -95,7 +95,7 @@ impl<'a> DatePickerButton<'a> {
}
/// Change the format shown on the button. (Default: %Y-%m-%d)
/// See [`chrono::format::strftime`] for valid formats.
/// See [`jiff::fmt::strtime`] for valid formats.
#[inline]
pub fn format(mut self, format: impl Into<String>) -> Self {
self.format = format.into();
@@ -115,7 +115,7 @@ impl<'a> DatePickerButton<'a> {
/// For example, if you want to provide the range of years from 2000 to 2035, you can use:
/// `start_end_years(2000..=2035)`.
#[inline]
pub fn start_end_years(mut self, start_end_years: RangeInclusive<i32>) -> Self {
pub fn start_end_years(mut self, start_end_years: RangeInclusive<i16>) -> Self {
self.start_end_years = Some(start_end_years);
self
}
@@ -130,7 +130,7 @@ impl<'a> DatePickerButton<'a> {
/// Scroll the year dropdown to this year when the picker first opens.
/// Defaults to the currently selected year.
#[inline]
pub fn year_scroll_to(mut self, year: i32) -> Self {
pub fn year_scroll_to(mut self, year: i16) -> Self {
self.year_scroll_to = Some(year);
self
}
@@ -144,9 +144,9 @@ impl Widget for DatePickerButton<'_> {
.unwrap_or_default();
let mut text = if self.show_icon {
RichText::new(format!("{} 📆", self.selection.format(&self.format)))
RichText::new(format!("{} 📆", self.selection.strftime(&self.format)))
} else {
RichText::new(format!("{}", self.selection.format(&self.format)))
RichText::new(format!("{}", self.selection.strftime(&self.format)))
};
let visuals = ui.visuals().widgets.open;
if button_state.picker_visible {

View File

@@ -4,32 +4,32 @@ mod button;
mod popup;
pub use button::DatePickerButton;
use chrono::{Datelike as _, Duration, NaiveDate, Weekday};
use jiff::civil::{Date, ISOWeekDate, Weekday};
#[derive(Debug)]
struct Week {
number: u8,
days: Vec<NaiveDate>,
days: Vec<Date>,
}
fn month_data(year: i32, month: u32) -> Vec<Week> {
let first = NaiveDate::from_ymd_opt(year, month, 1).expect("Could not create NaiveDate");
fn month_data(year: i16, month: i8) -> Vec<Week> {
let first = Date::new(year, month, 1).expect("Could not create Date");
let mut start = first;
while start.weekday() != Weekday::Mon {
start = start.checked_sub_signed(Duration::days(1)).unwrap();
while start.weekday() != Weekday::Monday {
start = start.yesterday().unwrap();
}
let mut weeks = vec![];
let mut week = vec![];
while start < first || start.month() == first.month() || start.weekday() != Weekday::Mon {
while start < first || start.month() == first.month() || start.weekday() != Weekday::Monday {
week.push(start);
if start.weekday() == Weekday::Sun {
if start.weekday() == Weekday::Sunday {
weeks.push(Week {
number: start.iso_week().week() as u8,
number: ISOWeekDate::from(start).week() as u8,
days: std::mem::take(&mut week),
});
}
start = start.checked_add_signed(Duration::days(1)).unwrap();
start = start.tomorrow().unwrap();
}
weeks

View File

@@ -1,4 +1,4 @@
use chrono::{Datelike as _, NaiveDate, Weekday};
use jiff::civil::{Date, Weekday};
use egui::{Align, Button, Color32, ComboBox, Direction, Id, Layout, RichText, Ui, Vec2};
@@ -9,43 +9,39 @@ use crate::{Column, Size, StripBuilder, TableBuilder};
#[derive(Default, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
struct DatePickerPopupState {
year: i32,
month: u32,
day: u32,
year: i16,
month: i8,
day: i8,
setup: bool,
year_scroll_needed: bool,
}
impl DatePickerPopupState {
fn last_day_of_month(&self) -> u32 {
let date: NaiveDate =
NaiveDate::from_ymd_opt(self.year, self.month, 1).expect("Could not create NaiveDate");
date.with_day(31)
.map(|_| 31)
.or_else(|| date.with_day(30).map(|_| 30))
.or_else(|| date.with_day(29).map(|_| 29))
.unwrap_or(28)
fn last_day_of_month(&self) -> i8 {
Date::new(self.year, self.month, 1)
.expect("Could not create Date")
.days_in_month()
}
}
pub(crate) struct DatePickerPopup<'a> {
pub selection: &'a mut NaiveDate,
pub selection: &'a mut Date,
pub button_id: Id,
pub combo_boxes: bool,
pub arrows: bool,
pub calendar: bool,
pub calendar_week: bool,
pub highlight_weekends: bool,
pub start_end_years: Option<std::ops::RangeInclusive<i32>>,
pub start_end_years: Option<std::ops::RangeInclusive<i16>>,
pub reverse_years: bool,
pub year_scroll_to: Option<i32>,
pub year_scroll_to: Option<i16>,
}
impl DatePickerPopup<'_> {
/// Returns `true` if user pressed `Save` button.
pub fn draw(&mut self, ui: &mut Ui) -> bool {
let id = ui.make_persistent_id("date_picker");
let today = chrono::offset::Utc::now().date_naive();
let today = jiff::Zoned::now().date();
let mut popup_state = ui
.data_mut(|data| data.get_persisted::<DatePickerPopupState>(id))
.unwrap_or_default();
@@ -95,7 +91,7 @@ impl DatePickerPopup<'_> {
};
let scroll_to_year =
self.year_scroll_to.unwrap_or(popup_state.year);
let years: Vec<i32> = if self.reverse_years {
let years: Vec<i16> = if self.reverse_years {
(start_year..=end_year).rev().collect()
} else {
(start_year..=end_year).collect()
@@ -132,7 +128,7 @@ impl DatePickerPopup<'_> {
ComboBox::from_id_salt("date_picker_month")
.selected_text(month_name(popup_state.month))
.show_ui(ui, |ui| {
for month in 1..=12 {
for month in 1i8..=12 {
if ui
.selectable_value(
&mut popup_state.month,
@@ -156,7 +152,7 @@ impl DatePickerPopup<'_> {
ComboBox::from_id_salt("date_picker_day")
.selected_text(popup_state.day.to_string())
.show_ui(ui, |ui| {
for day in 1..=popup_state.last_day_of_month() {
for day in 1i8..=popup_state.last_day_of_month() {
if ui
.selectable_value(
&mut popup_state.day,
@@ -333,9 +329,10 @@ impl DatePickerPopup<'_> {
&& popup_state.day == day.day()
{
ui.visuals().selection.bg_fill
} else if (day.weekday() == Weekday::Sat
|| day.weekday() == Weekday::Sun)
&& self.highlight_weekends
} else if (matches!(
day.weekday(),
Weekday::Saturday | Weekday::Sunday
)) && self.highlight_weekends
{
if ui.visuals().dark_mode {
Color32::DARK_RED
@@ -414,12 +411,12 @@ impl DatePickerPopup<'_> {
strip.cell(|ui| {
ui.with_layout(Layout::top_down_justified(Align::Center), |ui| {
if ui.button("Save").clicked() {
*self.selection = NaiveDate::from_ymd_opt(
*self.selection = Date::new(
popup_state.year,
popup_state.month,
popup_state.day,
)
.expect("Could not create NaiveDate");
.expect("Could not create Date");
saved = true;
close = true;
}
@@ -442,7 +439,7 @@ impl DatePickerPopup<'_> {
}
}
fn month_name(i: u32) -> &'static str {
fn month_name(i: i8) -> &'static str {
match i {
1 => "January",
2 => "February",

View File

@@ -8,7 +8,7 @@
#![expect(clippy::manual_range_contains)]
#[cfg(feature = "chrono")]
#[cfg(feature = "datepicker")]
mod datepicker;
pub mod syntax_highlighting;
@@ -21,7 +21,7 @@ mod sizing;
mod strip;
mod table;
#[cfg(feature = "chrono")]
#[cfg(feature = "datepicker")]
pub use crate::datepicker::DatePickerButton;
pub(crate) use crate::layout::StripLayout;