Skip to content

feat: add support for Quartz special characters L, W, and ##16

Merged
teesofttech merged 1 commit into
masterfrom
fix/issue-4-quartz-special-characters
Jun 15, 2026
Merged

feat: add support for Quartz special characters L, W, and ##16
teesofttech merged 1 commit into
masterfrom
fix/issue-4-quartz-special-characters

Conversation

@teesofttech

@teesofttech teesofttech commented Jun 15, 2026

Copy link
Copy Markdown
Owner

Closes #4

Summary

Adds support for the three Quartz-specific day field modifiers that were previously unhandled:

Expression Output
0 0 L * ? Last day of the month at 12:00 AM
0 0 LW * ? Last weekday of the month at 12:00 AM
0 0 15W * ? Nearest weekday to the 15th of the month at 12:00 AM
0 0 ? * 2#1 Every first Monday of the month at 12:00 AM
0 0 ? * 6#3 Every third Friday of the month at 12:00 AM
0 0 ? * 2L Last Monday of the month at 12:00 AM

Implementation notes

  • All special-character patterns are detected early in BuildHumanReadable and short-circuit before the standard 5-part logic, so existing behaviour is fully preserved.
  • Quartz # and L day-of-week fields use 1-based numbering (1=Sunday … 7=Saturday), while the existing daysMap is 0-based (0=Sunday … 6=Saturday). The conversion (quartzDay - 1) is applied before the lookup so day names resolve correctly for all DayNameFormat modes.
  • ConvertQuartzToCronos already passes L/W/# values through unchanged; only a bare ? is replaced with *.

Test plan

  • Test_L_LastDayOfMonthL in day-of-month
  • Test_LW_LastWeekdayOfMonthLW combination
  • Test_W_NearestWeekday15W modifier
  • Test_W_NearestWeekday_1st1W (edge: ordinal "1st")
  • Test_Hash_NthWeekdayOfMonth_FirstMonday2#1
  • Test_Hash_NthWeekdayOfMonth_ThirdFriday6#3
  • Test_Hash_NthWeekdayOfMonth_SecondWednesday4#2
  • Test_L_LastDayOfWeek_LastMonday2L
  • Test_L_LastDayOfWeek_LastFriday6L
  • Test_Hash_QuartzSixPart_FirstTuesday — 6-part Quartz with 3#1
  • All 18 pre-existing tests continue to pass (28/28 total)

Summary by CodeRabbit

  • New Features

    • Enhanced cron expression interpretation to support Quartz-specific special characters (L, LW, W, #) for improved human-readable output.
  • Tests

    • Added comprehensive test coverage for Quartz cron patterns including last-day-of-month, last-weekday, nearest-weekday, and nth-weekday-of-month scenarios.

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.
Copilot AI review requested due to automatic review settings June 15, 2026 12:23
@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1cd641d6-e2c0-4ba7-ad34-cd0d9a45a70f

📥 Commits

Reviewing files that changed from the base of the PR and between fe145c0 and f18d018.

📒 Files selected for processing (2)
  • CronCraft.Test/CronHelperTest.cs
  • CronCraft/Extensions/CronHelper.cs

📝 Walkthrough

Walkthrough

CronHelper.BuildHumanReadable gains five new early-exit branches handling Quartz-specific patterns (#, L-weekday suffix, LW, L, and nW day-of-month). A private DescribeNthWeekday helper converts Quartz {day}#{occurrence} syntax to ordinal phrases. ConvertQuartzToCronos receives a clarifying comment. Ten new MSTest methods validate all new patterns.

Changes

Quartz Special Character Support (L, W, #)

Layer / File(s) Summary
BuildHumanReadable special-case branches and DescribeNthWeekday helper
CronCraft/Extensions/CronHelper.cs
Adds early-return branches in BuildHumanReadable for # (nth weekday), L-suffix (last weekday of week), LW (last weekday of month), L (last day of month), and nW (nearest weekday), each producing a string through AtTime. Adds private DescribeNthWeekday that parses {day}#{occurrence} into ordinal weekday phrases via daysMap. Adds a comment in ConvertQuartzToCronos clarifying that only bare ? is replaced while L/#-suffixed values are preserved.
MSTest coverage for all new Quartz patterns
CronCraft.Test/CronHelperTest.cs
Adds 10 new test methods asserting exact human-readable output for L, LW, W, #, L-weekday-suffix, and 6-part Quartz expressions, covering both default and full DayNameFormat settings.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related issues

Poem

🐇 Hop, hop, the schedule's set!
Last day, last week — no sweat!
L and W and # too,
Quartz patterns finally breaking through.
Every first Monday? Done with flair —
CronCraft now reads crons with care! 🌟

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main feature: adding support for three Quartz special characters (L, W, #) that are central to this PR's changes.
Linked Issues check ✅ Passed The PR implementation addresses all coding requirements from #4: L in day-of-month, W modifier, # in day-of-week, LW combination, and L suffix on day-of-week fields with 10 comprehensive unit tests.
Out of Scope Changes check ✅ Passed All changes are scoped to requirements from #4: test additions in CronHelperTest.cs and enhancements to CronHelper.cs's BuildHumanReadable and ConvertQuartzToCronos methods as specified.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/issue-4-quartz-special-characters

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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-week xL (last weekday-of-month), and day-of-month L, LW, xW patterns.
  • Introduced DescribeNthWeekday helper to format x#y day-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.

Comment on lines +177 to +185
string occurrence = hashParts[1] switch
{
"1" => "first",
"2" => "second",
"3" => "third",
"4" => "fourth",
"5" => "fifth",
_ => $"{hashParts[1]}th"
};
Comment on lines +110 to +117
// 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)}";
}
Comment on lines +119 to +132
// 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)}";
}
@teesofttech
teesofttech merged commit 4610fbe into master Jun 15, 2026
4 checks passed
@github-actions

Copy link
Copy Markdown

🎉 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! 🚀

dotnet add package CronCraft --version 1.0.5

@github-actions

Copy link
Copy Markdown

🎉 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! 🚀

dotnet add package CronCraft --version 1.0.6

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for Quartz special characters: L (last), W (weekday), # (nth)

2 participants