diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json
new file mode 100644
index 0000000..fe017a6
--- /dev/null
+++ b/.config/dotnet-tools.json
@@ -0,0 +1,20 @@
+{
+ "version": 1,
+ "isRoot": true,
+ "tools": {
+ "dotnet-reportgenerator-globaltool": {
+ "version": "5.1.22",
+ "commands": [
+ "reportgenerator"
+ ],
+ "rollForward": false
+ },
+ "meziantou.framework.nugetpackagevalidation.tool": {
+ "version": "1.0.16",
+ "commands": [
+ "meziantou.validate-nuget-package"
+ ],
+ "rollForward": false
+ }
+ }
+}
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..fbe882f
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,125 @@
+# http://editorconfig.org/
+
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj,props,targets}]
+indent_size = 2
+
+[*.{sln}]
+charset = utf-8-bom
+indent_style = tab
+
+[*.{json,yml,xml,runsettings}]
+indent_size = 2
+
+[*.{cs,tt}]
+indent_style = space
+indent_size = 4
+max_line_length = 100
+
+[*.cs]
+dotnet_analyzer_diagnostic.category-Style.severity = warning
+
+# IDE0022: Use expression/block body for methods
+dotnet_diagnostic.IDE0022.severity = suggestion
+
+# IDE1006: Naming rule violation
+dotnet_diagnostic.IDE1006.severity = warning
+
+# Naming capitalization styles
+dotnet_naming_style.camel_case_style.capitalization = camel_case
+dotnet_naming_style.pascal_case_style.capitalization = pascal_case
+
+# Naming rule that private instance fields must use camel case
+dotnet_naming_symbols.private_fields.applicable_kinds = field
+dotnet_naming_symbols.private_fields.applicable_accessibilities = private
+dotnet_naming_rule.camel_case_private_fields.severity = warning
+dotnet_naming_rule.camel_case_private_fields.symbols = private_fields
+dotnet_naming_rule.camel_case_private_fields.style = camel_case_style
+
+# Naming rule that static read-only fields must use Pascal case
+dotnet_naming_symbols.static_readonly_fields.applicable_kinds = field
+dotnet_naming_symbols.static_readonly_fields.applicable_accessibilities = *
+dotnet_naming_symbols.static_readonly_fields.required_modifiers = readonly, static
+dotnet_naming_rule.pascal_case_static_readonly_fields.severity = warning
+dotnet_naming_rule.pascal_case_static_readonly_fields.symbols = static_readonly_fields
+dotnet_naming_rule.pascal_case_static_readonly_fields.style = pascal_case_style
+
+# Naming rule that const fields must use Pascal case
+dotnet_naming_symbols.const_fields.applicable_kinds = field
+dotnet_naming_symbols.const_fields.applicable_accessibilities = *
+dotnet_naming_symbols.const_fields.required_modifiers = const
+dotnet_naming_rule.pascal_case_const_fields.severity = warning
+dotnet_naming_rule.pascal_case_const_fields.symbols = const_fields
+dotnet_naming_rule.pascal_case_const_fields.style = pascal_case_style
+
+# this. preferences
+dotnet_style_qualification_for_event = false
+dotnet_style_qualification_for_field = true
+dotnet_style_qualification_for_method = false
+dotnet_style_qualification_for_property = false
+
+# Prefer "var" everywhere
+csharp_style_var_for_built_in_types = true
+csharp_style_var_when_type_is_apparent = true
+csharp_style_var_elsewhere = true
+
+# Prefer method-like constructs to have a block body
+csharp_style_expression_bodied_methods = true
+csharp_style_expression_bodied_constructors = true
+csharp_style_expression_bodied_operators = true
+
+# Prefer property-like constructs to have an expression-body
+csharp_style_expression_bodied_properties = true
+csharp_style_expression_bodied_indexers = true
+csharp_style_expression_bodied_accessors = true
+
+# Suggest more modern language features when available
+csharp_style_pattern_matching_over_is_with_cast_check = true
+csharp_style_pattern_matching_over_as_with_null_check = true
+csharp_style_inlined_variable_declaration = true
+csharp_style_throw_expression = true
+csharp_style_conditional_delegate_call = true
+csharp_prefer_simple_default_expression = true
+
+# Spacing
+csharp_space_after_cast = false
+csharp_space_after_keywords_in_control_flow_statements = true
+csharp_space_between_method_declaration_parameter_list_parentheses = false
+
+# Wrapping
+csharp_preserve_single_line_statements = true
+csharp_preserve_single_line_blocks = true
+
+# Indentation
+csharp_indent_case_contents_when_block = false
+
+# Modifier preferences
+dotnet_style_require_accessibility_modifiers = omit_if_default
+
+# IDE0011: Add braces
+csharp_prefer_braces = when_multiline
+
+# IDE0061: Use block body for local functions
+csharp_style_expression_bodied_local_functions = true
+
+# IDE0065: Misplaced using directive
+csharp_using_directive_placement = outside_namespace
+
+# IDE0048: Add parentheses for clarity
+dotnet_diagnostic.IDE0048.severity = suggestion
+
+# IDE0055: Fix formatting
+dotnet_diagnostic.IDE0055.severity = suggestion
+
+# IDE0046: Convert to conditional expression
+dotnet_diagnostic.IDE0046.severity = suggestion
+
+# IDE0160: Convert to block scoped namespace
+csharp_style_namespace_declarations = file_scoped
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
new file mode 100644
index 0000000..f040824
--- /dev/null
+++ b/.git-blame-ignore-revs
@@ -0,0 +1,5 @@
+# For more information on this file, see:
+# https://git-scm.com/docs/git-blame#Documentation/git-blame.txt---ignore-revltrevgt
+
+# Convert to file-scoped namespace
+da2b51af0c8186454c542dc34d17abb81e8c6dff
diff --git a/.gitignore b/.gitignore
index 0e46a6e..263ae55 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,17 +1,373 @@
-bin/
-*.dll
-*.pdb
-*.user
-*.cache
+# Created by https://www.gitignore.io/api/linux,macos,windows,visualstudio,visualstudiocode
+
+### Linux ###
+*~
+
+# temporary files which can be created if a process still has a handle open of a deleted file
+.fuse_hidden*
+
+# KDE directory preferences
+.directory
+
+# Linux trash folder which might appear on any partition or disk
+.Trash-*
+
+# .nfs files are created when an open file is removed but is still being accessed
+.nfs*
+
+### macOS ###
+*.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+### VisualStudioCode ###
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+.history
+
+### Windows ###
+# Windows thumbnail cache files
+Thumbs.db
+ehthumbs.db
+ehthumbs_vista.db
+
+# Folder config file
+Desktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Windows Installer files
+*.cab
+*.msi
+*.msm
+*.msp
+
+# Windows shortcuts
+*.lnk
+
+### VisualStudio ###
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
*.suo
-obj/
-obj.portable/
-_ReSharper.*
-*.orig
-dist/
-pkg/base/
-*.nupkg
-packages/*
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+
+# Visual Studio 2015 cache/options directory
.vs/
-project.lock.json
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUNIT
+*.VisualState.xml
TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+**/Properties/launchSettings.json
+
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.pch
+*.pdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding add-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# TODO: Uncomment the next line to ignore your web deploy settings.
+# By default, sensitive information, such as encrypted password
+# should be stored in the .pubxml.user file.
+#*.pubxml
+*.pubxml.user
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# The packages folder can be ignored because of Package Restore
+**/packages/*
+# except build/, which is used as an MSBuild target.
+!**/packages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/packages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Typescript v1 declaration files
+typings/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# JetBrains Rider
+.idea/
+*.sln.iml
+
+# CodeRush
+.cr/
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+### VisualStudio Patch ###
+# By default, sensitive information, such as encrypted password
+# should be stored in the .pubxml.user file.
+
+# End of https://www.gitignore.io/api/linux,macos,windows,visualstudio,visualstudiocode
+
+temp/
+tmp/
+etc/coverage/
diff --git a/Directory.Build.props b/Directory.Build.props
new file mode 100644
index 0000000..4ddecab
--- /dev/null
+++ b/Directory.Build.props
@@ -0,0 +1,12 @@
+
+
+ 12
+ true
+ 8.0-all
+ enable
+ true
+ true
+
+ EnableGenerateDocumentationFile
+
+
diff --git a/NCrontab.Signed/NCrontab.Signed.csproj b/NCrontab.Signed/NCrontab.Signed.csproj
index 17e3bac..4f577ad 100644
--- a/NCrontab.Signed/NCrontab.Signed.csproj
+++ b/NCrontab.Signed/NCrontab.Signed.csproj
@@ -1,68 +1,19 @@
-
+
+
+
- NCrontab is crontab for all .NET runtimes supported by .NET Standard 1.0. It provides parsing and formatting of crontab expressions as well as calculation of occurrences of time based on a schedule expressed in the crontab format.
- Copyright © 2008 Atif Aziz. All rights reserved. Portions Copyright © 2001 The OpenSymphony Group. All rights reserved.
- NCrontab (Signed)
- en-US
- 3.3.2
- Atif Aziz
- net35;netstandard1.0;netstandard2.0
- $(DefineConstants);SIGNED
- true
- portable
NCrontab.Signed
- Library
+ NCrontab (Signed)
key.snk
true
true
+ $(DefineConstants);SIGNED
NCrontab.Signed
- cron;schedule;time
- https://github.com/atifaziz/NCrontab
- true
- COPYING.txt
- ..\dist
- false
- false
- false
- false
- false
- $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb
-
-
-
-
-
-
-
- $(MSBuildProgramFiles32)\Reference Assemblies\Microsoft\Framework\.NETFramework\v3.5\Profile\Client
-
-
-
-
-
-
-
- $(DefineConstants);SERIALIZATION
-
-
-
-
-
-
-
-
-
-
-
-
-
+
diff --git a/NCrontab.Tests/.editorconfig b/NCrontab.Tests/.editorconfig
new file mode 100644
index 0000000..6e96408
--- /dev/null
+++ b/NCrontab.Tests/.editorconfig
@@ -0,0 +1,15 @@
+# http://editorconfig.org/
+
+[*.cs]
+
+# CA1707: Identifiers should not contain underscores
+dotnet_diagnostic.CA1707.severity = none
+
+# CA2201: Do not raise reserved exception types
+dotnet_diagnostic.CA2201.severity = suggestion
+
+# CA1062: Validate arguments of public methods
+dotnet_diagnostic.CA1062.severity = none
+
+# CA1861: Avoid constant arrays as arguments
+dotnet_diagnostic.CA1861.severity = suggestion
diff --git a/NCrontab.Tests/.runsettings b/NCrontab.Tests/.runsettings
new file mode 100644
index 0000000..cc74b6b
--- /dev/null
+++ b/NCrontab.Tests/.runsettings
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/NCrontab.Tests/CrontabScheduleTests.cs b/NCrontab.Tests/CrontabScheduleTests.cs
index 28d8c71..b35e68e 100644
--- a/NCrontab.Tests/CrontabScheduleTests.cs
+++ b/NCrontab.Tests/CrontabScheduleTests.cs
@@ -18,428 +18,489 @@
//
#endregion
-namespace NCrontab.Tests
+using System;
+using System.Globalization;
+using System.Linq;
+using NUnit.Framework;
+using ParseOptions = NCrontab.CrontabSchedule.ParseOptions;
+
+namespace NCrontab.Tests;
+
+[TestFixture]
+public sealed class CrontabScheduleTests
{
- using System;
- using System.Globalization;
- using System.Linq;
- using NUnit.Framework;
- using ParseOptions = CrontabSchedule.ParseOptions;
-
- [TestFixture]
- public sealed class CrontabScheduleTests
+ const string TimeFormat = "dd/MM/yyyy HH:mm:ss";
+
+ static readonly string[] TimeFormats =
+ [
+ "yyyy-MM-dd",
+ "yyyy-MM-dd HH:mm",
+ "yyyy-MM-dd HH:mm:ss",
+ "dd/MM/yyyy HH:mm:ss"
+ ];
+
+ [Test]
+ public void CannotParseNullString()
{
- const string TimeFormat = "dd/MM/yyyy HH:mm:ss";
+ Assert.That(() => CrontabSchedule.Parse(null!),
+ Throws.ArgumentNullException
+ .With.Property(nameof(ArgumentNullException.ParamName)).EqualTo("expression"));
+ }
- static readonly string[] TimeFormats =
- {
- "yyyy-MM-dd",
- "yyyy-MM-dd HH:mm",
- "yyyy-MM-dd HH:mm:ss",
- "dd/MM/yyyy HH:mm:ss"
- };
+ [Test]
+ public void CannotParseEmptyString()
+ {
+ Assert.That(() => CrontabSchedule.Parse(string.Empty), Throws.TypeOf());
+ }
- [Test]
- public void CannotParseNullString()
- {
- var e = Assert.Throws(() => CrontabSchedule.Parse(null));
- Assert.That(e.ParamName, Is.EqualTo("expression"));
- }
+ [Test]
+ public void TryParseNullString() =>
+ Assert.That(CrontabSchedule.TryParse(null!), Is.Null);
- [Test]
- public void CannotParseEmptyString()
- {
- Assert.Throws(() => CrontabSchedule.Parse(string.Empty));
- }
+ [Test]
+ public void TryParseEmptyString() =>
+ Assert.That(CrontabSchedule.TryParse(string.Empty), Is.Null);
- [Test]
- public void TryParseNullString() =>
- Assert.That(CrontabSchedule.TryParse(null), Is.Null);
+ [Test]
+ public void AllTimeString()
+ {
+ var result = CrontabSchedule.Parse("* * * * *").ToString();
+ Assert.That(result, Is.EqualTo("* * * * *"));
+ }
- [Test]
- public void TryParseEmptyString() =>
- Assert.That(CrontabSchedule.TryParse(string.Empty), Is.Null);
+ [Test]
+ public void SixPartAllTimeString()
+ {
+ var result = CrontabSchedule.Parse("* * * * * *", new ParseOptions { IncludingSeconds = true }).ToString();
+ Assert.That(result, Is.EqualTo("* * * * * *"));
+ }
- [Test]
- public void AllTimeString()
- {
- Assert.AreEqual("* * * * *", CrontabSchedule.Parse("* * * * *").ToString());
- }
+ [Test]
+ public void CannotParseWhenSecondsRequired()
+ {
+ Assert.That(() => CrontabSchedule.Parse("* * * * *", new ParseOptions { IncludingSeconds = true }),
+ Throws.TypeOf());
+ }
- [Test]
- public void SixPartAllTimeString()
- {
- Assert.AreEqual("* * * * * *", CrontabSchedule.Parse("* * * * * *", new ParseOptions { IncludingSeconds = true }).ToString());
- }
+ [TestCase("* 1-3 * * *" , "* 1-2,3 * * *" , false)]
+ [TestCase("* * * 1,3,5,7,9,11 *" , "* * * */2 *" , false)]
+ [TestCase("10,25,40 * * * *" , "10-40/15 * * * *" , false)]
+ [TestCase("* * * 1,3,8 1-2,5" , "* * * Mar,Jan,Aug Fri,Mon-Tue" , false)]
+ [TestCase("1 * 1-3 * * *" , "1 * 1-2,3 * * *" , true )]
+ [TestCase("22 * * * 1,3,5,7,9,11 *", "22 * * * */2 *" , true )]
+ [TestCase("33 10,25,40 * * * *" , "33 10-40/15 * * * *" , true )]
+ [TestCase("55 * * * 1,3,8 1-2,5" , "55 * * * Mar,Jan,Aug Fri,Mon-Tue", true )]
+ public void Formatting(string format, string expression, bool includingSeconds)
+ {
+ var options = new ParseOptions { IncludingSeconds = includingSeconds };
+ var result = CrontabSchedule.Parse(expression, options).ToString();
+ Assert.That(result, Is.EqualTo(format));
+ }
- [Test]
- public void CannotParseWhenSecondsRequired()
- {
- Assert.Throws(() => CrontabSchedule.Parse("* * * * *", new ParseOptions { IncludingSeconds = true }));
- }
-
- [TestCase("* 1-3 * * *" , "* 1-2,3 * * *" , false)]
- [TestCase("* * * 1,3,5,7,9,11 *" , "* * * */2 *" , false)]
- [TestCase("10,25,40 * * * *" , "10-40/15 * * * *" , false)]
- [TestCase("* * * 1,3,8 1-2,5" , "* * * Mar,Jan,Aug Fri,Mon-Tue" , false)]
- [TestCase("1 * 1-3 * * *" , "1 * 1-2,3 * * *" , true )]
- [TestCase("22 * * * 1,3,5,7,9,11 *", "22 * * * */2 *" , true )]
- [TestCase("33 10,25,40 * * * *" , "33 10-40/15 * * * *" , true )]
- [TestCase("55 * * * 1,3,8 1-2,5" , "55 * * * Mar,Jan,Aug Fri,Mon-Tue", true )]
- public void Formatting(string format, string expression, bool includingSeconds)
- {
- var options = new ParseOptions { IncludingSeconds = includingSeconds };
- Assert.AreEqual(format, CrontabSchedule.Parse(expression, options).ToString());
- }
-
- ///
- /// Tests to see if the cron class can calculate the previous matching
- /// time correctly in various circumstances.
- ///
+ ///
+ /// Tests to see if the cron class can calculate the previous matching
+ /// time correctly in various circumstances.
+ ///
- [TestCase("01/01/2003 00:00:00", "* * * * *", "01/01/2003 00:01:00", false)]
- [TestCase("01/01/2003 00:01:00", "* * * * *", "01/01/2003 00:02:00", false)]
- [TestCase("01/01/2003 00:02:00", "* * * * *", "01/01/2003 00:03:00", false)]
- [TestCase("01/01/2003 00:59:00", "* * * * *", "01/01/2003 01:00:00", false)]
- [TestCase("01/01/2003 01:59:00", "* * * * *", "01/01/2003 02:00:00", false)]
- [TestCase("01/01/2003 23:59:00", "* * * * *", "02/01/2003 00:00:00", false)]
- [TestCase("31/12/2003 23:59:00", "* * * * *", "01/01/2004 00:00:00", false)]
+ [TestCase("01/01/2003 00:00:00", "* * * * *", "01/01/2003 00:01:00", false)]
+ [TestCase("01/01/2003 00:01:00", "* * * * *", "01/01/2003 00:02:00", false)]
+ [TestCase("01/01/2003 00:02:00", "* * * * *", "01/01/2003 00:03:00", false)]
+ [TestCase("01/01/2003 00:59:00", "* * * * *", "01/01/2003 01:00:00", false)]
+ [TestCase("01/01/2003 01:59:00", "* * * * *", "01/01/2003 02:00:00", false)]
+ [TestCase("01/01/2003 23:59:00", "* * * * *", "02/01/2003 00:00:00", false)]
+ [TestCase("31/12/2003 23:59:00", "* * * * *", "01/01/2004 00:00:00", false)]
- [TestCase("28/02/2003 23:59:00", "* * * * *", "01/03/2003 00:00:00", false)]
- [TestCase("28/02/2004 23:59:00", "* * * * *", "29/02/2004 00:00:00", false)]
+ [TestCase("28/02/2003 23:59:00", "* * * * *", "01/03/2003 00:00:00", false)]
+ [TestCase("28/02/2004 23:59:00", "* * * * *", "29/02/2004 00:00:00", false)]
- // Second tests
+ // Second tests
- [TestCase("01/01/2003 00:00:00", "45 * * * * *", "01/01/2003 00:00:45" , true)]
+ [TestCase("01/01/2003 00:00:00", "45 * * * * *", "01/01/2003 00:00:45" , true)]
- [TestCase("01/01/2003 00:00:00", "45-47,48,49 * * * * *", "01/01/2003 00:00:45", true)]
- [TestCase("01/01/2003 00:00:45", "45-47,48,49 * * * * *", "01/01/2003 00:00:46", true)]
- [TestCase("01/01/2003 00:00:46", "45-47,48,49 * * * * *", "01/01/2003 00:00:47", true)]
- [TestCase("01/01/2003 00:00:47", "45-47,48,49 * * * * *", "01/01/2003 00:00:48", true)]
- [TestCase("01/01/2003 00:00:48", "45-47,48,49 * * * * *", "01/01/2003 00:00:49", true)]
- [TestCase("01/01/2003 00:00:49", "45-47,48,49 * * * * *", "01/01/2003 00:01:45", true)]
+ [TestCase("01/01/2003 00:00:00", "45-47,48,49 * * * * *", "01/01/2003 00:00:45", true)]
+ [TestCase("01/01/2003 00:00:45", "45-47,48,49 * * * * *", "01/01/2003 00:00:46", true)]
+ [TestCase("01/01/2003 00:00:46", "45-47,48,49 * * * * *", "01/01/2003 00:00:47", true)]
+ [TestCase("01/01/2003 00:00:47", "45-47,48,49 * * * * *", "01/01/2003 00:00:48", true)]
+ [TestCase("01/01/2003 00:00:48", "45-47,48,49 * * * * *", "01/01/2003 00:00:49", true)]
+ [TestCase("01/01/2003 00:00:49", "45-47,48,49 * * * * *", "01/01/2003 00:01:45", true)]
- [TestCase("01/01/2003 00:00:00", "2/5 * * * * *", "01/01/2003 00:00:02" , true)]
- [TestCase("01/01/2003 00:00:02", "2/5 * * * * *", "01/01/2003 00:00:07" , true)]
- [TestCase("01/01/2003 00:00:50", "2/5 * * * * *", "01/01/2003 00:00:52" , true)]
- [TestCase("01/01/2003 00:00:52", "2/5 * * * * *", "01/01/2003 00:00:57" , true)]
- [TestCase("01/01/2003 00:00:57", "2/5 * * * * *", "01/01/2003 00:01:02" , true)]
+ [TestCase("01/01/2003 00:00:00", "2/5 * * * * *", "01/01/2003 00:00:02" , true)]
+ [TestCase("01/01/2003 00:00:02", "2/5 * * * * *", "01/01/2003 00:00:07" , true)]
+ [TestCase("01/01/2003 00:00:50", "2/5 * * * * *", "01/01/2003 00:00:52" , true)]
+ [TestCase("01/01/2003 00:00:52", "2/5 * * * * *", "01/01/2003 00:00:57" , true)]
+ [TestCase("01/01/2003 00:00:57", "2/5 * * * * *", "01/01/2003 00:01:02" , true)]
- // Minute tests
+ // See: https://github.com/atifaziz/NCrontab/issues/90
+ [TestCase("24/02/2021 09:50:35", "* 0-1 10 * * *", "24/02/2021 10:00:00" , true)]
- [TestCase("01/01/2003 00:00:00", "45 * * * *", "01/01/2003 00:45:00", false)]
+ // Minute tests
- [TestCase("01/01/2003 00:00:00", "45-47,48,49 * * * *", "01/01/2003 00:45:00", false)]
- [TestCase("01/01/2003 00:45:00", "45-47,48,49 * * * *", "01/01/2003 00:46:00", false)]
- [TestCase("01/01/2003 00:46:00", "45-47,48,49 * * * *", "01/01/2003 00:47:00", false)]
- [TestCase("01/01/2003 00:47:00", "45-47,48,49 * * * *", "01/01/2003 00:48:00", false)]
- [TestCase("01/01/2003 00:48:00", "45-47,48,49 * * * *", "01/01/2003 00:49:00", false)]
- [TestCase("01/01/2003 00:49:00", "45-47,48,49 * * * *", "01/01/2003 01:45:00", false)]
-
- [TestCase("01/01/2003 00:00:00", "2/5 * * * *", "01/01/2003 00:02:00", false)]
- [TestCase("01/01/2003 00:02:00", "2/5 * * * *", "01/01/2003 00:07:00", false)]
- [TestCase("01/01/2003 00:50:00", "2/5 * * * *", "01/01/2003 00:52:00", false)]
- [TestCase("01/01/2003 00:52:00", "2/5 * * * *", "01/01/2003 00:57:00", false)]
- [TestCase("01/01/2003 00:57:00", "2/5 * * * *", "01/01/2003 01:02:00", false)]
+ [TestCase("01/01/2003 00:00:00", "45 * * * *", "01/01/2003 00:45:00", false)]
- [TestCase("01/01/2003 00:00:30", "3 45 * * * *", "01/01/2003 00:45:03", true)]
-
- [TestCase("01/01/2003 00:00:30", "6 45-47,48,49 * * * *", "01/01/2003 00:45:06", true)]
- [TestCase("01/01/2003 00:45:30", "6 45-47,48,49 * * * *", "01/01/2003 00:46:06", true)]
- [TestCase("01/01/2003 00:46:30", "6 45-47,48,49 * * * *", "01/01/2003 00:47:06", true)]
- [TestCase("01/01/2003 00:47:30", "6 45-47,48,49 * * * *", "01/01/2003 00:48:06", true)]
- [TestCase("01/01/2003 00:48:30", "6 45-47,48,49 * * * *", "01/01/2003 00:49:06", true)]
- [TestCase("01/01/2003 00:49:30", "6 45-47,48,49 * * * *", "01/01/2003 01:45:06", true)]
-
- [TestCase("01/01/2003 00:00:30", "9 2/5 * * * *", "01/01/2003 00:02:09", true)]
- [TestCase("01/01/2003 00:02:30", "9 2/5 * * * *", "01/01/2003 00:07:09", true)]
- [TestCase("01/01/2003 00:50:30", "9 2/5 * * * *", "01/01/2003 00:52:09", true)]
- [TestCase("01/01/2003 00:52:30", "9 2/5 * * * *", "01/01/2003 00:57:09", true)]
- [TestCase("01/01/2003 00:57:30", "9 2/5 * * * *", "01/01/2003 01:02:09", true)]
-
- // Hour tests
+ [TestCase("01/01/2003 00:00:00", "45-47,48,49 * * * *", "01/01/2003 00:45:00", false)]
+ [TestCase("01/01/2003 00:45:00", "45-47,48,49 * * * *", "01/01/2003 00:46:00", false)]
+ [TestCase("01/01/2003 00:46:00", "45-47,48,49 * * * *", "01/01/2003 00:47:00", false)]
+ [TestCase("01/01/2003 00:47:00", "45-47,48,49 * * * *", "01/01/2003 00:48:00", false)]
+ [TestCase("01/01/2003 00:48:00", "45-47,48,49 * * * *", "01/01/2003 00:49:00", false)]
+ [TestCase("01/01/2003 00:49:00", "45-47,48,49 * * * *", "01/01/2003 01:45:00", false)]
- [TestCase("20/12/2003 10:00:00", " * 3/4 * * *", "20/12/2003 11:00:00", false)]
- [TestCase("20/12/2003 00:30:00", " * 3 * * *", "20/12/2003 03:00:00", false)]
- [TestCase("20/12/2003 01:45:00", "30 3 * * *", "20/12/2003 03:30:00", false)]
+ [TestCase("01/01/2003 00:00:00", "2/5 * * * *", "01/01/2003 00:02:00", false)]
+ [TestCase("01/01/2003 00:02:00", "2/5 * * * *", "01/01/2003 00:07:00", false)]
+ [TestCase("01/01/2003 00:50:00", "2/5 * * * *", "01/01/2003 00:52:00", false)]
+ [TestCase("01/01/2003 00:52:00", "2/5 * * * *", "01/01/2003 00:57:00", false)]
+ [TestCase("01/01/2003 00:57:00", "2/5 * * * *", "01/01/2003 01:02:00", false)]
- // Day of month tests
+ // See: https://github.com/atifaziz/NCrontab/issues/90
+ [TestCase("24/02/2021 09:50:35", "* * 10 * * *", "24/02/2021 10:00:00", true)]
+ [TestCase("24/02/2021 09:50:35", "* 55 * * * *", "24/02/2021 09:55:00", true)]
- [TestCase("07/01/2003 00:00:00", "30 * 1 * *", "01/02/2003 00:30:00", false)]
- [TestCase("01/02/2003 00:30:00", "30 * 1 * *", "01/02/2003 01:30:00", false)]
-
- [TestCase("01/01/2003 00:00:00", "10 * 22 * *", "22/01/2003 00:10:00", false)]
- [TestCase("01/01/2003 00:00:00", "30 23 19 * *", "19/01/2003 23:30:00", false)]
- [TestCase("01/01/2003 00:00:00", "30 23 21 * *", "21/01/2003 23:30:00", false)]
- [TestCase("01/01/2003 00:01:00", " * * 21 * *", "21/01/2003 00:00:00", false)]
- [TestCase("10/07/2003 00:00:00", " * * 30,31 * *", "30/07/2003 00:00:00", false)]
+ [TestCase("01/01/2003 00:00:30", "3 45 * * * *", "01/01/2003 00:45:03", true)]
- // Test month rollovers for months with 28,29,30 and 31 days
+ [TestCase("01/01/2003 00:00:30", "6 45-47,48,49 * * * *", "01/01/2003 00:45:06", true)]
+ [TestCase("01/01/2003 00:45:30", "6 45-47,48,49 * * * *", "01/01/2003 00:46:06", true)]
+ [TestCase("01/01/2003 00:46:30", "6 45-47,48,49 * * * *", "01/01/2003 00:47:06", true)]
+ [TestCase("01/01/2003 00:47:30", "6 45-47,48,49 * * * *", "01/01/2003 00:48:06", true)]
+ [TestCase("01/01/2003 00:48:30", "6 45-47,48,49 * * * *", "01/01/2003 00:49:06", true)]
+ [TestCase("01/01/2003 00:49:30", "6 45-47,48,49 * * * *", "01/01/2003 01:45:06", true)]
- [TestCase("28/02/2002 23:59:59", "* * * 3 *", "01/03/2002 00:00:00", false)]
- [TestCase("29/02/2004 23:59:59", "* * * 3 *", "01/03/2004 00:00:00", false)]
- [TestCase("31/03/2002 23:59:59", "* * * 4 *", "01/04/2002 00:00:00", false)]
- [TestCase("30/04/2002 23:59:59", "* * * 5 *", "01/05/2002 00:00:00", false)]
+ [TestCase("01/01/2003 00:00:30", "9 2/5 * * * *", "01/01/2003 00:02:09", true)]
+ [TestCase("01/01/2003 00:02:30", "9 2/5 * * * *", "01/01/2003 00:07:09", true)]
+ [TestCase("01/01/2003 00:50:30", "9 2/5 * * * *", "01/01/2003 00:52:09", true)]
+ [TestCase("01/01/2003 00:52:30", "9 2/5 * * * *", "01/01/2003 00:57:09", true)]
+ [TestCase("01/01/2003 00:57:30", "9 2/5 * * * *", "01/01/2003 01:02:09", true)]
- // Test month 30,31 days
+ // Hour tests
- [TestCase("01/01/2000 00:00:00", "0 0 15,30,31 * *", "15/01/2000 00:00:00", false)]
- [TestCase("15/01/2000 00:00:00", "0 0 15,30,31 * *", "30/01/2000 00:00:00", false)]
- [TestCase("30/01/2000 00:00:00", "0 0 15,30,31 * *", "31/01/2000 00:00:00", false)]
- [TestCase("31/01/2000 00:00:00", "0 0 15,30,31 * *", "15/02/2000 00:00:00", false)]
+ [TestCase("20/12/2003 10:00:00", " * 3/4 * * *", "20/12/2003 11:00:00", false)]
+ [TestCase("20/12/2003 00:30:00", " * 3 * * *", "20/12/2003 03:00:00", false)]
+ [TestCase("20/12/2003 01:45:00", "30 3 * * *", "20/12/2003 03:30:00", false)]
- [TestCase("15/02/2000 00:00:00", "0 0 15,30,31 * *", "15/03/2000 00:00:00", false)]
+ // Day of month tests
- [TestCase("15/03/2000 00:00:00", "0 0 15,30,31 * *", "30/03/2000 00:00:00", false)]
- [TestCase("30/03/2000 00:00:00", "0 0 15,30,31 * *", "31/03/2000 00:00:00", false)]
- [TestCase("31/03/2000 00:00:00", "0 0 15,30,31 * *", "15/04/2000 00:00:00", false)]
+ [TestCase("07/01/2003 00:00:00", "30 * 1 * *", "01/02/2003 00:30:00", false)]
+ [TestCase("01/02/2003 00:30:00", "30 * 1 * *", "01/02/2003 01:30:00", false)]
- [TestCase("15/04/2000 00:00:00", "0 0 15,30,31 * *", "30/04/2000 00:00:00", false)]
- [TestCase("30/04/2000 00:00:00", "0 0 15,30,31 * *", "15/05/2000 00:00:00", false)]
+ [TestCase("01/01/2003 00:00:00", "10 * 22 * *", "22/01/2003 00:10:00", false)]
+ [TestCase("01/01/2003 00:00:00", "30 23 19 * *", "19/01/2003 23:30:00", false)]
+ [TestCase("01/01/2003 00:00:00", "30 23 21 * *", "21/01/2003 23:30:00", false)]
+ [TestCase("01/01/2003 00:01:00", " * * 21 * *", "21/01/2003 00:00:00", false)]
+ [TestCase("10/07/2003 00:00:00", " * * 30,31 * *", "30/07/2003 00:00:00", false)]
- [TestCase("15/05/2000 00:00:00", "0 0 15,30,31 * *", "30/05/2000 00:00:00", false)]
- [TestCase("30/05/2000 00:00:00", "0 0 15,30,31 * *", "31/05/2000 00:00:00", false)]
- [TestCase("31/05/2000 00:00:00", "0 0 15,30,31 * *", "15/06/2000 00:00:00", false)]
+ // Test month rollovers for months with 28,29,30 and 31 days
- [TestCase("15/06/2000 00:00:00", "0 0 15,30,31 * *", "30/06/2000 00:00:00", false)]
- [TestCase("30/06/2000 00:00:00", "0 0 15,30,31 * *", "15/07/2000 00:00:00", false)]
+ [TestCase("28/02/2002 23:59:59", "* * * 3 *", "01/03/2002 00:00:00", false)]
+ [TestCase("29/02/2004 23:59:59", "* * * 3 *", "01/03/2004 00:00:00", false)]
+ [TestCase("31/03/2002 23:59:59", "* * * 4 *", "01/04/2002 00:00:00", false)]
+ [TestCase("30/04/2002 23:59:59", "* * * 5 *", "01/05/2002 00:00:00", false)]
- [TestCase("15/07/2000 00:00:00", "0 0 15,30,31 * *", "30/07/2000 00:00:00", false)]
- [TestCase("30/07/2000 00:00:00", "0 0 15,30,31 * *", "31/07/2000 00:00:00", false)]
- [TestCase("31/07/2000 00:00:00", "0 0 15,30,31 * *", "15/08/2000 00:00:00", false)]
+ // Test month 30,31 days
- [TestCase("15/08/2000 00:00:00", "0 0 15,30,31 * *", "30/08/2000 00:00:00", false)]
- [TestCase("30/08/2000 00:00:00", "0 0 15,30,31 * *", "31/08/2000 00:00:00", false)]
- [TestCase("31/08/2000 00:00:00", "0 0 15,30,31 * *", "15/09/2000 00:00:00", false)]
+ [TestCase("01/01/2000 00:00:00", "0 0 15,30,31 * *", "15/01/2000 00:00:00", false)]
+ [TestCase("15/01/2000 00:00:00", "0 0 15,30,31 * *", "30/01/2000 00:00:00", false)]
+ [TestCase("30/01/2000 00:00:00", "0 0 15,30,31 * *", "31/01/2000 00:00:00", false)]
+ [TestCase("31/01/2000 00:00:00", "0 0 15,30,31 * *", "15/02/2000 00:00:00", false)]
- [TestCase("15/09/2000 00:00:00", "0 0 15,30,31 * *", "30/09/2000 00:00:00", false)]
- [TestCase("30/09/2000 00:00:00", "0 0 15,30,31 * *", "15/10/2000 00:00:00", false)]
+ [TestCase("15/02/2000 00:00:00", "0 0 15,30,31 * *", "15/03/2000 00:00:00", false)]
- [TestCase("15/10/2000 00:00:00", "0 0 15,30,31 * *", "30/10/2000 00:00:00", false)]
- [TestCase("30/10/2000 00:00:00", "0 0 15,30,31 * *", "31/10/2000 00:00:00", false)]
- [TestCase("31/10/2000 00:00:00", "0 0 15,30,31 * *", "15/11/2000 00:00:00", false)]
+ [TestCase("15/03/2000 00:00:00", "0 0 15,30,31 * *", "30/03/2000 00:00:00", false)]
+ [TestCase("30/03/2000 00:00:00", "0 0 15,30,31 * *", "31/03/2000 00:00:00", false)]
+ [TestCase("31/03/2000 00:00:00", "0 0 15,30,31 * *", "15/04/2000 00:00:00", false)]
- [TestCase("15/11/2000 00:00:00", "0 0 15,30,31 * *", "30/11/2000 00:00:00", false)]
- [TestCase("30/11/2000 00:00:00", "0 0 15,30,31 * *", "15/12/2000 00:00:00", false)]
+ [TestCase("15/04/2000 00:00:00", "0 0 15,30,31 * *", "30/04/2000 00:00:00", false)]
+ [TestCase("30/04/2000 00:00:00", "0 0 15,30,31 * *", "15/05/2000 00:00:00", false)]
- [TestCase("15/12/2000 00:00:00", "0 0 15,30,31 * *", "30/12/2000 00:00:00", false)]
- [TestCase("30/12/2000 00:00:00", "0 0 15,30,31 * *", "31/12/2000 00:00:00", false)]
- [TestCase("31/12/2000 00:00:00", "0 0 15,30,31 * *", "15/01/2001 00:00:00", false)]
+ [TestCase("15/05/2000 00:00:00", "0 0 15,30,31 * *", "30/05/2000 00:00:00", false)]
+ [TestCase("30/05/2000 00:00:00", "0 0 15,30,31 * *", "31/05/2000 00:00:00", false)]
+ [TestCase("31/05/2000 00:00:00", "0 0 15,30,31 * *", "15/06/2000 00:00:00", false)]
- // Other month tests (including year rollover)
+ [TestCase("15/06/2000 00:00:00", "0 0 15,30,31 * *", "30/06/2000 00:00:00", false)]
+ [TestCase("30/06/2000 00:00:00", "0 0 15,30,31 * *", "15/07/2000 00:00:00", false)]
- [TestCase("01/12/2003 05:00:00", "10 * * 6 *", "01/06/2004 00:10:00", false)]
- [TestCase("04/01/2003 00:00:00", " 1 2 3 * *", "03/02/2003 02:01:00", false)]
- [TestCase("01/07/2002 05:00:00", "10 * * February,April-Jun *", "01/02/2003 00:10:00", false)]
- [TestCase("01/01/2003 00:00:00", "0 12 1 6 *", "01/06/2003 12:00:00", false)]
- [TestCase("11/09/1988 14:23:00", "* 12 1 6 *", "01/06/1989 12:00:00", false)]
- [TestCase("11/03/1988 14:23:00", "* 12 1 6 *", "01/06/1988 12:00:00", false)]
- [TestCase("11/03/1988 14:23:00", "* 2,4-8,15 * 6 *", "01/06/1988 02:00:00", false)]
- [TestCase("11/03/1988 14:23:00", "20 * * january,FeB,Mar,april,May,JuNE,July,Augu,SEPT-October,Nov,DECEM *", "11/03/1988 15:20:00", false)]
+ [TestCase("15/07/2000 00:00:00", "0 0 15,30,31 * *", "30/07/2000 00:00:00", false)]
+ [TestCase("30/07/2000 00:00:00", "0 0 15,30,31 * *", "31/07/2000 00:00:00", false)]
+ [TestCase("31/07/2000 00:00:00", "0 0 15,30,31 * *", "15/08/2000 00:00:00", false)]
- // Day of week tests
+ [TestCase("15/08/2000 00:00:00", "0 0 15,30,31 * *", "30/08/2000 00:00:00", false)]
+ [TestCase("30/08/2000 00:00:00", "0 0 15,30,31 * *", "31/08/2000 00:00:00", false)]
+ [TestCase("31/08/2000 00:00:00", "0 0 15,30,31 * *", "15/09/2000 00:00:00", false)]
- [TestCase("26/06/2003 10:00:00", "30 6 * * 0", "29/06/2003 06:30:00", false)]
- [TestCase("26/06/2003 10:00:00", "30 6 * * sunday", "29/06/2003 06:30:00", false)]
- [TestCase("26/06/2003 10:00:00", "30 6 * * SUNDAY", "29/06/2003 06:30:00", false)]
- [TestCase("19/06/2003 00:00:00", "1 12 * * 2", "24/06/2003 12:01:00", false)]
- [TestCase("24/06/2003 12:01:00", "1 12 * * 2", "01/07/2003 12:01:00", false)]
+ [TestCase("15/09/2000 00:00:00", "0 0 15,30,31 * *", "30/09/2000 00:00:00", false)]
+ [TestCase("30/09/2000 00:00:00", "0 0 15,30,31 * *", "15/10/2000 00:00:00", false)]
- [TestCase("01/06/2003 14:55:00", "15 18 * * Mon", "02/06/2003 18:15:00", false)]
- [TestCase("02/06/2003 18:15:00", "15 18 * * Mon", "09/06/2003 18:15:00", false)]
- [TestCase("09/06/2003 18:15:00", "15 18 * * Mon", "16/06/2003 18:15:00", false)]
- [TestCase("16/06/2003 18:15:00", "15 18 * * Mon", "23/06/2003 18:15:00", false)]
- [TestCase("23/06/2003 18:15:00", "15 18 * * Mon", "30/06/2003 18:15:00", false)]
- [TestCase("30/06/2003 18:15:00", "15 18 * * Mon", "07/07/2003 18:15:00", false)]
+ [TestCase("15/10/2000 00:00:00", "0 0 15,30,31 * *", "30/10/2000 00:00:00", false)]
+ [TestCase("30/10/2000 00:00:00", "0 0 15,30,31 * *", "31/10/2000 00:00:00", false)]
+ [TestCase("31/10/2000 00:00:00", "0 0 15,30,31 * *", "15/11/2000 00:00:00", false)]
- [TestCase("01/01/2003 00:00:00", "* * * * Mon", "06/01/2003 00:00:00", false)]
- [TestCase("01/01/2003 12:00:00", "45 16 1 * Mon", "01/09/2003 16:45:00", false)]
- [TestCase("01/09/2003 23:45:00", "45 16 1 * Mon", "01/12/2003 16:45:00", false)]
+ [TestCase("15/11/2000 00:00:00", "0 0 15,30,31 * *", "30/11/2000 00:00:00", false)]
+ [TestCase("30/11/2000 00:00:00", "0 0 15,30,31 * *", "15/12/2000 00:00:00", false)]
- // Leap year tests
+ [TestCase("15/12/2000 00:00:00", "0 0 15,30,31 * *", "30/12/2000 00:00:00", false)]
+ [TestCase("30/12/2000 00:00:00", "0 0 15,30,31 * *", "31/12/2000 00:00:00", false)]
+ [TestCase("31/12/2000 00:00:00", "0 0 15,30,31 * *", "15/01/2001 00:00:00", false)]
- [TestCase("01/01/2000 12:00:00", "1 12 29 2 *", "29/02/2000 12:01:00", false)]
- [TestCase("29/02/2000 12:01:00", "1 12 29 2 *", "29/02/2004 12:01:00", false)]
- [TestCase("29/02/2004 12:01:00", "1 12 29 2 *", "29/02/2008 12:01:00", false)]
+ // Other month tests (including year rollover)
- // Non-leap year tests
+ [TestCase("01/12/2003 05:00:00", "10 * * 6 *", "01/06/2004 00:10:00", false)]
+ [TestCase("04/01/2003 00:00:00", " 1 2 3 * *", "03/02/2003 02:01:00", false)]
+ [TestCase("01/07/2002 05:00:00", "10 * * February,April-Jun *", "01/02/2003 00:10:00", false)]
+ [TestCase("01/01/2003 00:00:00", "0 12 1 6 *", "01/06/2003 12:00:00", false)]
+ [TestCase("11/09/1988 14:23:00", "* 12 1 6 *", "01/06/1989 12:00:00", false)]
+ [TestCase("11/03/1988 14:23:00", "* 12 1 6 *", "01/06/1988 12:00:00", false)]
+ [TestCase("11/03/1988 14:23:00", "* 2,4-8,15 * 6 *", "01/06/1988 02:00:00", false)]
+ [TestCase("11/03/1988 14:23:00", "20 * * january,FeB,Mar,april,May,JuNE,July,Augu,SEPT-October,Nov,DECEM *", "11/03/1988 15:20:00", false)]
- [TestCase("01/01/2000 12:00:00", "1 12 28 2 *", "28/02/2000 12:01:00", false)]
- [TestCase("28/02/2000 12:01:00", "1 12 28 2 *", "28/02/2001 12:01:00", false)]
- [TestCase("28/02/2001 12:01:00", "1 12 28 2 *", "28/02/2002 12:01:00", false)]
- [TestCase("28/02/2002 12:01:00", "1 12 28 2 *", "28/02/2003 12:01:00", false)]
- [TestCase("28/02/2003 12:01:00", "1 12 28 2 *", "28/02/2004 12:01:00", false)]
- [TestCase("29/02/2004 12:01:00", "1 12 28 2 *", "28/02/2005 12:01:00", false)]
+ // Day of week tests
- [TestCase("01/01/2000 12:00:00", "40 14/1 * * *", "01/01/2000 14:40:00", false)]
- [TestCase("01/01/2000 14:40:00", "40 14/1 * * *", "01/01/2000 15:40:00", false)]
+ [TestCase("26/06/2003 10:00:00", "30 6 * * 0", "29/06/2003 06:30:00", false)]
+ [TestCase("26/06/2003 10:00:00", "30 6 * * sunday", "29/06/2003 06:30:00", false)]
+ [TestCase("26/06/2003 10:00:00", "30 6 * * SUNDAY", "29/06/2003 06:30:00", false)]
+ [TestCase("19/06/2003 00:00:00", "1 12 * * 2", "24/06/2003 12:01:00", false)]
+ [TestCase("24/06/2003 12:01:00", "1 12 * * 2", "01/07/2003 12:01:00", false)]
- public void Evaluations(string startTimeString, string cronExpression, string nextTimeString, bool includingSeconds)
- {
- CronCall(startTimeString, cronExpression, nextTimeString, new ParseOptions
- {
- IncludingSeconds = includingSeconds
- });
- }
-
- [TestCase(" * * * * * ", "01/01/2003 00:00:00", "01/01/2003 00:00:00" , false)]
- [TestCase(" * * * * * ", "31/12/2002 23:59:59", "01/01/2003 00:00:00" , false)]
- [TestCase(" * * * * Mon", "31/12/2002 23:59:59", "01/01/2003 00:00:00" , false)]
- [TestCase(" * * * * Mon", "01/01/2003 00:00:00", "02/01/2003 00:00:00" , false)]
- [TestCase(" * * * * Mon", "01/01/2003 00:00:00", "02/01/2003 12:00:00" , false)]
- [TestCase("30 12 * * Mon", "01/01/2003 00:00:00", "06/01/2003 12:00:00" , false)]
-
- [TestCase(" * * * * * * ", "01/01/2003 00:00:00", "01/01/2003 00:00:00", true )]
- [TestCase(" * * * * * * ", "31/12/2002 23:59:59", "01/01/2003 00:00:00", true )]
- [TestCase(" * * * * * Mon", "31/12/2002 23:59:59", "01/01/2003 00:00:00", true )]
- [TestCase(" * * * * * Mon", "01/01/2003 00:00:00", "02/01/2003 00:00:00", true )]
- [TestCase(" * * * * * Mon", "01/01/2003 00:00:00", "02/01/2003 12:00:00", true )]
- [TestCase("10 30 12 * * Mon", "01/01/2003 00:00:00", "06/01/2003 12:00:10", true )]
-
- public void FiniteOccurrences(string cronExpression, string startTimeString, string endTimeString, bool includingSeconds)
- {
- CronFinite(cronExpression, startTimeString, endTimeString, new ParseOptions
- {
- IncludingSeconds = includingSeconds
- });
- }
-
- //
- // Test to check we don't loop indefinitely looking for a February
- // 31st because no such date would ever exist!
- //
-
- [Category("Performance")]
-#if NETCOREAPP1_0
- [Ignore("Timeout attribute missing from NUnit for .NET Core.")]
-#else
- [Timeout(1000)]
-#endif
- [TestCase("* * 31 Feb *", false)]
- [TestCase("* * * 31 Feb *", true)]
- public void DontLoopIndefinitely(string expression, bool includingSeconds)
+ [TestCase("01/06/2003 14:55:00", "15 18 * * Mon", "02/06/2003 18:15:00", false)]
+ [TestCase("02/06/2003 18:15:00", "15 18 * * Mon", "09/06/2003 18:15:00", false)]
+ [TestCase("09/06/2003 18:15:00", "15 18 * * Mon", "16/06/2003 18:15:00", false)]
+ [TestCase("16/06/2003 18:15:00", "15 18 * * Mon", "23/06/2003 18:15:00", false)]
+ [TestCase("23/06/2003 18:15:00", "15 18 * * Mon", "30/06/2003 18:15:00", false)]
+ [TestCase("30/06/2003 18:15:00", "15 18 * * Mon", "07/07/2003 18:15:00", false)]
+
+ [TestCase("01/01/2003 00:00:00", "* * * * Mon", "06/01/2003 00:00:00", false)]
+ [TestCase("01/01/2003 12:00:00", "45 16 1 * Mon", "01/09/2003 16:45:00", false)]
+ [TestCase("01/09/2003 23:45:00", "45 16 1 * Mon", "01/12/2003 16:45:00", false)]
+
+ // Leap year tests
+
+ [TestCase("01/01/2000 12:00:00", "1 12 29 2 *", "29/02/2000 12:01:00", false)]
+ [TestCase("29/02/2000 12:01:00", "1 12 29 2 *", "29/02/2004 12:01:00", false)]
+ [TestCase("29/02/2004 12:01:00", "1 12 29 2 *", "29/02/2008 12:01:00", false)]
+
+ // Non-leap year tests
+
+ [TestCase("01/01/2000 12:00:00", "1 12 28 2 *", "28/02/2000 12:01:00", false)]
+ [TestCase("28/02/2000 12:01:00", "1 12 28 2 *", "28/02/2001 12:01:00", false)]
+ [TestCase("28/02/2001 12:01:00", "1 12 28 2 *", "28/02/2002 12:01:00", false)]
+ [TestCase("28/02/2002 12:01:00", "1 12 28 2 *", "28/02/2003 12:01:00", false)]
+ [TestCase("28/02/2003 12:01:00", "1 12 28 2 *", "28/02/2004 12:01:00", false)]
+ [TestCase("29/02/2004 12:01:00", "1 12 28 2 *", "28/02/2005 12:01:00", false)]
+
+ [TestCase("01/01/2000 12:00:00", "40 14/1 * * *", "01/01/2000 14:40:00", false)]
+ [TestCase("01/01/2000 14:40:00", "40 14/1 * * *", "01/01/2000 15:40:00", false)]
+
+ public void Evaluations(string startTimeString, string cronExpression, string nextTimeString, bool includingSeconds)
+ {
+ CronCall(startTimeString, cronExpression, nextTimeString, new ParseOptions
{
- CronFinite(expression, "01/01/2001 00:00:00", "01/01/2010 00:00:00", new ParseOptions
- {
- IncludingSeconds = includingSeconds
- });
- }
+ IncludingSeconds = includingSeconds
+ });
+ }
- static void BadField(string expression, bool includingSeconds)
+ [TestCase(" * * * * * ", "01/01/2003 00:00:00", "01/01/2003 00:00:00" , false)]
+ [TestCase(" * * * * * ", "31/12/2002 23:59:59", "01/01/2003 00:00:00" , false)]
+ [TestCase(" * * * * Mon", "31/12/2002 23:59:59", "01/01/2003 00:00:00" , false)]
+ [TestCase(" * * * * Mon", "01/01/2003 00:00:00", "02/01/2003 00:00:00" , false)]
+ [TestCase(" * * * * Mon", "01/01/2003 00:00:00", "02/01/2003 12:00:00" , false)]
+ [TestCase("30 12 * * Mon", "01/01/2003 00:00:00", "06/01/2003 12:00:00" , false)]
+
+ [TestCase(" * * * * * * ", "01/01/2003 00:00:00", "01/01/2003 00:00:00", true )]
+ [TestCase(" * * * * * * ", "31/12/2002 23:59:59", "01/01/2003 00:00:00", true )]
+ [TestCase(" * * * * * Mon", "31/12/2002 23:59:59", "01/01/2003 00:00:00", true )]
+ [TestCase(" * * * * * Mon", "01/01/2003 00:00:00", "02/01/2003 00:00:00", true )]
+ [TestCase(" * * * * * Mon", "01/01/2003 00:00:00", "02/01/2003 12:00:00", true )]
+ [TestCase("10 30 12 * * Mon", "01/01/2003 00:00:00", "06/01/2003 12:00:10", true )]
+
+ public void FiniteOccurrences(string cronExpression, string startTimeString, string endTimeString, bool includingSeconds)
+ {
+ CronFinite(cronExpression, startTimeString, endTimeString, new ParseOptions
{
- Assert.Throws(() => CrontabSchedule.Parse(expression, new ParseOptions
- {
- IncludingSeconds = includingSeconds
- }));
- Assert.That(CrontabSchedule.TryParse(expression, new ParseOptions
- {
- IncludingSeconds = includingSeconds
- }), Is.Null);
- }
-
- [TestCase("bad * * * * *", false)]
- public void BadSecondsField(string expression, bool includingSeconds) =>
- BadField(expression, includingSeconds);
-
- [TestCase("bad * * * *", false)]
- [TestCase("* bad * * * *", true)]
- public void BadMinutesField(string expression, bool includingSeconds) =>
- BadField(expression, includingSeconds);
-
- [TestCase("* bad * * *", false)]
- [TestCase("* * bad * * *", true)]
- public void BadHoursField(string expression, bool includingSeconds) =>
- BadField(expression, includingSeconds);
-
- [TestCase("* * bad * *", false)]
- [TestCase("* * * bad * *", true)]
- public void BadDayField(string expression, bool includingSeconds) =>
- BadField(expression, includingSeconds);
-
- [TestCase("* * * bad *", false)]
- [TestCase("* * * * bad *", true)]
- public void BadMonthField(string expression, bool includingSeconds) =>
- BadField(expression, includingSeconds);
-
- [TestCase("* * * * mon,bad,wed", false)]
- [TestCase("* * * * * mon,bad,wed", true)]
- public void BadDayOfWeekField(string expression, bool includingSeconds) =>
- BadField(expression, includingSeconds);
-
- [TestCase("* 1,2,3,456,7,8,9 * * *", false)]
- [TestCase("* * 1,2,3,456,7,8,9 * * *", true)]
- public void OutOfRangeField(string expression, bool includingSeconds) =>
- BadField(expression, includingSeconds);
-
- [TestCase("* 1,Z,3,4 * * *", false)]
- [TestCase("* * 1,Z,3,4 * * *", true)]
- public void NonNumberValueInNumericOnlyField(string expression, bool includingSeconds) =>
- BadField(expression, includingSeconds);
-
- [TestCase("* 1/Z * * *", false)]
- [TestCase("* * 1/Z * * *", true)]
- public void NonNumericFieldInterval(string expression, bool includingSeconds) =>
- BadField(expression, includingSeconds);
-
- [TestCase("* 3-l2 * * *", false)]
- [TestCase("* * 3-l2 * * *", true)]
- public void NonNumericFieldRangeComponent(string expression, bool includingSeconds) =>
- BadField(expression, includingSeconds);
-
- ///
- /// Test case for
- /// issue
- /// #21 (GetNextOccurrence throws if next occurrence produces
- /// invalid time).
- ///
-
- [Test]
- public void GetNextOccurrences_NextOccurrenceInvalidTime_ShouldStopAtLastValidTime()
+ IncludingSeconds = includingSeconds
+ });
+ }
+
+ //
+ // Test to check we don't loop indefinitely looking for a February
+ // 31st because no such date would ever exist!
+ //
+
+ [Category("Performance")]
+ [Timeout(1000)]
+ [TestCase("* * 31 Feb *", false)]
+ [TestCase("* * * 31 Feb *", true)]
+ public void DontLoopIndefinitely(string expression, bool includingSeconds)
+ {
+ CronFinite(expression, "01/01/2001 00:00:00", "01/01/2010 00:00:00", new ParseOptions
{
- var schedule = CrontabSchedule.Parse("0 0 29 Feb Mon");
- var occurrences = schedule.GetNextOccurrences(new DateTime(9988, 1, 1), DateTime.MaxValue);
- Assert.AreEqual(new DateTime(9988, 2, 29), occurrences.Last());
- }
-
- // Instead of using strings and parsing as date,
- // consider NUnit's TestCaseData:
- // https://github.com/nunit/docs/wiki/TestCaseData
-
- [TestCase("0 0 29 Feb Mon", "2017-01-01", "2017-12-31", "2017-12-31")]
- [TestCase("0 0 29 Feb Mon", "9000-01-01", "9008-12-31", "9008-02-29")]
- public void GetNextOccurence(string expression, string startDate, string endDate, string expectedValue)
+ IncludingSeconds = includingSeconds
+ });
+ }
+
+ static void BadField(string expression, bool includingSeconds)
+ {
+ var options = new ParseOptions
{
- var schedule = CrontabSchedule.Parse(expression);
- var start = Time(startDate);
- var end = Time(endDate);
- var expected = Time(expectedValue);
+ IncludingSeconds = includingSeconds
+ };
+ Assert.That(() => CrontabSchedule.Parse(expression, options),
+ Throws.TypeOf());
+ Assert.That(CrontabSchedule.TryParse(expression, options), Is.Null);
+ }
- var occurrence = schedule.GetNextOccurrence(start, end);
+ [TestCase("bad * * * * *", false)]
+ public void BadSecondsField(string expression, bool includingSeconds) =>
+ BadField(expression, includingSeconds);
+
+ [TestCase("bad * * * *", false)]
+ [TestCase("* bad * * * *", true)]
+ public void BadMinutesField(string expression, bool includingSeconds) =>
+ BadField(expression, includingSeconds);
+
+ [TestCase("* bad * * *", false)]
+ [TestCase("* * bad * * *", true)]
+ public void BadHoursField(string expression, bool includingSeconds) =>
+ BadField(expression, includingSeconds);
+
+ [TestCase("* * bad * *", false)]
+ [TestCase("* * * bad * *", true)]
+ public void BadDayField(string expression, bool includingSeconds) =>
+ BadField(expression, includingSeconds);
+
+ [TestCase("* * * bad *", false)]
+ [TestCase("* * * * bad *", true)]
+ public void BadMonthField(string expression, bool includingSeconds) =>
+ BadField(expression, includingSeconds);
+
+ [TestCase("* * * * mon,bad,wed", false)]
+ [TestCase("* * * * * mon,bad,wed", true)]
+ public void BadDayOfWeekField(string expression, bool includingSeconds) =>
+ BadField(expression, includingSeconds);
+
+ [TestCase("* 1,2,3,456,7,8,9 * * *", false)]
+ [TestCase("* * 1,2,3,456,7,8,9 * * *", true)]
+ public void OutOfRangeField(string expression, bool includingSeconds) =>
+ BadField(expression, includingSeconds);
+
+ [TestCase("* 1,Z,3,4 * * *", false)]
+ [TestCase("* * 1,Z,3,4 * * *", true)]
+ public void NonNumberValueInNumericOnlyField(string expression, bool includingSeconds) =>
+ BadField(expression, includingSeconds);
+
+ [TestCase("* 1/Z * * *", false)]
+ [TestCase("* * 1/Z * * *", true)]
+ public void NonNumericFieldInterval(string expression, bool includingSeconds) =>
+ BadField(expression, includingSeconds);
+
+ [TestCase("* 3-l2 * * *", false)]
+ [TestCase("* * 3-l2 * * *", true)]
+ public void NonNumericFieldRangeComponent(string expression, bool includingSeconds) =>
+ BadField(expression, includingSeconds);
+
+ ///
+ /// Test case for
+ /// issue
+ /// #21 (GetNextOccurrence throws if next occurrence produces
+ /// invalid time).
+ ///
+
+ [Test]
+ public void GetNextOccurrences_NextOccurrenceInvalidTime_ShouldStopAtLastValidTime()
+ {
+ var schedule = CrontabSchedule.Parse("0 0 29 Feb Mon");
+ var occurrences = schedule.GetNextOccurrences(new DateTime(9988, 1, 1), DateTime.MaxValue);
+ Assert.That(occurrences.Last(), Is.EqualTo(new DateTime(9988, 2, 29)));
+ }
- Assert.AreEqual(expected, occurrence);
- }
+ // Instead of using strings and parsing as date,
+ // consider NUnit's TestCaseData:
+ // https://github.com/nunit/docs/wiki/TestCaseData
+ [TestCase("0 0 29 Feb Mon", "2017-01-01", "2017-12-31", "2017-12-31")]
+ [TestCase("0 0 29 Feb Mon", "9000-01-01", "9008-12-31", "9008-02-29")]
+ public void GetNextOccurrence(string expression, string startDate, string endDate, string expectedValue)
+ {
+ var schedule = CrontabSchedule.Parse(expression);
+ var start = Time(startDate);
+ var end = Time(endDate);
+ var expected = Time(expectedValue);
- static void CronCall(string startTimeString, string cronExpression, string nextTimeString, ParseOptions options)
- {
- var schedule = CrontabSchedule.Parse(cronExpression, options);
- var next = schedule.GetNextOccurrence(Time(startTimeString));
+ var occurrence = schedule.GetNextOccurrence(start, end);
- Assert.AreEqual(nextTimeString, TimeString(next),
- "Occurrence of <{0}> after <{1}>.", cronExpression, startTimeString);
- }
+ Assert.That(occurrence, Is.EqualTo(expected));
+ }
- static void CronFinite(string cronExpression, string startTimeString, string endTimeString, ParseOptions options)
- {
- var schedule = CrontabSchedule.Parse(cronExpression, options);
- var occurrence = schedule.GetNextOccurrence(Time(startTimeString), Time(endTimeString));
+ [Test]
+ public void GetNextOccurrencesWithNullSchedule()
+ {
+ // Overload 1
- Assert.AreEqual(endTimeString, TimeString(occurrence),
- "Occurrence of <{0}> after <{1}> did not terminate with <{2}>.",
- cronExpression, startTimeString, endTimeString);
- }
+ Assert.That(() => CrontabScheduleExtensions.GetNextOccurrences(null!, DateTime.MinValue, DateTime.MaxValue),
+ Throws.ArgumentNullException
+ .With.Property(nameof(ArgumentNullException.ParamName)).EqualTo("schedules"));
- static string TimeString(DateTime time) => time.ToString(TimeFormat, CultureInfo.InvariantCulture);
- static DateTime Time(string str) => DateTime.ParseExact(str, TimeFormats, CultureInfo.InvariantCulture, DateTimeStyles.None);
+ // Overload 2
+
+ Assert.That(() => CrontabScheduleExtensions.GetNextOccurrences