mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 08:27:35 +00:00
cal: Make start of the week configurable
Making all the other parts of the world happier :^) Add a `--starting-day` (`-s`) option to be compatible with GNU cal, which has a similar option. The GNU option takes allows passing either an int or a day name. Let's do something similar using weekdays we already have in AK/DateConstants.h. Also add myself to the copyright header, as by now I've modified most of the lines in this file.
This commit is contained in:
parent
b4bec4dd2f
commit
114da3a275
2 changed files with 54 additions and 7 deletions
|
@ -5,7 +5,7 @@ cal - Display a calendar
|
||||||
## Synopsis
|
## Synopsis
|
||||||
|
|
||||||
```**sh
|
```**sh
|
||||||
$ cal [[month] year]
|
$ cal [--starting-day weekday] [[month] year]
|
||||||
```
|
```
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
@ -17,6 +17,10 @@ The current day is always highlighted.
|
||||||
|
|
||||||
Days, months and years are specified with numbers. Week starts at Sunday.
|
Days, months and years are specified with numbers. Week starts at Sunday.
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
* `-s`, `--starting-day`: Specify which day should start the week. Accepts either short or long weekday names or indexes (0 being Sunday).
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||||
|
* Copyright (c) 2023, Karol Baraniecki <karol@baraniecki.eu>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <AK/DateConstants.h>
|
#include <AK/DateConstants.h>
|
||||||
|
#include <AK/Find.h>
|
||||||
#include <AK/String.h>
|
#include <AK/String.h>
|
||||||
#include <AK/StringBuilder.h>
|
#include <AK/StringBuilder.h>
|
||||||
|
#include <AK/StringUtils.h>
|
||||||
#include <AK/StringView.h>
|
#include <AK/StringView.h>
|
||||||
#include <LibCore/ArgsParser.h>
|
#include <LibCore/ArgsParser.h>
|
||||||
#include <LibCore/DateTime.h>
|
#include <LibCore/DateTime.h>
|
||||||
|
@ -25,6 +28,25 @@ int current_year;
|
||||||
int current_month;
|
int current_month;
|
||||||
int current_day;
|
int current_day;
|
||||||
|
|
||||||
|
static ErrorOr<int> weekday_index(StringView weekday_name)
|
||||||
|
{
|
||||||
|
auto is_same_weekday_name = [&weekday_name](StringView other) {
|
||||||
|
return AK::StringUtils::equals_ignoring_ascii_case(weekday_name, other);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (auto it = AK::find_if(AK::long_day_names.begin(), AK::long_day_names.end(), is_same_weekday_name); !it.is_end())
|
||||||
|
return it.index();
|
||||||
|
if (auto it = AK::find_if(AK::short_day_names.begin(), AK::short_day_names.end(), is_same_weekday_name); !it.is_end())
|
||||||
|
return it.index();
|
||||||
|
if (auto it = AK::find_if(AK::mini_day_names.begin(), AK::mini_day_names.end(), is_same_weekday_name); !it.is_end())
|
||||||
|
return it.index();
|
||||||
|
|
||||||
|
if (auto numeric_weekday = AK::StringUtils::convert_to_int(weekday_name); numeric_weekday.has_value())
|
||||||
|
return numeric_weekday.value();
|
||||||
|
|
||||||
|
return Error::from_string_view(TRY(String::formatted("Unknown weekday name: '{}'", weekday_name)));
|
||||||
|
}
|
||||||
|
|
||||||
static ErrorOr<StringView> month_name(int month)
|
static ErrorOr<StringView> month_name(int month)
|
||||||
{
|
{
|
||||||
int month_index = month - 1;
|
int month_index = month - 1;
|
||||||
|
@ -35,12 +57,24 @@ static ErrorOr<StringView> month_name(int month)
|
||||||
return AK::long_month_names.at(month_index);
|
return AK::long_month_names.at(month_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ErrorOr<String> weekday_names_header(int start_of_week)
|
||||||
|
{
|
||||||
|
// Generates a header in a style of "Su Mo Tu We Th Fr Sa"
|
||||||
|
|
||||||
|
Vector<String> weekdays;
|
||||||
|
for (size_t i = 0; i < AK::mini_day_names.size(); i++) {
|
||||||
|
size_t day_index = (i + start_of_week) % mini_day_names.size();
|
||||||
|
TRY(weekdays.try_append(TRY(String::from_utf8(AK::mini_day_names.at(day_index)))));
|
||||||
|
}
|
||||||
|
return TRY(String::join(' ', weekdays));
|
||||||
|
}
|
||||||
|
|
||||||
enum class Header {
|
enum class Header {
|
||||||
MonthAndYear,
|
MonthAndYear,
|
||||||
Month,
|
Month,
|
||||||
};
|
};
|
||||||
|
|
||||||
static ErrorOr<Vector<String>> month_lines_to_print(Header header_mode, int month, int year)
|
static ErrorOr<Vector<String>> month_lines_to_print(Header header_mode, int start_of_week, int month, int year)
|
||||||
{
|
{
|
||||||
Vector<String> lines;
|
Vector<String> lines;
|
||||||
|
|
||||||
|
@ -56,12 +90,15 @@ static ErrorOr<Vector<String>> month_lines_to_print(Header header_mode, int mont
|
||||||
}
|
}
|
||||||
|
|
||||||
TRY(lines.try_append(TRY(String::formatted("{: ^{}s}", header, month_width))));
|
TRY(lines.try_append(TRY(String::formatted("{: ^{}s}", header, month_width))));
|
||||||
TRY(lines.try_append(TRY(String::from_utf8("Su Mo Tu We Th Fr Sa"sv))));
|
TRY(lines.try_append(TRY(weekday_names_header(start_of_week))));
|
||||||
|
|
||||||
auto date_time = Core::DateTime::create(year, month, 1);
|
auto date_time = Core::DateTime::create(year, month, 1);
|
||||||
int first_day_of_week_for_month = date_time.weekday();
|
int first_day_of_week_for_month = date_time.weekday();
|
||||||
int days_in_month = date_time.days_in_month();
|
int days_in_month = date_time.days_in_month();
|
||||||
|
|
||||||
|
first_day_of_week_for_month += 7 - start_of_week;
|
||||||
|
first_day_of_week_for_month %= 7;
|
||||||
|
|
||||||
Vector<String> days_in_row;
|
Vector<String> days_in_row;
|
||||||
int day = 1;
|
int day = 1;
|
||||||
for (int i = 1; day <= days_in_month; ++i) {
|
for (int i = 1; day <= days_in_month; ++i) {
|
||||||
|
@ -106,12 +143,14 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||||
|
|
||||||
int month = 0;
|
int month = 0;
|
||||||
int year = 0;
|
int year = 0;
|
||||||
|
StringView week_start_day_name {};
|
||||||
|
|
||||||
Core::ArgsParser args_parser;
|
Core::ArgsParser args_parser;
|
||||||
args_parser.set_general_help("Display a nice overview of a month or year, defaulting to the current month.");
|
args_parser.set_general_help("Display a nice overview of a month or year, defaulting to the current month.");
|
||||||
// FIXME: This should ensure one value gets parsed as just a year
|
// FIXME: This should ensure one value gets parsed as just a year
|
||||||
args_parser.add_positional_argument(month, "Month", "month", Core::ArgsParser::Required::No);
|
args_parser.add_positional_argument(month, "Month", "month", Core::ArgsParser::Required::No);
|
||||||
args_parser.add_positional_argument(year, "Year", "year", Core::ArgsParser::Required::No);
|
args_parser.add_positional_argument(year, "Year", "year", Core::ArgsParser::Required::No);
|
||||||
|
args_parser.add_option(week_start_day_name, "Day that starts the week", "starting-day", 's', "day");
|
||||||
args_parser.parse(arguments);
|
args_parser.parse(arguments);
|
||||||
|
|
||||||
time_t now = time(nullptr);
|
time_t now = time(nullptr);
|
||||||
|
@ -128,6 +167,10 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||||
|
|
||||||
bool year_mode = !month && year;
|
bool year_mode = !month && year;
|
||||||
|
|
||||||
|
int week_start_day = 0;
|
||||||
|
if (!week_start_day_name.is_empty())
|
||||||
|
week_start_day = TRY(weekday_index(week_start_day_name));
|
||||||
|
|
||||||
if (!year)
|
if (!year)
|
||||||
year = current_year;
|
year = current_year;
|
||||||
if (!month)
|
if (!month)
|
||||||
|
@ -139,13 +182,13 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||||
for (int month_index = 1; month_index < 12; ++month_index) {
|
for (int month_index = 1; month_index < 12; ++month_index) {
|
||||||
outln();
|
outln();
|
||||||
outln();
|
outln();
|
||||||
Vector<String> lines_left = TRY(month_lines_to_print(Header::Month, month_index++, year));
|
Vector<String> lines_left = TRY(month_lines_to_print(Header::Month, week_start_day, month_index++, year));
|
||||||
Vector<String> lines_center = TRY(month_lines_to_print(Header::Month, month_index++, year));
|
Vector<String> lines_center = TRY(month_lines_to_print(Header::Month, week_start_day, month_index++, year));
|
||||||
Vector<String> lines_right = TRY(month_lines_to_print(Header::Month, month_index, year));
|
Vector<String> lines_right = TRY(month_lines_to_print(Header::Month, week_start_day, month_index, year));
|
||||||
print_months_side_by_side(lines_left, lines_center, lines_right);
|
print_months_side_by_side(lines_left, lines_center, lines_right);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Vector<String> lines = TRY(month_lines_to_print(Header::MonthAndYear, month, year));
|
Vector<String> lines = TRY(month_lines_to_print(Header::MonthAndYear, week_start_day, month, year));
|
||||||
for (String const& line : lines) {
|
for (String const& line : lines) {
|
||||||
outln("{}", line);
|
outln("{}", line);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue