i3status_rs/blocks/time.rs
1//! The current time.
2//!
3//! # Configuration
4//!
5//! Key | Values | Default
6//! -----------|--------|--------
7//! `format` | [MultiFormat][MaybeMultiFormatConfig] string. See [chrono docs](https://docs.rs/chrono/0.3.0/chrono/format/strftime/index.html#specifiers) for all options. | `[" $icon $timestamp.datetime() "]`
8//! `interval` | Update interval in seconds | `10`
9//! `timezone` | A timezone specifier (e.g. "Europe/Lisbon") | Local timezone
10//!
11//! Placeholder | Value | Type | Unit
12//! --------------|---------------------------------------------|----------|-----
13//! `icon` | A static icon | Icon | -
14//! `timestamp` | The current time | Datetime | -
15//!
16//! Action | Default button
17//! ----------------|---------------
18//! `next_timezone` | Left
19//! `prev_timezone` | Right
20//! `next_format` | -
21//! `prev_format` | -
22//!
23//! # Example
24//!
25//! ```toml
26//! [[block]]
27//! block = "time"
28//! interval = 60
29//! [block.format]
30//! full = " $icon $timestamp.datetime(f:'%a %Y-%m-%d %R %Z', l:fr_BE) "
31//! short = " $icon $timestamp.datetime(f:%R) "
32//! ```
33//!
34//! # Non Gregorian calendars
35//!
36//! You can use calendars other than the Gregorian calendar by adding the calendar specifier in the locale string. When using
37//! this feature you can't use chrono style format string, and you should use one of the options provided by
38//! the `icu4x` crate: `short`, `medium`, `long`, `full`.
39//! If you set `precision` to `hours`/`hour`/`h`, `minutes`/`minute`/`m`, or `seconds`/`second`/`s` then then the datetime will be formatted accordingly, otherwise only the date will be displayed.
40//!
41//! ** Only available using feature `icu_calendar`. **
42//!
43//! ## Example
44//!
45//! ```toml
46//! [[block]]
47//! block = "time"
48//! interval = 60
49//! format = "$timestamp.datetime(locale:'fa-IR-u-ca-persian', f:'full', precision: minutes)"
50//! ```
51//!
52//! # Icons Used
53//! - `time`
54
55use chrono::{Timelike as _, Utc};
56use chrono_tz::Tz;
57
58use super::prelude::*;
59
60#[derive(Deserialize, Debug, SmartDefault)]
61#[serde(default)]
62pub struct Config {
63 #[serde(flatten)]
64 pub formats: MaybeMultiFormatConfig,
65 #[default(10.into())]
66 pub interval: Seconds,
67 pub timezone: Option<Timezone>,
68}
69
70#[derive(Deserialize, Debug, Clone)]
71#[serde(untagged)]
72pub enum Timezone {
73 Timezone(Tz),
74 Timezones(Vec<Tz>),
75}
76
77pub async fn run(config: &Config, api: &CommonApi) -> Result<()> {
78 let mut actions = api.get_actions()?;
79 api.set_default_actions(&[
80 (MouseButton::Left, None, "next_timezone"),
81 (MouseButton::Right, None, "prev_timezone"),
82 ])?;
83
84 let mut formats = config
85 .formats
86 .with_default(" $icon $timestamp.datetime() ")?;
87
88 let timezones = match config.timezone.clone() {
89 Some(tzs) => match tzs {
90 Timezone::Timezone(tz) => vec![tz],
91 Timezone::Timezones(tzs) => tzs,
92 },
93 None => Vec::new(),
94 };
95
96 let prev_step_length = timezones.len().saturating_sub(2);
97
98 let mut timezone_iter = timezones.iter().cycle();
99
100 let mut timezone = timezone_iter.next();
101
102 let interval_seconds = config.interval.seconds().max(1);
103
104 let mut timer = tokio::time::interval_at(
105 tokio::time::Instant::now() + Duration::from_secs(interval_seconds),
106 Duration::from_secs(interval_seconds),
107 );
108 timer.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
109
110 loop {
111 let mut widget = Widget::new().with_format(formats.get_format());
112 let now = Utc::now();
113
114 widget.set_values(map! {
115 "icon" => Value::icon("time"),
116 "timestamp" => Value::datetime(now, timezone.copied())
117 });
118
119 api.set_widget(widget)?;
120
121 let phase = now.second() as u64 % interval_seconds;
122 if phase != 0 {
123 timer.reset_after(Duration::from_secs(interval_seconds - phase));
124 }
125
126 tokio::select! {
127 _ = timer.tick() => (),
128 _ = api.wait_for_update_request() => (),
129 Some(action) = actions.recv() => match action.as_ref() {
130 "next_timezone" => {
131 timezone = timezone_iter.next();
132 },
133 "prev_timezone" => {
134 timezone = timezone_iter.nth(prev_step_length);
135 },
136 "next_format" => {
137 formats.next_format();
138 },
139 "prev_format" => {
140 formats.prev_format();
141 },
142 _ => (),
143 }
144 }
145 }
146}