feat: add support for Quartz special characters L, W, and ##16
Conversation
Closes #4 - L in day-of-month: "0 0 L * ?" → "Last day of the month at 12:00 AM" - LW in day-of-month: "0 0 LW * ?" → "Last weekday of the month at 12:00 AM" - W modifier: "0 0 15W * ?" → "Nearest weekday to the 15th of the month at 12:00 AM" - # in day-of-week: "0 0 ? * 2#1" → "Every first Monday of the month at 12:00 AM" - L suffix on day-of-week: "0 0 ? * 2L" → "Last Monday of the month at 12:00 AM" Quartz day-of-week is 1-based (1=Sun…7=Sat); converted to 0-based before looking up in daysMap so day names resolve correctly across all DayNameFormat modes. 10 new tests covering all special character combinations including 6-part Quartz expressions.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthrough
ChangesQuartz Special Character Support (L, W, #)
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Possibly related issues
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
Adds human-readable support for Quartz-specific day-field modifiers (L, W, #) in CronHelper.BuildHumanReadable, with accompanying unit tests to validate the new descriptions.
Changes:
- Added early-detection handling for Quartz
#(nth weekday), day-of-weekxL(last weekday-of-month), and day-of-monthL,LW,xWpatterns. - Introduced
DescribeNthWeekdayhelper to formatx#yday-of-week expressions. - Added MSTest coverage for all newly supported Quartz patterns (including 6-part Quartz input).
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| CronCraft/Extensions/CronHelper.cs | Adds special-case parsing/formatting for Quartz L, W, and # modifiers and a helper for # descriptions. |
| CronCraft.Test/CronHelperTest.cs | Adds unit tests covering the new Quartz modifier behaviors and a Quartz 6-part # scenario. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| string occurrence = hashParts[1] switch | ||
| { | ||
| "1" => "first", | ||
| "2" => "second", | ||
| "3" => "third", | ||
| "4" => "fourth", | ||
| "5" => "fifth", | ||
| _ => $"{hashParts[1]}th" | ||
| }; |
| // day-of-week: 2L → "Last Monday of the month" (Quartz 1-based, convert to 0-based) | ||
| if (dayOfWeek.EndsWith('L') && dayOfWeek != "L") | ||
| { | ||
| var quartzDay = int.TryParse(dayOfWeek.TrimEnd('L'), out var qd) ? qd : -1; | ||
| var standardDay = (quartzDay - 1).ToString(); | ||
| var dayName = daysMap.TryGetValue(standardDay, out var n) ? n : $"Day {standardDay}"; | ||
| return $"Last {dayName} of the month {Phrase("AtTime", time)}"; | ||
| } |
| // day-of-month: LW → "Last weekday of the month" | ||
| if (dayOfMonth.Equals("LW", StringComparison.OrdinalIgnoreCase)) | ||
| return $"Last weekday of the month {Phrase("AtTime", time)}"; | ||
|
|
||
| // day-of-month: L → "Last day of the month" | ||
| if (dayOfMonth == "L") | ||
| return $"Last day of the month {Phrase("AtTime", time)}"; | ||
|
|
||
| // day-of-month: 15W → "Nearest weekday to the 15th" | ||
| if (dayOfMonth.EndsWith('W')) | ||
| { | ||
| var baseDay = Ordinal(dayOfMonth.TrimEnd('W')); | ||
| return $"Nearest weekday to the {baseDay} of the month {Phrase("AtTime", time)}"; | ||
| } |
|
🎉 Thank you for your contribution! This PR was included in CronCraft v1.0.5 and is now live on NuGet for everyone to use. We really appreciate the time and effort you put in — it makes the library better for the whole community! 🚀 |
|
🎉 Thank you for your contribution! This PR was included in CronCraft v1.0.6 and is now live on NuGet for everyone to use. We really appreciate the time and effort you put in — it makes the library better for the whole community! 🚀 |
Closes #4
Summary
Adds support for the three Quartz-specific day field modifiers that were previously unhandled:
0 0 L * ?0 0 LW * ?0 0 15W * ?0 0 ? * 2#10 0 ? * 6#30 0 ? * 2LImplementation notes
BuildHumanReadableand short-circuit before the standard 5-part logic, so existing behaviour is fully preserved.#andLday-of-week fields use 1-based numbering (1=Sunday … 7=Saturday), while the existingdaysMapis 0-based (0=Sunday … 6=Saturday). The conversion (quartzDay - 1) is applied before the lookup so day names resolve correctly for allDayNameFormatmodes.ConvertQuartzToCronosalready passesL/W/#values through unchanged; only a bare?is replaced with*.Test plan
Test_L_LastDayOfMonth—Lin day-of-monthTest_LW_LastWeekdayOfMonth—LWcombinationTest_W_NearestWeekday—15WmodifierTest_W_NearestWeekday_1st—1W(edge: ordinal "1st")Test_Hash_NthWeekdayOfMonth_FirstMonday—2#1Test_Hash_NthWeekdayOfMonth_ThirdFriday—6#3Test_Hash_NthWeekdayOfMonth_SecondWednesday—4#2Test_L_LastDayOfWeek_LastMonday—2LTest_L_LastDayOfWeek_LastFriday—6LTest_Hash_QuartzSixPart_FirstTuesday— 6-part Quartz with3#1Summary by CodeRabbit
New Features
L,LW,W,#) for improved human-readable output.Tests