SGU.Tools_CronExpressionBuilder 1.1.4

dotnet add package SGU.Tools_CronExpressionBuilder --version 1.1.4
                    
NuGet\Install-Package SGU.Tools_CronExpressionBuilder -Version 1.1.4
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="SGU.Tools_CronExpressionBuilder" Version="1.1.4" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="SGU.Tools_CronExpressionBuilder" Version="1.1.4" />
                    
Directory.Packages.props
<PackageReference Include="SGU.Tools_CronExpressionBuilder" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add SGU.Tools_CronExpressionBuilder --version 1.1.4
                    
#r "nuget: SGU.Tools_CronExpressionBuilder, 1.1.4"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package SGU.Tools_CronExpressionBuilder@1.1.4
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=SGU.Tools_CronExpressionBuilder&version=1.1.4
                    
Install as a Cake Addin
#tool nuget:?package=SGU.Tools_CronExpressionBuilder&version=1.1.4
                    
Install as a Cake Tool

SGU.Tools_CronExpressionBuilder

NuGet Version

Buy Me a Coffee

A comprehensive WinForms control for visually building and editing Quartz and Hangfire CRON expressions with an intuitive tabbed interface.


🎯 Live Demo Application

Want to see all features in action? The package includes a Demo Application in the source code!

Features in Demo

  • ✅ Interactive testing of all tab options
  • ✅ Quick test expressions (Every second, Daily at 9 AM, Weekdays, etc.)
  • ✅ Parse and edit existing expressions
  • ✅ Format selector to switch between Quartz/Hangfire
  • ✅ Expression validation and testing
  • ✅ Copy to clipboard functionality
  • ✅ Field-by-field breakdown display

Perfect for: Evaluating the control before integrating it into your project!

