initial commit
This commit is contained in:
6 files changed
+1082
No files matched your search
@@ -0,0 +1 @@
|
|||||||
|
/target
|
||||||
Generated
+395
@@ -0,0 +1,395 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "android-tzdata"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "android_system_properties"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bumpalo"
|
||||||
|
version = "3.16.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.1.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72db2f7947ecee9b03b510377e8bb9077afa27176fdbff55c51027e976fdcc48"
|
||||||
|
dependencies = [
|
||||||
|
"shlex",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chrono"
|
||||||
|
version = "0.4.38"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
|
||||||
|
dependencies = [
|
||||||
|
"android-tzdata",
|
||||||
|
"iana-time-zone",
|
||||||
|
"js-sys",
|
||||||
|
"num-traits",
|
||||||
|
"serde",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "core-foundation-sys"
|
||||||
|
version = "0.8.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "equivalent"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.14.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iana-time-zone"
|
||||||
|
version = "0.1.60"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
|
||||||
|
dependencies = [
|
||||||
|
"android_system_properties",
|
||||||
|
"core-foundation-sys",
|
||||||
|
"iana-time-zone-haiku",
|
||||||
|
"js-sys",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"windows-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iana-time-zone-haiku"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "2.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c"
|
||||||
|
dependencies = [
|
||||||
|
"equivalent",
|
||||||
|
"hashbrown",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "js-sys"
|
||||||
|
version = "0.3.70"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a"
|
||||||
|
dependencies = [
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.158"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.22"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-traits"
|
||||||
|
version = "0.2.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.19.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.86"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.36"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.208"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.208"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.125"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"memchr",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_yaml"
|
||||||
|
version = "0.9.34+deprecated"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
|
||||||
|
dependencies = [
|
||||||
|
"indexmap",
|
||||||
|
"itoa",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
"unsafe-libyaml",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shlex"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.75"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f6af063034fc1935ede7be0122941bafa9bacb949334d090b77ca98b5817c7d9"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unsafe-libyaml"
|
||||||
|
version = "0.2.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen"
|
||||||
|
version = "0.2.93"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"once_cell",
|
||||||
|
"wasm-bindgen-macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-backend"
|
||||||
|
version = "0.2.93"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro"
|
||||||
|
version = "0.2.93"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"wasm-bindgen-macro-support",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro-support"
|
||||||
|
version = "0.2.93"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-backend",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-shared"
|
||||||
|
version = "0.2.93"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-core"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm",
|
||||||
|
"windows_aarch64_msvc",
|
||||||
|
"windows_i686_gnu",
|
||||||
|
"windows_i686_gnullvm",
|
||||||
|
"windows_i686_msvc",
|
||||||
|
"windows_x86_64_gnu",
|
||||||
|
"windows_x86_64_gnullvm",
|
||||||
|
"windows_x86_64_msvc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "workdays"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"chrono",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"serde_yaml",
|
||||||
|
]
|
||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
[package]
|
||||||
|
name = "workdays"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
authors = ["Stephen Waits <steve@waits.net>"]
|
||||||
|
description = "A Rust library for computing work days and handling work calendars, inspired by the WORKDAY function in Excel."
|
||||||
|
license = "MIT"
|
||||||
|
repository = "https://github.com/swaits/workdays"
|
||||||
|
documentation = "https://docs.rs/workdays"
|
||||||
|
readme = "README.md"
|
||||||
|
keywords = ["calendar", "workday", "date", "business", "excel"]
|
||||||
|
categories = ["date-and-time", "algorithms"]
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
chrono = { version = "0.4.38", features = ["serde"] }
|
||||||
|
serde = { version = "1.0.208", features = ["derive"] }
|
||||||
|
serde_json = "1.0.125"
|
||||||
|
serde_yaml = "0.9.34"
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
Copyright 2024 Stephen Waits <steve@waits.net>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the “Software”), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
# Workdays
|
||||||
|
|
||||||
|
`workdays` is a Rust library for computing work days and handling work calendars, inspired by the WORKDAY function in Excel. It provides functionality to define custom work weeks, add holidays, and compute dates based on a given number of work days.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Compute end dates based on work days
|
||||||
|
- Calculate the number of work days between two dates
|
||||||
|
- Parse and handle work calendar configurations (YAML or JSON)
|
||||||
|
- Support for custom work days and holidays
|
||||||
|
- Flexible weekday parsing
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Add `workdays` as a depend in your `Cargo.toml`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo add workdays
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Here's a quick example:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use workdays::WorkCalendar;
|
||||||
|
use chrono::NaiveDate;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let mut calendar = WorkCalendar::new();
|
||||||
|
calendar.add_holiday(NaiveDate::from_ymd_opt(2023, 12, 25).unwrap());
|
||||||
|
calendar.set_work_days("Mon,Tue,Wed,Thu,Fri")?;
|
||||||
|
|
||||||
|
let start_date = NaiveDate::from_ymd_opt(2023, 8, 21).unwrap();
|
||||||
|
let days_worked = 20;
|
||||||
|
|
||||||
|
let (end_date, calendar_duration) = calendar.compute_end_date(start_date, days_worked)?;
|
||||||
|
println!("End date: {}", end_date);
|
||||||
|
println!("Calendar duration: {} days", calendar_duration.num_days());
|
||||||
|
|
||||||
|
// Using FromStr to create a WorkCalendar from YAML or JSON
|
||||||
|
let config = r#"
|
||||||
|
work_days:
|
||||||
|
- Monday
|
||||||
|
- Tuesday
|
||||||
|
- Wednesday
|
||||||
|
holidays:
|
||||||
|
- 2023-12-25
|
||||||
|
"#;
|
||||||
|
let custom_calendar = WorkCalendar::from_str(config)?;
|
||||||
|
println!("Is Monday a work day? {}", custom_calendar.is_work_day(&chrono::Weekday::Mon));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This project is licensed under the MIT License - see the [LICENSE](./LICENSE) file for details.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
Contributions are welcome! Please feel free to submit a Pull Request.
|
||||||
|
|
||||||
|
## Acknowledgments
|
||||||
|
|
||||||
|
- Inspired by the WORKDAY function in Excel
|
||||||
+580
@@ -0,0 +1,580 @@
|
|||||||
|
//! # Workdays
|
||||||
|
//!
|
||||||
|
//! `workdays` is a Rust library for computing work days and handling work calendars.
|
||||||
|
//! It provides functionality to define custom work weeks, add holidays, and compute
|
||||||
|
//! dates based on a given number of work days.
|
||||||
|
//!
|
||||||
|
//! ## Features
|
||||||
|
//!
|
||||||
|
//! - Compute end dates based on work days
|
||||||
|
//! - Calculate the number of work days between two dates
|
||||||
|
//! - Parse and handle work calendar configurations (YAML or JSON)
|
||||||
|
//! - Support for custom work days and holidays
|
||||||
|
//! - Flexible weekday parsing
|
||||||
|
//!
|
||||||
|
//! ## Usage
|
||||||
|
//!
|
||||||
|
//! Here's a quick example:
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! use workdays::WorkCalendar;
|
||||||
|
//! use chrono::NaiveDate;
|
||||||
|
//!
|
||||||
|
//! let mut calendar = WorkCalendar::new();
|
||||||
|
//! calendar.add_holiday(NaiveDate::from_ymd_opt(2023, 12, 25).unwrap());
|
||||||
|
//! calendar.set_work_days("Mon,Tue,Wed,Thu,Fri").unwrap();
|
||||||
|
//!
|
||||||
|
//! let start_date = NaiveDate::from_ymd_opt(2023, 8, 21).unwrap();
|
||||||
|
//! let days_worked = 20;
|
||||||
|
//!
|
||||||
|
//! let (end_date, calendar_duration) = calendar.compute_end_date(start_date, days_worked).unwrap();
|
||||||
|
//! println!("End date: {}", end_date);
|
||||||
|
//! println!("Calendar duration: {} days", calendar_duration.num_days());
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
use chrono::{Datelike, Duration, NaiveDate, Weekday};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::{collections::HashSet, str::FromStr};
|
||||||
|
|
||||||
|
/// Represents a work calendar with customizable work days and holidays.
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Default)]
|
||||||
|
pub struct WorkCalendar {
|
||||||
|
work_days: HashSet<Weekday>,
|
||||||
|
holidays: HashSet<NaiveDate>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for WorkCalendar {
|
||||||
|
type Err = Box<dyn std::error::Error>;
|
||||||
|
|
||||||
|
/// Creates a `WorkCalendar` from a YAML or JSON string.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `s` - A string slice that holds the configuration in YAML or JSON format.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::str::FromStr;
|
||||||
|
/// use workdays::WorkCalendar;
|
||||||
|
///
|
||||||
|
/// let config = r#"
|
||||||
|
/// work_days:
|
||||||
|
/// - Monday
|
||||||
|
/// - Tuesday
|
||||||
|
/// - Wednesday
|
||||||
|
/// holidays:
|
||||||
|
/// - 2023-12-25
|
||||||
|
/// "#;
|
||||||
|
///
|
||||||
|
/// let calendar = WorkCalendar::from_str(config).unwrap();
|
||||||
|
/// assert!(calendar.is_work_day(&chrono::Weekday::Mon));
|
||||||
|
/// assert!(!calendar.is_work_day(&chrono::Weekday::Thu));
|
||||||
|
/// ```
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let config: WorkCalendarConfig = if s.trim_start().starts_with('{') {
|
||||||
|
serde_json::from_str(s)?
|
||||||
|
} else {
|
||||||
|
serde_yaml::from_str(s)?
|
||||||
|
};
|
||||||
|
Ok(Self::from(config))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WorkCalendar {
|
||||||
|
/// Creates a new `WorkCalendar` with default work days (Monday to Friday) and no holidays.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use workdays::WorkCalendar;
|
||||||
|
/// use chrono::Weekday;
|
||||||
|
///
|
||||||
|
/// let calendar = WorkCalendar::new();
|
||||||
|
/// assert!(calendar.is_work_day(&Weekday::Mon));
|
||||||
|
/// assert!(!calendar.is_work_day(&Weekday::Sat));
|
||||||
|
/// ```
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let work_days = [
|
||||||
|
Weekday::Mon,
|
||||||
|
Weekday::Tue,
|
||||||
|
Weekday::Wed,
|
||||||
|
Weekday::Thu,
|
||||||
|
Weekday::Fri,
|
||||||
|
]
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
WorkCalendar {
|
||||||
|
work_days,
|
||||||
|
holidays: HashSet::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Computes the end date and calendar duration given a start date and number of work days.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `start_date` - The starting date.
|
||||||
|
/// * `days_worked` - Number of work days to add.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// A tuple containing the end date and the calendar duration, or an error if the input is invalid.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use workdays::WorkCalendar;
|
||||||
|
/// use chrono::NaiveDate;
|
||||||
|
///
|
||||||
|
/// let calendar = WorkCalendar::new();
|
||||||
|
/// let start_date = NaiveDate::from_ymd_opt(2023, 8, 21).unwrap();
|
||||||
|
/// let (end_date, duration) = calendar.compute_end_date(start_date, 5).unwrap();
|
||||||
|
///
|
||||||
|
/// assert_eq!(end_date, NaiveDate::from_ymd_opt(2023, 8, 25).unwrap());
|
||||||
|
/// assert_eq!(duration.num_days(), 4);
|
||||||
|
/// ```
|
||||||
|
pub fn compute_end_date(
|
||||||
|
&self,
|
||||||
|
start_date: NaiveDate,
|
||||||
|
days_worked: i64,
|
||||||
|
) -> Result<(NaiveDate, Duration), String> {
|
||||||
|
if days_worked < 0 {
|
||||||
|
return Err("days_worked must be non-negative".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.work_days.is_empty() {
|
||||||
|
return Err("No work days defined".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut current_date = start_date;
|
||||||
|
let mut remaining_days = days_worked;
|
||||||
|
|
||||||
|
// If the start date is a work day, count it
|
||||||
|
if self.is_work_day(¤t_date.weekday()) && !self.is_holiday(¤t_date) {
|
||||||
|
remaining_days -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while remaining_days > 0 {
|
||||||
|
current_date += Duration::days(1);
|
||||||
|
|
||||||
|
if self.is_work_day(¤t_date.weekday()) && !self.is_holiday(¤t_date) {
|
||||||
|
remaining_days -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let calendar_duration = current_date.signed_duration_since(start_date);
|
||||||
|
Ok((current_date, calendar_duration))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a work day to the calendar.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `day` - The `Weekday` to add as a work day.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use workdays::WorkCalendar;
|
||||||
|
/// use chrono::Weekday;
|
||||||
|
///
|
||||||
|
/// let mut calendar = WorkCalendar::new();
|
||||||
|
/// calendar.add_work_day(Weekday::Sat);
|
||||||
|
/// assert!(calendar.is_work_day(&Weekday::Sat));
|
||||||
|
/// ```
|
||||||
|
pub fn add_work_day(&mut self, day: Weekday) {
|
||||||
|
self.work_days.insert(day);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes a work day from the calendar.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `day` - The `Weekday` to remove from work days.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use workdays::WorkCalendar;
|
||||||
|
/// use chrono::Weekday;
|
||||||
|
///
|
||||||
|
/// let mut calendar = WorkCalendar::new();
|
||||||
|
/// calendar.remove_work_day(&Weekday::Fri);
|
||||||
|
/// assert!(!calendar.is_work_day(&Weekday::Fri));
|
||||||
|
/// ```
|
||||||
|
pub fn remove_work_day(&mut self, day: &Weekday) {
|
||||||
|
self.work_days.remove(day);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a holiday to the calendar.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `date` - The `NaiveDate` to add as a holiday.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use workdays::WorkCalendar;
|
||||||
|
/// use chrono::NaiveDate;
|
||||||
|
///
|
||||||
|
/// let mut calendar = WorkCalendar::new();
|
||||||
|
/// let holiday = NaiveDate::from_ymd_opt(2023, 12, 25).unwrap();
|
||||||
|
/// calendar.add_holiday(holiday);
|
||||||
|
/// assert!(calendar.is_holiday(&holiday));
|
||||||
|
/// ```
|
||||||
|
pub fn add_holiday(&mut self, date: NaiveDate) {
|
||||||
|
self.holidays.insert(date);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes a holiday from the calendar.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `date` - The `NaiveDate` to remove from holidays.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use workdays::WorkCalendar;
|
||||||
|
/// use chrono::NaiveDate;
|
||||||
|
///
|
||||||
|
/// let mut calendar = WorkCalendar::new();
|
||||||
|
/// let holiday = NaiveDate::from_ymd_opt(2023, 12, 25).unwrap();
|
||||||
|
/// calendar.add_holiday(holiday);
|
||||||
|
/// calendar.remove_holiday(&holiday);
|
||||||
|
/// assert!(!calendar.is_holiday(&holiday));
|
||||||
|
/// ```
|
||||||
|
pub fn remove_holiday(&mut self, date: &NaiveDate) {
|
||||||
|
self.holidays.remove(date);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set work days from a comma-separated string.
|
||||||
|
///
|
||||||
|
/// This method overrides all existing work days with the ones specified in the input string.
|
||||||
|
/// Day names are case-insensitive and can be full names (e.g., "Monday") or abbreviations (e.g., "Mon").
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `days` - A comma-separated string of day names (e.g., "mon,tue,Wednesday,thu,friday")
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(())` if successful
|
||||||
|
/// * `Err(String)` if the input string contains invalid day names
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use workdays::WorkCalendar;
|
||||||
|
/// use chrono::Weekday;
|
||||||
|
///
|
||||||
|
/// let mut calendar = WorkCalendar::new();
|
||||||
|
/// calendar.set_work_days("Mon,Wed,Fri").unwrap();
|
||||||
|
/// assert!(calendar.is_work_day(&Weekday::Mon));
|
||||||
|
/// assert!(calendar.is_work_day(&Weekday::Wed));
|
||||||
|
/// assert!(calendar.is_work_day(&Weekday::Fri));
|
||||||
|
/// assert!(!calendar.is_work_day(&Weekday::Tue));
|
||||||
|
/// ```
|
||||||
|
pub fn set_work_days(&mut self, days: &str) -> Result<(), String> {
|
||||||
|
let new_work_days: HashSet<Weekday> = days
|
||||||
|
.split(',')
|
||||||
|
.filter_map(|day| parse_weekday(day.trim()))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if new_work_days.is_empty() {
|
||||||
|
return Err("No valid work days provided".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.work_days = new_work_days;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if a given day is a work day.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `day` - The `Weekday` to check.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// `true` if the day is a work day, `false` otherwise.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use workdays::WorkCalendar;
|
||||||
|
/// use chrono::Weekday;
|
||||||
|
///
|
||||||
|
/// let calendar = WorkCalendar::new();
|
||||||
|
/// assert!(calendar.is_work_day(&Weekday::Mon));
|
||||||
|
/// assert!(!calendar.is_work_day(&Weekday::Sat));
|
||||||
|
/// ```
|
||||||
|
pub fn is_work_day(&self, day: &Weekday) -> bool {
|
||||||
|
self.work_days.contains(day)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if a given date is a holiday.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `date` - The `NaiveDate` to check.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// `true` if the date is a holiday, `false` otherwise.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use workdays::WorkCalendar;
|
||||||
|
/// use chrono::NaiveDate;
|
||||||
|
///
|
||||||
|
/// let mut calendar = WorkCalendar::new();
|
||||||
|
/// let holiday = NaiveDate::from_ymd_opt(2023, 12, 25).unwrap();
|
||||||
|
/// calendar.add_holiday(holiday);
|
||||||
|
/// assert!(calendar.is_holiday(&holiday));
|
||||||
|
/// ```
|
||||||
|
pub fn is_holiday(&self, date: &NaiveDate) -> bool {
|
||||||
|
self.holidays.contains(date)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculates the number of work days between two dates (inclusive).
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `start_date` - The starting date.
|
||||||
|
/// * `end_date` - The ending date.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// The number of work days between the two dates (inclusive).
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use workdays::WorkCalendar;
|
||||||
|
/// use chrono::NaiveDate;
|
||||||
|
///
|
||||||
|
/// let calendar = WorkCalendar::new();
|
||||||
|
/// let start_date = NaiveDate::from_ymd_opt(2023, 8, 21).unwrap(); // Monday
|
||||||
|
/// let end_date = NaiveDate::from_ymd_opt(2023, 8, 25).unwrap(); // Friday
|
||||||
|
/// assert_eq!(calendar.work_days_between(start_date, end_date), 5);
|
||||||
|
/// ```
|
||||||
|
pub fn work_days_between(&self, start_date: NaiveDate, end_date: NaiveDate) -> i64 {
|
||||||
|
let mut work_days = 0;
|
||||||
|
let mut current_date = start_date;
|
||||||
|
|
||||||
|
while current_date <= end_date {
|
||||||
|
if self.work_days.contains(¤t_date.weekday()) && !self.is_holiday(¤t_date) {
|
||||||
|
work_days += 1;
|
||||||
|
}
|
||||||
|
current_date += Duration::days(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
work_days
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Default)]
|
||||||
|
struct WorkCalendarConfig {
|
||||||
|
work_days: Option<Vec<String>>,
|
||||||
|
holidays: Option<Vec<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<WorkCalendarConfig> for WorkCalendar {
|
||||||
|
fn from(config: WorkCalendarConfig) -> Self {
|
||||||
|
let mut calendar = WorkCalendar::new();
|
||||||
|
|
||||||
|
if let Some(days) = config.work_days {
|
||||||
|
calendar.work_days = days
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|day| parse_weekday(&day))
|
||||||
|
.collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(dates) = config.holidays {
|
||||||
|
calendar.holidays = dates
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|date_str| NaiveDate::parse_from_str(&date_str, "%Y-%m-%d").ok())
|
||||||
|
.collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
calendar
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parses a weekday string into a `Weekday` enum.
|
||||||
|
///
|
||||||
|
/// This function is case-insensitive and accepts both full names (e.g., "Monday")
|
||||||
|
/// and abbreviations (e.g., "Mon").
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `day` - A string slice that holds the name of the day.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Some(Weekday)` if the day name is valid.
|
||||||
|
/// * `None` if the day name is invalid.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use workdays::parse_weekday;
|
||||||
|
/// use chrono::Weekday;
|
||||||
|
///
|
||||||
|
/// assert_eq!(parse_weekday("Monday"), Some(Weekday::Mon));
|
||||||
|
/// assert_eq!(parse_weekday("tue"), Some(Weekday::Tue));
|
||||||
|
/// assert_eq!(parse_weekday("Invalid"), None);
|
||||||
|
/// ```
|
||||||
|
pub fn parse_weekday(day: &str) -> Option<Weekday> {
|
||||||
|
match day.to_lowercase().as_str() {
|
||||||
|
"monday" | "mon" => Some(Weekday::Mon),
|
||||||
|
"tuesday" | "tue" => Some(Weekday::Tue),
|
||||||
|
"wednesday" | "wed" => Some(Weekday::Wed),
|
||||||
|
"thursday" | "thu" => Some(Weekday::Thu),
|
||||||
|
"friday" | "fri" => Some(Weekday::Fri),
|
||||||
|
"saturday" | "sat" => Some(Weekday::Sat),
|
||||||
|
"sunday" | "sun" => Some(Weekday::Sun),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_compute_end_date_standard_week() {
|
||||||
|
let calendar = WorkCalendar::new();
|
||||||
|
let start_date = NaiveDate::from_ymd_opt(2023, 8, 21).unwrap(); // Monday
|
||||||
|
let (end_date, duration) = calendar.compute_end_date(start_date, 5).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(end_date, NaiveDate::from_ymd_opt(2023, 8, 25).unwrap()); // Friday
|
||||||
|
assert_eq!(duration.num_days(), 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_compute_end_date_with_weekend() {
|
||||||
|
let calendar = WorkCalendar::new();
|
||||||
|
let start_date = NaiveDate::from_ymd_opt(2023, 8, 18).unwrap(); // Friday
|
||||||
|
let (end_date, duration) = calendar.compute_end_date(start_date, 5).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(end_date, NaiveDate::from_ymd_opt(2023, 8, 24).unwrap()); // Next Thursday
|
||||||
|
assert_eq!(duration.num_days(), 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_compute_end_date_with_holiday() {
|
||||||
|
let mut calendar = WorkCalendar::new();
|
||||||
|
calendar.add_holiday(NaiveDate::from_ymd_opt(2023, 8, 23).unwrap()); // Wednesday
|
||||||
|
let start_date = NaiveDate::from_ymd_opt(2023, 8, 21).unwrap(); // Monday
|
||||||
|
let (end_date, duration) = calendar.compute_end_date(start_date, 5).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(end_date, NaiveDate::from_ymd_opt(2023, 8, 28).unwrap()); // Next Monday
|
||||||
|
assert_eq!(duration.num_days(), 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_compute_end_date_custom_work_days() {
|
||||||
|
let mut calendar = WorkCalendar::new();
|
||||||
|
calendar.set_work_days("Monday,Wednesday,Friday").unwrap();
|
||||||
|
let start_date = NaiveDate::from_ymd_opt(2023, 8, 21).unwrap(); // Monday
|
||||||
|
let (end_date, duration) = calendar.compute_end_date(start_date, 5).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(end_date, NaiveDate::from_ymd_opt(2023, 8, 30).unwrap()); // Wednesday
|
||||||
|
assert_eq!(duration.num_days(), 9);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_compute_end_date_zero_days() {
|
||||||
|
let calendar = WorkCalendar::new();
|
||||||
|
let start_date = NaiveDate::from_ymd_opt(2023, 8, 21).unwrap(); // Monday
|
||||||
|
let (end_date, duration) = calendar.compute_end_date(start_date, 0).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(end_date, start_date);
|
||||||
|
assert_eq!(duration.num_days(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_compute_end_date_negative_days() {
|
||||||
|
let calendar = WorkCalendar::new();
|
||||||
|
let start_date = NaiveDate::from_ymd_opt(2023, 8, 21).unwrap(); // Monday
|
||||||
|
assert!(calendar.compute_end_date(start_date, -1).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_set_work_days() {
|
||||||
|
let mut calendar = WorkCalendar::new();
|
||||||
|
assert!(calendar.set_work_days("Mon,Wed,Fri").is_ok());
|
||||||
|
assert!(calendar.is_work_day(&Weekday::Mon));
|
||||||
|
assert!(calendar.is_work_day(&Weekday::Wed));
|
||||||
|
assert!(calendar.is_work_day(&Weekday::Fri));
|
||||||
|
assert!(!calendar.is_work_day(&Weekday::Tue));
|
||||||
|
assert!(!calendar.is_work_day(&Weekday::Thu));
|
||||||
|
assert!(!calendar.is_work_day(&Weekday::Sat));
|
||||||
|
assert!(!calendar.is_work_day(&Weekday::Sun));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_add_and_remove_holiday() {
|
||||||
|
let mut calendar = WorkCalendar::new();
|
||||||
|
let holiday = NaiveDate::from_ymd_opt(2023, 12, 25).unwrap();
|
||||||
|
calendar.add_holiday(holiday);
|
||||||
|
assert!(calendar.is_holiday(&holiday));
|
||||||
|
calendar.remove_holiday(&holiday);
|
||||||
|
assert!(!calendar.is_holiday(&holiday));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_work_days_between() {
|
||||||
|
let calendar = WorkCalendar::new();
|
||||||
|
let start_date = NaiveDate::from_ymd_opt(2023, 8, 21).unwrap(); // Monday
|
||||||
|
let end_date = NaiveDate::from_ymd_opt(2023, 8, 25).unwrap(); // Friday
|
||||||
|
assert_eq!(calendar.work_days_between(start_date, end_date), 5);
|
||||||
|
|
||||||
|
let end_date = NaiveDate::from_ymd_opt(2023, 8, 27).unwrap(); // Sunday
|
||||||
|
assert_eq!(calendar.work_days_between(start_date, end_date), 5);
|
||||||
|
|
||||||
|
let mut calendar = WorkCalendar::new();
|
||||||
|
calendar.add_holiday(NaiveDate::from_ymd_opt(2023, 8, 23).unwrap()); // Wednesday
|
||||||
|
assert_eq!(calendar.work_days_between(start_date, end_date), 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_from_str_yaml() {
|
||||||
|
let config = r#"
|
||||||
|
work_days:
|
||||||
|
- Monday
|
||||||
|
- Wednesday
|
||||||
|
- Friday
|
||||||
|
holidays:
|
||||||
|
- 2023-12-25
|
||||||
|
"#;
|
||||||
|
let calendar = WorkCalendar::from_str(config).unwrap();
|
||||||
|
assert!(calendar.is_work_day(&Weekday::Mon));
|
||||||
|
assert!(calendar.is_work_day(&Weekday::Wed));
|
||||||
|
assert!(calendar.is_work_day(&Weekday::Fri));
|
||||||
|
assert!(!calendar.is_work_day(&Weekday::Tue));
|
||||||
|
assert!(calendar.is_holiday(&NaiveDate::from_ymd_opt(2023, 12, 25).unwrap()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_from_str_json() {
|
||||||
|
let config = r#"
|
||||||
|
{
|
||||||
|
"work_days": ["Monday", "Wednesday", "Friday"],
|
||||||
|
"holidays": ["2023-12-25"]
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
let calendar = WorkCalendar::from_str(config).unwrap();
|
||||||
|
assert!(calendar.is_work_day(&Weekday::Mon));
|
||||||
|
assert!(calendar.is_work_day(&Weekday::Wed));
|
||||||
|
assert!(calendar.is_work_day(&Weekday::Fri));
|
||||||
|
assert!(!calendar.is_work_day(&Weekday::Tue));
|
||||||
|
assert!(calendar.is_holiday(&NaiveDate::from_ymd_opt(2023, 12, 25).unwrap()));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user