Changes
4 changed files (+157/-13)
-
-
@@ -26,26 +26,21 @@ sunwait-report - Prints detailed report of sunrise and sunset times for the date== Synposis *sunwait* *report* [*y* _<years-since-2000>_] [*m* _<month>_] [*d* _<day-of-month>_] *sunwait* *report* [*--date* _<YYYY-MM-DD>_] == Description This command prints detailed report on sunrise, sunset and other sun-related times for the date. It by default targets current date, but you can override year, month, and/or day of month individually. == Options *y* _<years-since-2000>_:: Years since 2000. 0 to 99. Defaults to the current year - 2000. *--date* _<YYYY-MM-DD>_:: Calendar date to generate report for. Defaults to the current date. *m* _<month>_:: Month. 1 to 12. Defaults to the current month. *d* _<day-of-month>_:: Day of the month. 1 to 31. Defaults to the current date's day of the month. + Each component must be 0 padded. Missing a digit is a format error. == Environment
-
@@ -60,7 +55,7 @@ Produce a report of the different sunrises and sunsets on 2022/03/15 for Christmas Island, Australia.[,shell] ---- sunwait report y 20 m 3 d 15 --lat 10.49S --lon 105.55E sunwait report --date 2022-03-15 --lat 10.49S --lon 105.55E ---- == See also
-
-
-
@@ -17,6 +17,7 @@ // SPDX-License-Identifier: GPL-3.0-onlyconst std = @import("std"); const CalendarDate = @import("./RunOptions/date.zig").CalendarDate; const EventType = @import("./RunOptions/event.zig").EventType; const ParseArgsError = @import("./RunOptions/parser.zig").ParseArgsError; const TwilightAngle = @import("./RunOptions/twilight.zig").TwilightAngle;
-
@@ -40,6 +41,30 @@ month: ?u4 = null,year_since_2000: ?c_int = null, pub fn parseArg(self: *@This(), arg: []const u8, args: *std.process.ArgIterator) ParseArgsError!void { if (std.mem.eql(u8, "--date", arg)) { const next = args.next() orelse { std.log.err("{s} option requires a value", .{arg}); return ParseArgsError.MissingValue; }; const date = CalendarDate.fromString(next) catch |err| { std.log.err("\"{s}\" is not a valid date string: {s}", .{ next, @errorName(err) }); return ParseArgsError.InvalidDateFormat; }; self.year_since_2000 = @as(c_int, date.year) - 2000; self.month = date.month; self.day_of_month = date.day; return; } if (CalendarDate.fromString(arg)) |date| { self.year_since_2000 = @as(c_int, date.year) - 2000; self.month = date.month; self.day_of_month = date.day; return; } else |_| {} if (std.mem.eql(u8, "d", arg)) { const next = args.next() orelse { std.log.err("{s} option requires a value", .{arg});
-
@@ -553,3 +578,7 @@ },.utcBiasHours = @as(f64, @floatFromInt(c.timezone)) / 60.0 / 60.0, }; } test { _ = @import("RunOptions/date.zig"); }
-
-
src/RunOptions/date.zig (new)
-
@@ -0,0 +1,119 @@// Copyright (C) 2025 Shota FUJI // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. // // SPDX-License-Identifier: GPL-3.0-only const std = @import("std"); const ParseArgsError = @import("./parser.zig").ParseArgsError; const format = "YYYY-MM-DD"; /// Timezone-free date. Each fields' size is same to ones of `std.time.epoch`, /// in case integration will be added. pub const CalendarDate = packed struct { year: u16, month: u4, day: u5, pub const ParseError = error{ InvalidFormat, MonthOutOfRange, DayOfMonthOutOfRange, }; pub fn fromString(str: []const u8) ParseError!@This() { if (str.len != format.len) { return ParseError.InvalidFormat; } if (str[4] != '-' or str[7] != '-') { return ParseError.InvalidFormat; } const month = std.fmt.parseUnsigned(u4, str[5..7], 10) catch |err| { return switch (err) { error.InvalidCharacter => ParseError.InvalidFormat, error.Overflow => ParseError.MonthOutOfRange, }; }; if (month == 0 or month > 12) { return ParseError.MonthOutOfRange; } const day = std.fmt.parseUnsigned(u5, str[8..], 10) catch |err| { return switch (err) { error.InvalidCharacter => ParseError.InvalidFormat, error.Overflow => ParseError.DayOfMonthOutOfRange, }; }; if (day == 0 or day > 31) { return ParseError.DayOfMonthOutOfRange; } return .{ .year = std.fmt.parseUnsigned(u16, str[0..4], 10) catch { return ParseError.InvalidFormat; }, .month = month, .day = day, }; } test fromString { { const d = try fromString("2000-01-01"); try std.testing.expectEqual(2000, d.year); try std.testing.expectEqual(1, d.month); try std.testing.expectEqual(1, d.day); } { const d = try fromString("1901-12-31"); try std.testing.expectEqual(1901, d.year); try std.testing.expectEqual(12, d.month); try std.testing.expectEqual(31, d.day); } { const d = try fromString("2025-07-22"); try std.testing.expectEqual(2025, d.year); try std.testing.expectEqual(7, d.month); try std.testing.expectEqual(22, d.day); } { const d = try fromString("9999-12-31"); try std.testing.expectEqual(9999, d.year); try std.testing.expectEqual(12, d.month); try std.testing.expectEqual(31, d.day); } try std.testing.expectError(ParseError.InvalidFormat, fromString("1970-1-1")); try std.testing.expectError(ParseError.InvalidFormat, fromString("1970-01-1")); try std.testing.expectError(ParseError.InvalidFormat, fromString("1970-1-01")); try std.testing.expectError(ParseError.InvalidFormat, fromString("70-01-01")); try std.testing.expectError(ParseError.MonthOutOfRange, fromString("1970-00-01")); try std.testing.expectError(ParseError.MonthOutOfRange, fromString("1970-13-01")); try std.testing.expectError(ParseError.DayOfMonthOutOfRange, fromString("1970-08-00")); try std.testing.expectError(ParseError.DayOfMonthOutOfRange, fromString("1970-08-32")); } }; test { _ = CalendarDate; }
-
-
-
@@ -26,4 +26,5 @@ InvalidYearSince2000,InvalidEventType, InvalidAngle, InvalidOffset, InvalidDateFormat, };
-