Demo Project - GitHub(https://github.com/Solutions-Group-Unlimited/SGU_Tools_CronExpressionBuilder)

-

Features

Visual CRON Expression Builder

  • Intuitive Tabbed Interface: Separate tabs for Seconds, Minutes, Hours, Days, Months, and Years
  • No CRON Syntax Knowledge Required: Build complex schedules using radio buttons, checkboxes, and dropdowns
  • Real-Time Preview: See the CRON expression and human-readable description update as you build
  • Bidirectional Editing: Parse existing CRON expressions and edit them visually

Multiple Format Support

  • Quartz 7-Position: Second Minute Hour Day Month DayOfWeek Year
  • Quartz 6-Position: Second Minute Hour Day Month DayOfWeek
  • Hangfire 5-Position: Minute Hour Day Month DayOfWeek

Advanced Scheduling Options

  • Intervals: Every N seconds/minutes/hours/days/months/years
  • Ranges: Between specific values (e.g., 9 AM - 5 PM)
  • Specific Values: Select multiple specific values
  • Day Patterns:
    • Last day of month
    • Last weekday
    • Nearest weekday to a specific day
    • Nth occurrence of a day (e.g., 2nd Tuesday)
    • Days before end of month

Expression Parsing

  • Parse Existing Expressions: Paste any CRON expression and click "Parse" to populate all tabs
  • Format Auto-Detection: Automatically detects Quartz 7, Quartz 6, or Hangfire 5 formats
  • Validation & Error Handling: Clear error messages for invalid expressions

Installation

Install via NuGet Package Manager Console:

Install-Package SGU.Tools_CronExpressionBuilder

Or via .NET CLI:

dotnet add package SGU.Tools_CronExpressionBuilder

Or search for SGU.Tools_CronExpressionBuilder in the NuGet Package Manager UI.


Quick Start

Option 1: Add from Toolbox (Drag & Drop)

After installing the NuGet package and rebuilding your project:

  1. Open the Toolbox in Visual Studio (View → Toolbox or Ctrl+Alt+X)
  2. Find "SGU Tools" category or search for "Cron Expression Builder"
  3. Drag and drop the CronExpressionControl onto your Form
  4. Resize to desired size (recommended minimum: 760 x 540)
  5. Subscribe to events in the Properties window or code-behind

The control will appear in the Toolbox under the "SGU Tools" category with the display name "Cron Expression Builder".

Option 2: Add Programmatically

using SGU.Tools;

public partial class MyForm : Form
{
    private CronExpressionControl cronControl;

    public MyForm()
    {
        InitializeComponent();
        
        // Create the control
        cronControl = new CronExpressionControl
        {
            Dock = DockStyle.Fill
        };
        
        // Add to your form
        this.Controls.Add(cronControl);
        
        // Subscribe to events
        cronControl.ExpressionChanged += CronControl_ExpressionChanged;
    }

    private void CronControl_ExpressionChanged(object sender, EventArgs e)
    {
        string expression = cronControl.ExpressionString;
        string description = cronControl.Description;
        
        Console.WriteLine($"Expression: {expression}");
        Console.WriteLine($"Description: {description}");
    }
}

Option 3: Add via Designer

  1. Open your Form in the Designer
  2. Click on your Form or container control
  3. In the Designer, click to add from Toolbox or use Add → UserControl
  4. Select CronExpressionControl from the dialog
  5. Configure properties in the Properties Window

Design-Time Support

Properties Window Configuration

When you select the control in the Designer, the following properties are available in the Properties Window:

CRON Expression Category
  • ExpressionString - Set the initial CRON expression
    • Example: "0 0 9 ? * MON-FRI *"
    • Supports multi-line editor for easy editing
  • Format - Choose the CRON format
    • Options: Quartz7, Quartz6, Hangfire5
    • Default: Quartz7
  • Description (read-only) - View the human-readable description
Appearance Category
  • ShowFormatSelector - Show/hide the Format selector dropdown (Would allow users to change format at runtime)
    • Type: Boolean
    • Default: false
    • When false, control uses the Format property value without displaying the dropdown
  • ShowDescription - Show/hide the human-readable description text area
    • Type: Boolean
    • Default: true
    • When false, hides the description textbox and label
    • Useful for when using a custom description display
Behavior
  • Enabled - Enable/disable the control
  • Visible - Show/hide the control
Layout
  • Dock - Dock the control to edges
    • Recommended: Fill or None
  • Size - Set control size
  • Location - Position on form
Design
  • BackColor - Background color
  • ForeColor - Text color

Events in Designer

Double-click the control or use the Events tab (⚡) in Properties to add event handlers:

  • ExpressionChanged - Fires when the CRON expression changes
  • FormatChanged - Fires when the format is changed
// Auto-generated event handler
private void cronExpressionControl1_ExpressionChanged(object sender, EventArgs e)
{
    // Access the control's properties
    string expr = cronExpressionControl1.ExpressionString;
    string desc = cronExpressionControl1.Description;
    
    // Update your UI
    lblCurrentExpression.Text = expr;
    txtDescription.Text = desc;
}

Usage Examples

Building a CRON Expression Visually

Users interact with the tabbed interface to build their schedule:

  1. Seconds Tab: Choose "Every second", "Every N seconds", specific seconds, or a range
  2. Minutes Tab: Choose "Every minute", "Every 15 minutes", specific minutes, etc.
  3. Hours Tab: Choose "Every hour", "9 AM - 5 PM", specific hours, etc.
  4. Day Tab: Choose "Every day", "Weekdays only", "Last Friday", "2nd Monday", etc.
  5. Month Tab: Choose "Every month", specific months, "Every 3 months", etc.
  6. Year Tab: Choose "Any year", specific years, or a range

The expression and description update in real-time as selections are made.

Get the CRON Expression

// Get as string
string cronExpression = cronControl.ExpressionString;
// Example: "0 0 9 ? * MON-FRI *"

// Get as object
CronExpression expression = cronControl.Expression;
Console.WriteLine($"Format: {expression.Format}");
Console.WriteLine($"Second: {expression.Second}");
Console.WriteLine($"Minute: {expression.Minute}");
// ... access all fields

// Get description
string description = cronControl.Description;
// Example: "at second 0, at minute 0, at hour 9 AM, every day of the week between Monday and Friday, every month, every year"

Set/Parse a CRON Expression

// Set expression - automatically parses and populates all tabs
cronControl.ExpressionString = "0 */15 9-17 ? * MON-FRI *";

// The control will:
// 1. Detect format (Quartz 7-position)
// 2. Parse the expression
// 3. Populate Seconds tab: "at second 0"
// 4. Populate Minutes tab: "every 15 minutes"
// 5. Populate Hours tab: "9 AM - 5 PM"
// 6. Populate Day tab: "Monday-Friday"
// 7. Populate Month tab: "every month"
// 8. Populate Year tab: "every year"

// User can now edit visually using the tabs!

Change Format

// Switch between formats
cronControl.Format = CronExpressionFormat.Quartz7;    // 7 fields
cronControl.Format = CronExpressionFormat.Quartz6;    // 6 fields (no year, every year is implied)
cronControl.Format = CronExpressionFormat.Hangfire5;  // 5 fields (no seconds)

// Subscribe to format changes
cronControl.FormatChanged += (s, e) => 
{
    Console.WriteLine($"Format changed to: {cronControl.Format}");
};

// Show format selector to let users change format at runtime
cronControl.ShowFormatSelector = true;

// Hide format selector and use programmatic control only
cronControl.ShowFormatSelector = false;
cronControl.Format = CronExpressionFormat.Quartz7;

Programmatic Expression Building

// Build expression programmatically
var builder = new CronExpressionBuilder(CronExpressionFormat.Quartz7)
    .WithSecond("0")                    // At second 0
    .WithMinute("*/5")                  // Every 5 minutes
    .WithHour("9-17")                   // Between 9 AM and 5 PM
    .WithDayOfMonth("?")                // Any day of month
    .WithMonth("*")                     // Every month
    .WithDayOfWeek("MON-FRI")          // Monday through Friday
    .WithYear("*");                     // Every year

CronExpression expression = builder.Build();
cronControl.Expression = expression;

Use Presets

// Common schedules as presets
cronControl.Expression = CronExpressionBuilder.Presets.EverySecond();
cronControl.Expression = CronExpressionBuilder.Presets.EveryMinute();
cronControl.Expression = CronExpressionBuilder.Presets.EveryHour();
cronControl.Expression = CronExpressionBuilder.Presets.Daily(hour: 9, minute: 0);
cronControl.Expression = CronExpressionBuilder.Presets.Weekly(DayOfWeek.Monday, hour: 9);
cronControl.Expression = CronExpressionBuilder.Presets.Monthly(dayOfMonth: 1, hour: 9);
cronControl.Expression = CronExpressionBuilder.Presets.Yearly(month: 1, dayOfMonth: 1, hour: 9);

Parse Button Workflow

// User types or pastes a CRON expression into the text box
// Then clicks the "Parse" button
// The control automatically:
// 1. Validates the expression
// 2. Detects the format
// 3. Parses all fields
// 4. Populates all tabs with the correct selections
// 5. Shows success or error message

// You can also trigger parsing programmatically
try
{
    cronControl.ExpressionString = "0 0 12 1 * ?";
    // Tabs are now populated and user can edit visually
}
catch (ArgumentException ex)
{
    MessageBox.Show($"Invalid expression: {ex.Message}");
}

Supported CRON Patterns

Time Components

  • * - Every second/minute/hour
  • */N - Every N seconds/minutes/hours
  • N - Specific value
  • N,M,O - Multiple specific values
  • N-M - Range of values
  • N/M - Every M starting at N

Day of Month

  • * - Every day
  • ? - No specific value
  • N - Specific day
  • N,M - Specific days
  • N-M - Range of days
  • L - Last day of month
  • LW - Last weekday of month
  • L-N - N days before end of month
  • NW - Nearest weekday to day N
  • N/M - Every M days starting on day N

Day of Week

  • * - Every day of week
  • ? - No specific value
  • N or DAY - Specific day (1=SUN, 2=MON, etc.)
  • N,M or MON,WED,FRI - Specific days
  • N-M or MON-FRI - Range of days
  • NL - Last occurrence of day N
  • N#M - Mth occurrence of day N (e.g., 2#2 = 2nd Monday)
  • N/M - Every M days of week starting on day N

Month

  • * - Every month
  • N or MON - Specific month
  • N,M or JAN,MAR,DEC - Specific months
  • N-M or JAN-JUN - Range of months
  • N/M - Every M months starting at month N

Year (Quartz 7-position only)

  • * - Every year
  • YYYY - Specific year
  • YYYY,YYYY - Specific years
  • YYYY-YYYY - Range of years
  • YYYY/N - Every N years starting at YYYY

Real-World Examples

Every Weekday at 9 AM

Visual Selection:

  • Seconds: "At second 0"
  • Minutes: "At minute 0"
  • Hours: "At hour 9"
  • Day: Select "Monday, Tuesday, Wednesday, Thursday, Friday"
  • Month: "Every month"
  • Year: "Every year"

Result: 0 0 9 ? * MON-FRI *

Description: "at second 0, at minute 0, at hour 9 AM, every day of the week between Monday and Friday, every month, every year"


Every 15 Minutes During Business Hours

Visual Selection:

  • Seconds: "At second 0"
  • Minutes: "Every 15 minute(s)"
  • Hours: "Between hour 9 and hour 17"
  • Day: "Every day"
  • Month: "Every month"
  • Year: "Every year"

Result: 0 */15 9-17 ? * * *

Description: "at second 0, every 15 minutes, every hour between 9 AM and 5 PM, every day, every month, every year"


First Monday of Every Month at Noon

Visual Selection:

  • Seconds: "At second 0"
  • Minutes: "At minute 0"
  • Hours: "At hour 12 (noon)"
  • Day: "On the 1st Monday of the month"
  • Month: "Every month"
  • Year: "Every year"

Result: 0 0 12 ? * MON#1 *

Description: "at second 0, at minute 0, at hour noon, on the first Monday of the month, every month, every year"


Last Day of Every Quarter

Visual Selection:

  • Seconds: "At second 0"
  • Minutes: "At minute 0"
  • Hours: "At hour 0 (midnight)"
  • Day: "On the last day of the month"
  • Month: Select "MAR, JUN, SEP, DEC"
  • Year: "Every year"

Result: 0 0 0 L * ? * (for monthly) or 0 0 0 L MAR,JUN,SEP,DEC ? *


API Reference

CronExpressionControl

Properties
  • Expression - Gets/sets the current CRON expression object
  • ExpressionString - Gets/sets the CRON expression as a string
  • Description - Gets the human-readable description
  • DescriptionText - Gets/sets the displayed description text
  • Format - Gets/sets the CRON expression format
  • Translator - Gets/sets the translator for descriptions
  • ShowFormatSelector - Gets/sets whether the format selector dropdown is visible (default: false)
  • ShowDescription - Gets/sets whether the description text area is visible (default: true)
Events
  • ExpressionChanged - Fired when the expression changes
  • FormatChanged - Fired when the format changes
Methods
  • GetExpression() - Gets the current CRON expression as a string
  • SetExpression(string) - Sets the CRON expression from a string

CronExpression

public class CronExpression
{
    public string Second { get; set; }
    public string Minute { get; set; }
    public string Hour { get; set; }
    public string DayOfMonth { get; set; }
    public string Month { get; set; }
    public string DayOfWeek { get; set; }
    public string Year { get; set; }
    public CronExpressionFormat Format { get; set; }
    
    public static CronExpression Parse(string expression, CronExpressionFormat format);
    public bool IsValid();
    public CronExpression Clone();
    public override string ToString();
}

CronExpressionBuilder

public class CronExpressionBuilder
{
    public CronExpressionBuilder(CronExpressionFormat format);
    
    public CronExpressionBuilder WithSecond(string second);
    public CronExpressionBuilder WithMinute(string minute);
    public CronExpressionBuilder WithHour(string hour);
    public CronExpressionBuilder WithDayOfMonth(string dayOfMonth);
    public CronExpressionBuilder WithMonth(string month);
    public CronExpressionBuilder WithDayOfWeek(string dayOfWeek);
    public CronExpressionBuilder WithYear(string year);
    
    public CronExpression Build();
    
    // Presets
    public static class Presets
    {
        public static CronExpression EverySecond();
        public static CronExpression EveryMinute();
        public static CronExpression EveryHour();
        public static CronExpression Daily(int hour = 0, int minute = 0);
        public static CronExpression Weekly(DayOfWeek day, int hour = 0, int minute = 0);
        public static CronExpression Monthly(int dayOfMonth = 1, int hour = 0, int minute = 0);
        public static CronExpression Yearly(int month = 1, int dayOfMonth = 1, int hour = 0, int minute = 0);
    }
}

Platform Support

  • .NET Framework 4.8
  • .NET 8.0 (Windows)
  • .NET 10.0 (Windows)
  • .NET 10.0 (Windows 10.0.26100.0)

Note: This is a WinForms control and requires Windows desktop applications.


Integration Examples

Save to Database

public void SaveSchedule()
{
    var schedule = new ScheduleConfig
    {
        Name = txtScheduleName.Text,
        CronExpression = cronControl.ExpressionString,
        Description = cronControl.Description,
        Format = cronControl.Format.ToString(),
        IsActive = true,
        CreatedDate = DateTime.UtcNow
    };
    
    database.Schedules.Add(schedule);
    database.SaveChanges();
}

Load from Database

public void LoadSchedule(int scheduleId)
{
    var schedule = database.Schedules.Find(scheduleId);
    if (schedule != null)
    {
        txtScheduleName.Text = schedule.Name;
        cronControl.ExpressionString = schedule.CronExpression;
        // Tabs are automatically populated for visual editing
    }
}

Use with Quartz.NET

using Quartz;

public void ScheduleJob()
{
    // Get expression from control
    string cronExpression = cronControl.ExpressionString;
    
    // Create Quartz trigger
    var trigger = TriggerBuilder.Create()
        .WithIdentity("myTrigger", "group1")
        .WithCronSchedule(cronExpression)
        .Build();
    
    // Schedule job
    scheduler.ScheduleJob(myJob, trigger);
}

Use with Hangfire

using Hangfire;

public void ScheduleRecurringJob()
{
    // Get expression from control (ensure Hangfire format)
    cronControl.Format = CronExpressionFormat.Hangfire6;
    string cronExpression = cronControl.ExpressionString;
    
    // Schedule with Hangfire
    RecurringJob.AddOrUpdate(
        "myJob",
        () => MyMethod(),
        cronExpression);
}

Tips & Best Practices

Validation

// Always validate before using
if (cronControl.Expression.IsValid())
{
    string expr = cronControl.ExpressionString;
    // Use expression
}
else
{
    MessageBox.Show("Invalid CRON expression");
}

Format Selection

// Match your scheduler's format
if (usingHangfire)
{
    cronControl.Format = CronExpressionFormat.Hangfire5;
}
else if (usingQuartzWithYear)
{
    cronControl.Format = CronExpressionFormat.Quartz7;
}
else
{
    cronControl.Format = CronExpressionFormat.Quartz6;
}

User Experience

// Provide real-time feedback
cronControl.ExpressionChanged += (s, e) =>
{
    lblExpression.Text = cronControl.ExpressionString;
    lblDescription.Text = cronControl.Description;
    
    // Show next 5 execution times
    ShowNextExecutions(cronControl.ExpressionString);
};
// For a cleaner, simpler interface, hide advanced options
// This is the default configuration - no code needed!

// Format selector is hidden by default
// Control defaults to Quartz 7-position format
// Change format programmatically if needed:
cronControl.Format = CronExpressionFormat.Hangfire5;

// If you need to show the format selector:
cronControl.ShowFormatSelector = true;      // Let users switch formats

// Hide description for compact layout:
cronControl.ShowDescription = false;        

// Show/hide description dynamically:
chkShowDescription.CheckedChanged += (s, e) =>
{
    cronControl.ShowDescription = chkShowDescription.Checked;
};

UI Customization

Control Visibility Options

The control provides two visibility properties to customize the UI based on your application's needs:

ShowDescription Property

Controls the visibility of the human-readable description text area.

Default: true (description visible)

When to Hide:

  • Space-constrained UIs where vertical space is limited
  • Applications with expert users who don't need visual confirmation
  • Custom description displays elsewhere in your UI
  • Embedded scenarios where the control is part of a larger form

When to Show (Default):

  • New users who need to understand what their CRON expression means
  • Training or educational scenarios
  • Quick visual confirmation of expression meaning
  • General use where space is not a concern

Example Usage:

// Hide description 
cronControl.ShowDescription = false;

// Show description (default)
cronControl.ShowDescription = true;


// Toggle dynamically
btnToggleDescription.Click += (s, e) =>
{
    cronControl.ShowDescription = !cronControl.ShowDescription;
    btnToggleDescription.Text = cronControl.ShowDescription 
        ? "Hide Description" 
        : "Show Description";
};

// Use description elsewhere when hidden
if (!cronControl.ShowDescription)
{
    // Display in a separate label or tooltip
    lblCustomDescription.Text = cronControl.Description;
    toolTip1.SetToolTip(cronControl, cronControl.Description);
}
ShowFormatSelector Property

Controls the visibility of the format selector dropdown.

Default: false (dropdown hidden)

When to Hide (Default):

  • Single format applications (e.g., only using Hangfire)
  • Simplified UI for end users
  • Format is controlled programmatically
  • Prevent users from changing format accidentally

When to Show:

  • Multi-scheduler applications (supporting both Quartz and Hangfire)
  • Power users who need format flexibility
  • Development/testing tools

Example Usage:

// Show format selector for flexibility
cronControl.ShowFormatSelector = true;

// Hide format selector and lock format
cronControl.ShowFormatSelector = false;
cronControl.Format = CronExpressionFormat.Hangfire5;

// Dynamic control based on user role
if (user.IsAdmin)
{
    cronControl.ShowFormatSelector = true;
}
Combined Scenarios
// Scenario 1: Minimal UI (most compact)
cronControl.ShowFormatSelector = false;
cronControl.ShowDescription = false;
// Best for: Simple schedulers

// Scenario 2: Standard UI (default, recommended)
cronControl.ShowFormatSelector = false;
cronControl.ShowDescription = true;
// Best for: Most applications, good balance

// Scenario 3: Power User UI
cronControl.ShowFormatSelector = true;
cronControl.ShowDescription = true;
// Best for: Admin panels, development tools

// Scenario 4: Custom Description Display
cronControl.ShowDescription = false;
cronControl.ExpressionChanged += (s, e) =>
{
    // Show in custom location
    txtMyCustomDescription.Text = cronControl.Description;
    // Or in a tooltip
    toolTip1.SetToolTip(lblExpression, cronControl.Description);
};
// Best for: Custom layouts, specialized UIs

Layout Adjustment

// The control handles layout automatically
cronControl.ShowDescription = false;  
cronControl.ShowDescription = true;   

// Smooth transition with no flickering
this.SuspendLayout();
cronControl.ShowDescription = !cronControl.ShowDescription;
this.ResumeLayout();

📋 Issue Reporting & Support

If you encounter any bugs or have feature requests, please contact:

Solutions Group Unlimited, LLC
Email: support@solutionsgroupunlimited.com

Please include:

  • Version of the package
  • Target framework (.NET Framework 4.8, .NET 8, .NET 10)
  • Steps to reproduce the issue
  • Expected vs actual behavior
  • Sample CRON expression (if applicable)

License

Licensed under the MIT License.


Author

Solutions Group Unlimited, LLC

Copyright © 2025 Solutions Group Unlimited, LLC. All rights reserved.


Changelog


Support

If you find this library helpful, consider supporting development:

Buy Me a Coffee

Product Compatible and additional computed target framework versions.
.NET net8.0-windows7.0 is compatible.  net9.0-windows was computed.  net10.0-windows was computed.  net10.0-windows7.0 is compatible.  net10.0-windows10.0.26100 is compatible. 
.NET Framework net48 is compatible.  net481 was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETFramework 4.8

    • No dependencies.
  • net10.0-windows10.0.26100

    • No dependencies.
  • net10.0-windows7.0

    • No dependencies.
  • net8.0-windows7.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.1.4 90 1/14/2026
1.1.2 84 1/13/2026

🚀 Visual CRON Expression Builder for WinForms

✨ Features:
• Intuitive tabbed interface (no CRON syntax knowledge required)
• Support for Quartz 7/6 and Hangfire 5 formats
• Parse existing CRON expressions
• Real-time description preview
• Advanced scheduling: Last day, Nth occurrence, nearest weekday, and more

📦 Installation:
Install-Package SGU.Tools_CronExpressionBuilder

📖 Documentation:
See the package README for complete documentation, API reference, and usage examples.
Demo application is included in the package for hands-on evaluation.

🆕 What's New in This Version:
• Renamed Demo project (formerly TestApp) for better clarity
• Comprehensive test suite with 414 tests covering all scenarios
• Fixed Hangfire vs Quartz day-of-week indexing (Monday=1 in Hangfire, Monday=2 in Quartz)
• Improved symbol package handling (symbols for internal builds only, not published to NuGet.org)

📧 Support:
For issues or questions, contact: support@solutionsgroupunlimited.com

📄 License: MIT
© 2025 Solutions Group Unlimited, LLC