|
| 1 | +const std = @import("std"); |
| 2 | + |
| 3 | +pub const Month = enum { |
| 4 | + january, |
| 5 | + february, |
| 6 | + march, |
| 7 | + april, |
| 8 | + may, |
| 9 | + june, |
| 10 | + july, |
| 11 | + august, |
| 12 | + september, |
| 13 | + october, |
| 14 | + november, |
| 15 | + december, |
| 16 | +}; |
| 17 | + |
| 18 | +pub const Week = enum { |
| 19 | + first, |
| 20 | + second, |
| 21 | + third, |
| 22 | + fourth, |
| 23 | + teenth, |
| 24 | + last, |
| 25 | +}; |
| 26 | + |
| 27 | +pub const DayOfWeek = enum { |
| 28 | + monday, |
| 29 | + tuesday, |
| 30 | + wednesday, |
| 31 | + thursday, |
| 32 | + friday, |
| 33 | + saturday, |
| 34 | + sunday, |
| 35 | +}; |
| 36 | + |
| 37 | +fn isLeapYear(year: u12) bool { |
| 38 | + return (year % 4 == 0) and ((year % 100 != 0) or (year % 400 == 0)); |
| 39 | +} |
| 40 | + |
| 41 | +fn daysInMonth(year: u12, month: Month) u6 { |
| 42 | + return switch (month) { |
| 43 | + .january => 31, |
| 44 | + .february => if (isLeapYear(year)) 29 else 28, |
| 45 | + .march => 31, |
| 46 | + .april => 30, |
| 47 | + .may => 31, |
| 48 | + .june => 30, |
| 49 | + .july => 31, |
| 50 | + .august => 31, |
| 51 | + .september => 30, |
| 52 | + .october => 31, |
| 53 | + .november => 30, |
| 54 | + .december => 31, |
| 55 | + }; |
| 56 | +} |
| 57 | + |
| 58 | +fn weekConcludes(year: u12, month: Month, week: Week) u6 { |
| 59 | + return switch (week) { |
| 60 | + .first => 7, |
| 61 | + .second => 14, |
| 62 | + .third => 21, |
| 63 | + .fourth => 28, |
| 64 | + .teenth => 19, |
| 65 | + .last => daysInMonth(year, month), |
| 66 | + }; |
| 67 | +} |
| 68 | + |
| 69 | +fn monthOffset(month: Month) u12 { |
| 70 | + return switch (month) { |
| 71 | + .january => 307, // offset from the end of February of previous year |
| 72 | + .february => 338, |
| 73 | + .march => 1, |
| 74 | + .april => 32, |
| 75 | + .may => 62, |
| 76 | + .june => 93, |
| 77 | + .july => 123, |
| 78 | + .august => 154, |
| 79 | + .september => 185, |
| 80 | + .october => 215, |
| 81 | + .november => 246, |
| 82 | + .december => 276, |
| 83 | + }; |
| 84 | +} |
| 85 | + |
| 86 | +fn concludingDay(year: u12, month: Month, day_of_month: u6) u12 { |
| 87 | + const y = if (month == .january or month == .february) (year - 1) else year; |
| 88 | + return (y + (y / 4) - (y / 100) + (y / 400) + monthOffset(month) + day_of_month) % 7; |
| 89 | +} |
| 90 | + |
| 91 | +pub fn meetupDayOfMonth(year: u12, month: Month, week: Week, day_of_week: DayOfWeek) u12 { |
| 92 | + const day = weekConcludes(year, month, week); |
| 93 | + const concluding = concludingDay(year, month, day); |
| 94 | + const required = @intFromEnum(day_of_week); |
| 95 | + const adjustment: u12 = if (concluding < required) 7 else 0; |
| 96 | + return day + required - (concluding + adjustment); |
| 97 | +} |
| 98 | + |
| 99 | +pub fn meetup(year: u12, month: Month, week: Week, day_of_week: DayOfWeek) [10]u8 { |
| 100 | + const day_of_month = meetupDayOfMonth(year, month, week, day_of_week); |
| 101 | + |
| 102 | + var buffer: [10]u8 = undefined; |
| 103 | + _ = std.fmt.bufPrint(&buffer, "{d:0>4}-{d:0>2}-{d:0>2}", .{ year, 1 + @intFromEnum(month), day_of_month }) catch unreachable; |
| 104 | + return buffer; |
| 105 | +} |
0 commit comments