summaryrefslogtreecommitdiff
path: root/actualbudget_models/src/actualbudget_date.rs
diff options
context:
space:
mode:
authorJoe Carstairs <me@joeac.net>2024-12-12 08:28:04 +0000
committerJoe Carstairs <me@joeac.net>2024-12-12 08:28:04 +0000
commit54894f9de9bad4fecb2a116d59a03cdd003f84c0 (patch)
tree6ce5517cecf23d4e93d7a381a374e1fa6c810cee /actualbudget_models/src/actualbudget_date.rs
parent8a30bcbdf9264d235bf93da3df8e170cfde53d02 (diff)
Moves rust workspace to root
Diffstat (limited to 'actualbudget_models/src/actualbudget_date.rs')
-rw-r--r--actualbudget_models/src/actualbudget_date.rs79
1 files changed, 79 insertions, 0 deletions
diff --git a/actualbudget_models/src/actualbudget_date.rs b/actualbudget_models/src/actualbudget_date.rs
new file mode 100644
index 0000000..13525fc
--- /dev/null
+++ b/actualbudget_models/src/actualbudget_date.rs
@@ -0,0 +1,79 @@
+use diesel::{backend::Backend, deserialize::{FromSql, FromSqlRow}, expression::AsExpression, sql_types::Integer, sqlite::Sqlite};
+
+#[derive(Clone, Hash, PartialEq, Eq, Debug, AsExpression, FromSqlRow)]
+#[diesel(sql_type=Integer)]
+pub struct ActualbudgetDate(i32);
+
+#[derive(Clone, PartialOrd, PartialEq, Eq)]
+enum DatePart {
+ MonthOfYear,
+ DayOfMonth,
+}
+
+// actualbudget represents dates as numbers which "look like" the corresponding
+// ISO string when written in decimal. E.g. 20240901 represents 1 Sep 2024. It
+// also uses month specifiers, e.g. 202409 to represent Sep 2024. In this case,
+// we interpret this as the first day of the month, e.g. 2024-09-01.
+
+impl ActualbudgetDate {
+ pub fn to_iso(&self) -> String {
+ format!("{0}-{1}-{2}T00:00:00.000Z", self.year(), self.month(), self.day())
+ }
+
+ pub fn to_iso_date(&self) -> String {
+ format!("{0}-{1}-{2}", self.year(), self.month(), self.day())
+ }
+
+ pub fn year(&self) -> String {
+ self.0.to_string()[0..4].to_string()
+ }
+
+ pub fn month(&self) -> String {
+ self.0.to_string()[4..6].to_string()
+ }
+
+ pub fn day(&self) -> String {
+ match self.date_part() {
+ DatePart::DayOfMonth => self.0.to_string()[6..8].to_string(),
+ DatePart::MonthOfYear => String::from("01"),
+ }
+ }
+
+ pub fn from_i32(i: i32) -> Self {
+ ActualbudgetDate(i)
+ }
+
+ fn date_part(&self) -> DatePart {
+ if self.0 < 10000000 {
+ DatePart::MonthOfYear
+ } else {
+ DatePart::DayOfMonth
+ }
+ }
+
+ fn dayified_value(&self) -> i32 {
+ match self.date_part() {
+ DatePart::DayOfMonth => self.0,
+ DatePart::MonthOfYear => self.0 * 100 + 1,
+ }
+ }
+}
+
+impl Ord for ActualbudgetDate {
+ fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+ self.dayified_value().cmp(&other.dayified_value())
+ }
+}
+
+impl PartialOrd for ActualbudgetDate {
+ fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl FromSql<Integer, Sqlite> for ActualbudgetDate {
+ fn from_sql(bytes: <Sqlite as Backend>::RawValue<'_>) -> diesel::deserialize::Result<Self> {
+ let int: i32 = FromSql::<Integer, Sqlite>::from_sql(bytes)?;
+ Ok(ActualbudgetDate(int))
+ }
+}