From f5e03aaf1c67c2a2b06cf863ce47b0a49c1809d8 Mon Sep 17 00:00:00 2001 From: "Joseph T. Lyons" Date: Sat, 9 May 2020 19:05:48 -0400 Subject: [PATCH] Add cal command (#1739) * Add cal command * Fix docmentation to show commands on two lines * Use bullet points on flag documentation for cal * Dereference day before calling string method * Silence Clippy warning regarding a function with too many arguments * Update cal flag descriptions and documentation * Add some tests for the cal command --- crates/nu-cli/src/cli.rs | 1 + crates/nu-cli/src/commands.rs | 2 + crates/nu-cli/src/commands/cal.rs | 261 ++++++++++++++++++++++++++++ crates/nu-cli/tests/commands/cal.rs | 39 +++++ crates/nu-cli/tests/commands/mod.rs | 1 + docs/commands/cal.md | 188 ++++++++++++++++++++ 6 files changed, 492 insertions(+) create mode 100644 crates/nu-cli/src/commands/cal.rs create mode 100644 crates/nu-cli/tests/commands/cal.rs create mode 100644 docs/commands/cal.md diff --git a/crates/nu-cli/src/cli.rs b/crates/nu-cli/src/cli.rs index 30b5ee14bc..65f42d9158 100644 --- a/crates/nu-cli/src/cli.rs +++ b/crates/nu-cli/src/cli.rs @@ -251,6 +251,7 @@ pub fn create_default_context( whole_stream_command(Touch), whole_stream_command(Cpy), whole_stream_command(Date), + whole_stream_command(Cal), whole_stream_command(Calc), whole_stream_command(Mkdir), whole_stream_command(Move), diff --git a/crates/nu-cli/src/commands.rs b/crates/nu-cli/src/commands.rs index 6c583dfeee..d12ac42fd2 100644 --- a/crates/nu-cli/src/commands.rs +++ b/crates/nu-cli/src/commands.rs @@ -8,6 +8,7 @@ pub(crate) mod alias; pub(crate) mod append; pub(crate) mod args; pub(crate) mod autoview; +pub(crate) mod cal; pub(crate) mod calc; pub(crate) mod cd; pub(crate) mod classified; @@ -128,6 +129,7 @@ pub(crate) use command::{whole_stream_command, Command, UnevaluatedCallInfo, Who pub(crate) use alias::Alias; pub(crate) use append::Append; +pub(crate) use cal::Cal; pub(crate) use calc::Calc; pub(crate) use compact::Compact; pub(crate) use config::Config; diff --git a/crates/nu-cli/src/commands/cal.rs b/crates/nu-cli/src/commands/cal.rs new file mode 100644 index 0000000000..2c0f41b1c8 --- /dev/null +++ b/crates/nu-cli/src/commands/cal.rs @@ -0,0 +1,261 @@ +use crate::prelude::*; +use chrono::{DateTime, Datelike, Local, NaiveDate}; +use nu_errors::ShellError; +use nu_protocol::Dictionary; + +use crate::commands::WholeStreamCommand; +use indexmap::IndexMap; +use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value}; + +pub struct Cal; + +impl WholeStreamCommand for Cal { + fn name(&self) -> &str { + "cal" + } + + fn signature(&self) -> Signature { + Signature::build("cal") + .switch( + "month-names", + "Display the month names instead of integers", + Some('m'), + ) + .named( + "year", + SyntaxShape::Int, + "Display a year-long calendar for the specified year", + Some('y'), + ) + } + + fn usage(&self) -> &str { + "Display a calendar." + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + cal(args, registry) + } +} + +pub fn cal(args: CommandArgs, registry: &CommandRegistry) -> Result { + let args = args.evaluate_once(registry)?; + let mut calendar_vec_deque = VecDeque::new(); + let tag = args.call_info.name_tag.clone(); + + let (current_year, current_month, current_day) = get_current_date(); + let should_show_month_names = args.has("month-names"); + + if args.has("year") { + let mut day_value: Option = Some(current_day); + let year_option = args.get("year"); + let mut year_value = current_year as u64; + + if let Some(year) = year_option { + if let Ok(year_u64) = year.as_u64() { + year_value = year_u64; + } + + if year_value != current_year as u64 { + day_value = None + } + } + + add_year_to_table( + &mut calendar_vec_deque, + &tag, + year_value as i32, + current_year, + current_month, + day_value, + should_show_month_names, + ); + } else { + let (day_start_offset, number_of_days_in_month, _) = + get_month_information(current_year, current_month, current_year); + + add_month_to_table( + &mut calendar_vec_deque, + &tag, + current_year, + current_month, + Some(current_day), + day_start_offset, + number_of_days_in_month as usize, + should_show_month_names, + ); + } + + Ok(futures::stream::iter(calendar_vec_deque).to_output_stream()) +} + +fn get_current_date() -> (i32, u32, u32) { + let local_now: DateTime = Local::now(); + + let current_year: i32 = local_now.date().year(); + let current_month: u32 = local_now.date().month(); + let current_day: u32 = local_now.date().day(); + + (current_year, current_month, current_day) +} + +fn add_year_to_table( + mut calendar_vec_deque: &mut VecDeque, + tag: &Tag, + mut selected_year: i32, + current_year: i32, + current_month: u32, + current_day_option: Option, + should_show_month_names: bool, +) { + for month_number in 1..=12 { + let (day_start_offset, number_of_days_in_month, chosen_date_is_valid) = + get_month_information(selected_year, month_number, current_year); + + if !chosen_date_is_valid { + selected_year = current_year; + } + + let mut new_current_day_option: Option = None; + + if let Some(current_day) = current_day_option { + if month_number == current_month { + new_current_day_option = Some(current_day) + } + } + + add_month_to_table( + &mut calendar_vec_deque, + &tag, + selected_year, + month_number, + new_current_day_option, + day_start_offset, + number_of_days_in_month, + should_show_month_names, + ); + } +} + +#[allow(clippy::too_many_arguments)] +fn add_month_to_table( + calendar_vec_deque: &mut VecDeque, + tag: &Tag, + year: i32, + month: u32, + _current_day_option: Option, // Can be used in the future to display current day + day_start_offset: usize, + number_of_days_in_month: usize, + should_show_month_names: bool, +) { + let day_limit = number_of_days_in_month + day_start_offset; + let mut day_count: usize = 1; + + let days_of_the_week = [ + "sunday", + "monday", + "tuesday", + "wednesday", + "thurday", + "friday", + "saturday", + ]; + + while day_count <= day_limit { + let mut indexmap = IndexMap::new(); + + indexmap.insert("year".to_string(), UntaggedValue::int(year).into_value(tag)); + + let month_value = if should_show_month_names { + UntaggedValue::string(get_month_name(month)).into_value(tag) + } else { + UntaggedValue::int(month).into_value(tag) + }; + + indexmap.insert("month".to_string(), month_value); + + for day in &days_of_the_week { + let value = if (day_count <= day_limit) && (day_count > day_start_offset) { + UntaggedValue::int(day_count - day_start_offset).into_value(tag) + } else { + UntaggedValue::nothing().into_value(tag) + }; + + indexmap.insert((*day).to_string(), value); + + day_count += 1; + } + + calendar_vec_deque + .push_back(UntaggedValue::Row(Dictionary::from(indexmap)).into_value(tag)); + } +} + +fn get_month_name(month_number: u32) -> String { + let month_name = match month_number { + 1 => "january", + 2 => "february", + 3 => "march", + 4 => "april", + 5 => "may", + 6 => "june", + 7 => "july", + 8 => "august", + 9 => "september", + 10 => "october", + 11 => "november", + _ => "december", + }; + + month_name.to_string() +} + +fn get_month_information( + selected_year: i32, + month: u32, + current_year: i32, +) -> (usize, usize, bool) { + let (naive_date, chosen_date_is_valid_one) = + get_safe_naive_date(selected_year, month, current_year); + let weekday = naive_date.weekday(); + let (days_in_month, chosen_date_is_valid_two) = + get_days_in_month(selected_year, month, current_year); + + ( + weekday.num_days_from_sunday() as usize, + days_in_month, + chosen_date_is_valid_one && chosen_date_is_valid_two, + ) +} + +fn get_days_in_month(selected_year: i32, month: u32, current_year: i32) -> (usize, bool) { + // Chrono does not provide a method to output the amount of days in a month + // This is a workaround taken from the example code from the Chrono docs here: + // https://docs.rs/chrono/0.3.0/chrono/naive/date/struct.NaiveDate.html#example-30 + let (adjusted_year, adjusted_month) = if month == 12 { + (selected_year + 1, 1) + } else { + (selected_year, month + 1) + }; + + let (naive_date, chosen_date_is_valid) = + get_safe_naive_date(adjusted_year, adjusted_month, current_year); + + (naive_date.pred().day() as usize, chosen_date_is_valid) +} + +fn get_safe_naive_date( + selected_year: i32, + selected_month: u32, + current_year: i32, +) -> (NaiveDate, bool) { + if let Some(naive_date) = NaiveDate::from_ymd_opt(selected_year, selected_month, 1) { + return (naive_date, true); + } + + (NaiveDate::from_ymd(current_year, selected_month, 1), false) +} diff --git a/crates/nu-cli/tests/commands/cal.rs b/crates/nu-cli/tests/commands/cal.rs new file mode 100644 index 0000000000..16a0d7640c --- /dev/null +++ b/crates/nu-cli/tests/commands/cal.rs @@ -0,0 +1,39 @@ +use nu_test_support::{nu, pipeline}; + +#[test] +fn cal_february_2020_leap_year() { + let actual = nu!( + cwd: ".", pipeline( + r#" + cal -my 2020 | where month == "february" | to json + "# + )); + + let cal_february_json = r#"[{"year":2020,"month":"february","sunday":null,"monday":null,"tuesday":null,"wednesday":null,"thurday":null,"friday":null,"saturday":1},{"year":2020,"month":"february","sunday":2,"monday":3,"tuesday":4,"wednesday":5,"thurday":6,"friday":7,"saturday":8},{"year":2020,"month":"february","sunday":9,"monday":10,"tuesday":11,"wednesday":12,"thurday":13,"friday":14,"saturday":15},{"year":2020,"month":"february","sunday":16,"monday":17,"tuesday":18,"wednesday":19,"thurday":20,"friday":21,"saturday":22},{"year":2020,"month":"february","sunday":23,"monday":24,"tuesday":25,"wednesday":26,"thurday":27,"friday":28,"saturday":29}]"#; + + assert_eq!(actual.out, cal_february_json); +} + +#[test] +fn cal_friday_the_thirteenths_in_2015() { + let actual = nu!( + cwd: ".", pipeline( + r#" + cal -ym 2015 | where friday == 13 | count + "# + )); + + assert!(actual.out.contains("3")); +} + +#[test] +fn cal_rows_in_2020() { + let actual = nu!( + cwd: ".", pipeline( + r#" + cal -y 2020 | count + "# + )); + + assert!(actual.out.contains("62")); +} diff --git a/crates/nu-cli/tests/commands/mod.rs b/crates/nu-cli/tests/commands/mod.rs index dc6d8db174..a24eaea2f9 100644 --- a/crates/nu-cli/tests/commands/mod.rs +++ b/crates/nu-cli/tests/commands/mod.rs @@ -1,5 +1,6 @@ mod alias; mod append; +mod cal; mod calc; mod cd; mod compact; diff --git a/docs/commands/cal.md b/docs/commands/cal.md new file mode 100644 index 0000000000..1d52349f5e --- /dev/null +++ b/docs/commands/cal.md @@ -0,0 +1,188 @@ +# cal + +Use `cal` to display a calendar. + +## Flags + +* `-m`, `--month-names`: Display the month names instead of integers +* `-y`, `--year`: Display a year-long calendar for the specified year + +## Examples + +```shell +> cal +───┬──────┬───────┬────────┬────────┬─────────┬───────────┬─────────┬────────┬────────── + # │ year │ month │ sunday │ monday │ tuesday │ wednesday │ thurday │ friday │ saturday +───┼──────┼───────┼────────┼────────┼─────────┼───────────┼─────────┼────────┼────────── + 0 │ 2020 │ 5 │ │ │ │ │ │ 1 │ 2 + 1 │ 2020 │ 5 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 + 2 │ 2020 │ 5 │ 10 │ 11 │ 12 │ 13 │ 14 │ 15 │ 16 + 3 │ 2020 │ 5 │ 17 │ 18 │ 19 │ 20 │ 21 │ 22 │ 23 + 4 │ 2020 │ 5 │ 24 │ 25 │ 26 │ 27 │ 28 │ 29 │ 30 + 5 │ 2020 │ 5 │ 31 │ │ │ │ │ │ +───┴──────┴───────┴────────┴────────┴─────────┴───────────┴─────────┴────────┴────────── +``` + +```shell +> cal -y 2020 +────┬──────┬───────┬────────┬────────┬─────────┬───────────┬─────────┬────────┬────────── + # │ year │ month │ sunday │ monday │ tuesday │ wednesday │ thurday │ friday │ saturday +────┼──────┼───────┼────────┼────────┼─────────┼───────────┼─────────┼────────┼────────── + 0 │ 2020 │ 1 │ │ │ │ 1 │ 2 │ 3 │ 4 + 1 │ 2020 │ 1 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 + 2 │ 2020 │ 1 │ 12 │ 13 │ 14 │ 15 │ 16 │ 17 │ 18 + 3 │ 2020 │ 1 │ 19 │ 20 │ 21 │ 22 │ 23 │ 24 │ 25 + 4 │ 2020 │ 1 │ 26 │ 27 │ 28 │ 29 │ 30 │ 31 │ + 5 │ 2020 │ 2 │ │ │ │ │ │ │ 1 + 6 │ 2020 │ 2 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 + 7 │ 2020 │ 2 │ 9 │ 10 │ 11 │ 12 │ 13 │ 14 │ 15 + 8 │ 2020 │ 2 │ 16 │ 17 │ 18 │ 19 │ 20 │ 21 │ 22 + 9 │ 2020 │ 2 │ 23 │ 24 │ 25 │ 26 │ 27 │ 28 │ 29 + 10 │ 2020 │ 3 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 + 11 │ 2020 │ 3 │ 8 │ 9 │ 10 │ 11 │ 12 │ 13 │ 14 + 12 │ 2020 │ 3 │ 15 │ 16 │ 17 │ 18 │ 19 │ 20 │ 21 + 13 │ 2020 │ 3 │ 22 │ 23 │ 24 │ 25 │ 26 │ 27 │ 28 + 14 │ 2020 │ 3 │ 29 │ 30 │ 31 │ │ │ │ + 15 │ 2020 │ 4 │ │ │ │ 1 │ 2 │ 3 │ 4 + 16 │ 2020 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 + 17 │ 2020 │ 4 │ 12 │ 13 │ 14 │ 15 │ 16 │ 17 │ 18 + 18 │ 2020 │ 4 │ 19 │ 20 │ 21 │ 22 │ 23 │ 24 │ 25 + 19 │ 2020 │ 4 │ 26 │ 27 │ 28 │ 29 │ 30 │ │ + 20 │ 2020 │ 5 │ │ │ │ │ │ 1 │ 2 + 21 │ 2020 │ 5 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 + 22 │ 2020 │ 5 │ 10 │ 11 │ 12 │ 13 │ 14 │ 15 │ 16 + 23 │ 2020 │ 5 │ 17 │ 18 │ 19 │ 20 │ 21 │ 22 │ 23 + 24 │ 2020 │ 5 │ 24 │ 25 │ 26 │ 27 │ 28 │ 29 │ 30 + 25 │ 2020 │ 5 │ 31 │ │ │ │ │ │ + 26 │ 2020 │ 6 │ │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 + 27 │ 2020 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │ 12 │ 13 + 28 │ 2020 │ 6 │ 14 │ 15 │ 16 │ 17 │ 18 │ 19 │ 20 + 29 │ 2020 │ 6 │ 21 │ 22 │ 23 │ 24 │ 25 │ 26 │ 27 + 30 │ 2020 │ 6 │ 28 │ 29 │ 30 │ │ │ │ + 31 │ 2020 │ 7 │ │ │ │ 1 │ 2 │ 3 │ 4 + 32 │ 2020 │ 7 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 + 33 │ 2020 │ 7 │ 12 │ 13 │ 14 │ 15 │ 16 │ 17 │ 18 + 34 │ 2020 │ 7 │ 19 │ 20 │ 21 │ 22 │ 23 │ 24 │ 25 + 35 │ 2020 │ 7 │ 26 │ 27 │ 28 │ 29 │ 30 │ 31 │ + 36 │ 2020 │ 8 │ │ │ │ │ │ │ 1 + 37 │ 2020 │ 8 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 + 38 │ 2020 │ 8 │ 9 │ 10 │ 11 │ 12 │ 13 │ 14 │ 15 + 39 │ 2020 │ 8 │ 16 │ 17 │ 18 │ 19 │ 20 │ 21 │ 22 + 40 │ 2020 │ 8 │ 23 │ 24 │ 25 │ 26 │ 27 │ 28 │ 29 + 41 │ 2020 │ 8 │ 30 │ 31 │ │ │ │ │ + 42 │ 2020 │ 9 │ │ │ 1 │ 2 │ 3 │ 4 │ 5 + 43 │ 2020 │ 9 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │ 12 + 44 │ 2020 │ 9 │ 13 │ 14 │ 15 │ 16 │ 17 │ 18 │ 19 + 45 │ 2020 │ 9 │ 20 │ 21 │ 22 │ 23 │ 24 │ 25 │ 26 + 46 │ 2020 │ 9 │ 27 │ 28 │ 29 │ 30 │ │ │ + 47 │ 2020 │ 10 │ │ │ │ │ 1 │ 2 │ 3 + 48 │ 2020 │ 10 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 + 49 │ 2020 │ 10 │ 11 │ 12 │ 13 │ 14 │ 15 │ 16 │ 17 + 50 │ 2020 │ 10 │ 18 │ 19 │ 20 │ 21 │ 22 │ 23 │ 24 + 51 │ 2020 │ 10 │ 25 │ 26 │ 27 │ 28 │ 29 │ 30 │ 31 + 52 │ 2020 │ 11 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 + 53 │ 2020 │ 11 │ 8 │ 9 │ 10 │ 11 │ 12 │ 13 │ 14 + 54 │ 2020 │ 11 │ 15 │ 16 │ 17 │ 18 │ 19 │ 20 │ 21 + 55 │ 2020 │ 11 │ 22 │ 23 │ 24 │ 25 │ 26 │ 27 │ 28 + 56 │ 2020 │ 11 │ 29 │ 30 │ │ │ │ │ + 57 │ 2020 │ 12 │ │ │ 1 │ 2 │ 3 │ 4 │ 5 + 58 │ 2020 │ 12 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │ 12 + 59 │ 2020 │ 12 │ 13 │ 14 │ 15 │ 16 │ 17 │ 18 │ 19 + 60 │ 2020 │ 12 │ 20 │ 21 │ 22 │ 23 │ 24 │ 25 │ 26 + 61 │ 2020 │ 12 │ 27 │ 28 │ 29 │ 30 │ 31 │ │ +────┴──────┴───────┴────────┴────────┴─────────┴───────────┴─────────┴────────┴────────── +``` + +```shell +> cal -my 2020 +────┬──────┬───────────┬────────┬────────┬─────────┬───────────┬─────────┬────────┬────────── + # │ year │ month │ sunday │ monday │ tuesday │ wednesday │ thurday │ friday │ saturday +────┼──────┼───────────┼────────┼────────┼─────────┼───────────┼─────────┼────────┼────────── + 0 │ 2020 │ january │ │ │ │ 1 │ 2 │ 3 │ 4 + 1 │ 2020 │ january │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 + 2 │ 2020 │ january │ 12 │ 13 │ 14 │ 15 │ 16 │ 17 │ 18 + 3 │ 2020 │ january │ 19 │ 20 │ 21 │ 22 │ 23 │ 24 │ 25 + 4 │ 2020 │ january │ 26 │ 27 │ 28 │ 29 │ 30 │ 31 │ + 5 │ 2020 │ february │ │ │ │ │ │ │ 1 + 6 │ 2020 │ february │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 + 7 │ 2020 │ february │ 9 │ 10 │ 11 │ 12 │ 13 │ 14 │ 15 + 8 │ 2020 │ february │ 16 │ 17 │ 18 │ 19 │ 20 │ 21 │ 22 + 9 │ 2020 │ february │ 23 │ 24 │ 25 │ 26 │ 27 │ 28 │ 29 + 10 │ 2020 │ march │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 + 11 │ 2020 │ march │ 8 │ 9 │ 10 │ 11 │ 12 │ 13 │ 14 + 12 │ 2020 │ march │ 15 │ 16 │ 17 │ 18 │ 19 │ 20 │ 21 + 13 │ 2020 │ march │ 22 │ 23 │ 24 │ 25 │ 26 │ 27 │ 28 + 14 │ 2020 │ march │ 29 │ 30 │ 31 │ │ │ │ + 15 │ 2020 │ april │ │ │ │ 1 │ 2 │ 3 │ 4 + 16 │ 2020 │ april │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 + 17 │ 2020 │ april │ 12 │ 13 │ 14 │ 15 │ 16 │ 17 │ 18 + 18 │ 2020 │ april │ 19 │ 20 │ 21 │ 22 │ 23 │ 24 │ 25 + 19 │ 2020 │ april │ 26 │ 27 │ 28 │ 29 │ 30 │ │ + 20 │ 2020 │ may │ │ │ │ │ │ 1 │ 2 + 21 │ 2020 │ may │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 + 22 │ 2020 │ may │ 10 │ 11 │ 12 │ 13 │ 14 │ 15 │ 16 + 23 │ 2020 │ may │ 17 │ 18 │ 19 │ 20 │ 21 │ 22 │ 23 + 24 │ 2020 │ may │ 24 │ 25 │ 26 │ 27 │ 28 │ 29 │ 30 + 25 │ 2020 │ may │ 31 │ │ │ │ │ │ + 26 │ 2020 │ june │ │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 + 27 │ 2020 │ june │ 7 │ 8 │ 9 │ 10 │ 11 │ 12 │ 13 + 28 │ 2020 │ june │ 14 │ 15 │ 16 │ 17 │ 18 │ 19 │ 20 + 29 │ 2020 │ june │ 21 │ 22 │ 23 │ 24 │ 25 │ 26 │ 27 + 30 │ 2020 │ june │ 28 │ 29 │ 30 │ │ │ │ + 31 │ 2020 │ july │ │ │ │ 1 │ 2 │ 3 │ 4 + 32 │ 2020 │ july │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 + 33 │ 2020 │ july │ 12 │ 13 │ 14 │ 15 │ 16 │ 17 │ 18 + 34 │ 2020 │ july │ 19 │ 20 │ 21 │ 22 │ 23 │ 24 │ 25 + 35 │ 2020 │ july │ 26 │ 27 │ 28 │ 29 │ 30 │ 31 │ + 36 │ 2020 │ august │ │ │ │ │ │ │ 1 + 37 │ 2020 │ august │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 + 38 │ 2020 │ august │ 9 │ 10 │ 11 │ 12 │ 13 │ 14 │ 15 + 39 │ 2020 │ august │ 16 │ 17 │ 18 │ 19 │ 20 │ 21 │ 22 + 40 │ 2020 │ august │ 23 │ 24 │ 25 │ 26 │ 27 │ 28 │ 29 + 41 │ 2020 │ august │ 30 │ 31 │ │ │ │ │ + 42 │ 2020 │ september │ │ │ 1 │ 2 │ 3 │ 4 │ 5 + 43 │ 2020 │ september │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │ 12 + 44 │ 2020 │ september │ 13 │ 14 │ 15 │ 16 │ 17 │ 18 │ 19 + 45 │ 2020 │ september │ 20 │ 21 │ 22 │ 23 │ 24 │ 25 │ 26 + 46 │ 2020 │ september │ 27 │ 28 │ 29 │ 30 │ │ │ + 47 │ 2020 │ october │ │ │ │ │ 1 │ 2 │ 3 + 48 │ 2020 │ october │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 + 49 │ 2020 │ october │ 11 │ 12 │ 13 │ 14 │ 15 │ 16 │ 17 + 50 │ 2020 │ october │ 18 │ 19 │ 20 │ 21 │ 22 │ 23 │ 24 + 51 │ 2020 │ october │ 25 │ 26 │ 27 │ 28 │ 29 │ 30 │ 31 + 52 │ 2020 │ november │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 + 53 │ 2020 │ november │ 8 │ 9 │ 10 │ 11 │ 12 │ 13 │ 14 + 54 │ 2020 │ november │ 15 │ 16 │ 17 │ 18 │ 19 │ 20 │ 21 + 55 │ 2020 │ november │ 22 │ 23 │ 24 │ 25 │ 26 │ 27 │ 28 + 56 │ 2020 │ november │ 29 │ 30 │ │ │ │ │ + 57 │ 2020 │ december │ │ │ 1 │ 2 │ 3 │ 4 │ 5 + 58 │ 2020 │ december │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │ 12 + 59 │ 2020 │ december │ 13 │ 14 │ 15 │ 16 │ 17 │ 18 │ 19 + 60 │ 2020 │ december │ 20 │ 21 │ 22 │ 23 │ 24 │ 25 │ 26 + 61 │ 2020 │ december │ 27 │ 28 │ 29 │ 30 │ 31 │ │ +────┴──────┴───────────┴────────┴────────┴─────────┴───────────┴─────────┴────────┴────────── +``` + +```shell +> cal -my 2303 | where month == "june" +───┬──────┬───────┬────────┬────────┬─────────┬───────────┬─────────┬────────┬────────── + # │ year │ month │ sunday │ monday │ tuesday │ wednesday │ thurday │ friday │ saturday +───┼──────┼───────┼────────┼────────┼─────────┼───────────┼─────────┼────────┼────────── + 0 │ 2303 │ june │ │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 + 1 │ 2303 │ june │ 7 │ 8 │ 9 │ 10 │ 11 │ 12 │ 13 + 2 │ 2303 │ june │ 14 │ 15 │ 16 │ 17 │ 18 │ 19 │ 20 + 3 │ 2303 │ june │ 21 │ 22 │ 23 │ 24 │ 25 │ 26 │ 27 + 4 │ 2303 │ june │ 28 │ 29 │ 30 │ │ │ │ +───┴──────┴───────┴────────┴────────┴─────────┴───────────┴─────────┴────────┴────────── +``` + +```shell +> cal -my 2020 | where friday == 13 +───┬──────┬──────────┬────────┬────────┬─────────┬───────────┬─────────┬────────┬────────── + # │ year │ month │ sunday │ monday │ tuesday │ wednesday │ thurday │ friday │ saturday +───┼──────┼──────────┼────────┼────────┼─────────┼───────────┼─────────┼────────┼────────── + 0 │ 2020 │ march │ 8 │ 9 │ 10 │ 11 │ 12 │ 13 │ 14 + 1 │ 2020 │ november │ 8 │ 9 │ 10 │ 11 │ 12 │ 13 │ 14 +───┴──────┴──────────┴────────┴────────┴─────────┴───────────┴─────────┴────────┴────────── + +```