initial commit
This commit is contained in:
		
							
								
								
									
										12
									
								
								Demo/Demo.csproj
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								Demo/Demo.csproj
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
<Project Sdk="Microsoft.NET.Sdk">
 | 
			
		||||
 | 
			
		||||
  <PropertyGroup>
 | 
			
		||||
    <OutputType>Exe</OutputType>
 | 
			
		||||
    <TargetFrameworks>net48;net6.0</TargetFrameworks>
 | 
			
		||||
  </PropertyGroup>
 | 
			
		||||
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <ProjectReference Include="..\src\CronTimer.csproj" />
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
 | 
			
		||||
</Project>
 | 
			
		||||
							
								
								
									
										22
									
								
								Demo/Program.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								Demo/Program.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
namespace Demo
 | 
			
		||||
{
 | 
			
		||||
    class Program
 | 
			
		||||
    {
 | 
			
		||||
        static void Main()
 | 
			
		||||
        {
 | 
			
		||||
            var expression = "0-30/5 * * * * *";
 | 
			
		||||
            Console.WriteLine(expression);
 | 
			
		||||
            var timer = new CronTimer(expression, "Asia/Hong_Kong", includingSeconds: true);
 | 
			
		||||
            timer.OnOccurence += (s, ea) => Console.WriteLine($"{ea.At:T} - {DateTime.Now}");
 | 
			
		||||
            timer.Start();
 | 
			
		||||
 | 
			
		||||
            while (Console.ReadKey().Key != ConsoleKey.Escape)
 | 
			
		||||
            {
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            timer.Stop();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										16
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
# CronTimer
 | 
			
		||||
 | 
			
		||||
Simple .net Timer that is based on cron expressions with second accuracy to fire timer events to a very specific schedule.
 | 
			
		||||
 | 
			
		||||
Regular timers are very useful for tasks that do not really require any precision like polling a service at a rough interval but sometimes there is a need for more precision based on time. There is already a great time schedule expression syntax that originated from [Cron](https://en.wikipedia.org/wiki/Cron). Normally you would likely schedule such jobs via the operating system if they are things like on every Friday at 18:00 run a report but there are schedules that make more sense to have running in process like to not have a certain overhead of launching a whole job process or just because the process is already running. This small library makes it super easy to define such timers on a specific schedule.
 | 
			
		||||
 | 
			
		||||
## Example
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Fire a timer event every 10 minutes from Monday through Friday between 8:00 and 17:00
 | 
			
		||||
 | 
			
		||||
```c#
 | 
			
		||||
var timer = new CronTimer("*/10 08-17 * * 1-5", "Europe/Amsterdam", includingSeconds: false);
 | 
			
		||||
timer.OnOccurence += (s, ea) => Console.Out.WriteLineAsync(ea + " - " + DateTime.Now);
 | 
			
		||||
timer.Start();
 | 
			
		||||
```
 | 
			
		||||
							
								
								
									
										6
									
								
								src/CronEventArgs.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/CronEventArgs.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
public class CronTimerEventArgs : EventArgs
 | 
			
		||||
{
 | 
			
		||||
    public DateTime At { get; set; }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										78
									
								
								src/CronTimer.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								src/CronTimer.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,78 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
using NCrontab;
 | 
			
		||||
 | 
			
		||||
public class CronTimer
 | 
			
		||||
{
 | 
			
		||||
    public const string UTC = "Etc/UTC";
 | 
			
		||||
 | 
			
		||||
    static readonly TimeSpan InfiniteTimeSpan = TimeSpan.FromMilliseconds(Timeout.Infinite); // net 3.5
 | 
			
		||||
 | 
			
		||||
    readonly CrontabSchedule schedule;
 | 
			
		||||
    readonly TimeZoneInfo tzi;
 | 
			
		||||
    readonly string id;
 | 
			
		||||
    readonly Timer t;
 | 
			
		||||
 | 
			
		||||
    public string tz { get; }
 | 
			
		||||
    public string Expression { get; }
 | 
			
		||||
    public event EventHandler<CronTimerEventArgs> OnOccurence;
 | 
			
		||||
 | 
			
		||||
    public DateTime Next { get; private set; }
 | 
			
		||||
 | 
			
		||||
    public CronTimer(string expression, string tz = UTC, bool includingSeconds = false)
 | 
			
		||||
    {
 | 
			
		||||
        Expression = expression;
 | 
			
		||||
        this.tz = tz;
 | 
			
		||||
        id = TimeZoneConverter.TZConvert.IanaToWindows(tz);
 | 
			
		||||
        tzi = TimeZoneInfo.FindSystemTimeZoneById(id);
 | 
			
		||||
        schedule = CrontabSchedule.Parse(expression, new CrontabSchedule.ParseOptions { IncludingSeconds = includingSeconds });
 | 
			
		||||
        Next = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, tzi);
 | 
			
		||||
        OnOccurence += OnOccurenceScheduleNext;
 | 
			
		||||
        t = new Timer(s =>
 | 
			
		||||
        {
 | 
			
		||||
            var ea = new CronTimerEventArgs
 | 
			
		||||
            {
 | 
			
		||||
                At = Next
 | 
			
		||||
            };
 | 
			
		||||
            OnOccurence(this, ea);
 | 
			
		||||
        }, null, InfiniteTimeSpan, InfiniteTimeSpan);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void OnOccurenceScheduleNext(object sender, EventArgs e)
 | 
			
		||||
    {
 | 
			
		||||
        var delay = CalculateDelay();
 | 
			
		||||
        //Console.WriteLine($"Next for [{tz} {expression}] in {delay}.");
 | 
			
		||||
        t.Change(delay, InfiniteTimeSpan);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Start()
 | 
			
		||||
    {
 | 
			
		||||
        var delay = CalculateDelay();
 | 
			
		||||
        //Console.WriteLine($"Next for [{tz} {expression}] in {delay}.");
 | 
			
		||||
        t.Change(delay, InfiniteTimeSpan);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    TimeSpan CalculateDelay()
 | 
			
		||||
    {
 | 
			
		||||
        var nowUtc = DateTime.UtcNow;
 | 
			
		||||
        Next = schedule.GetNextOccurrence(Next);
 | 
			
		||||
        TimeSpan delay;
 | 
			
		||||
        if (tz != UTC)
 | 
			
		||||
        {
 | 
			
		||||
            var nextUtc = TimeZoneInfo.ConvertTimeToUtc(Next, tzi);
 | 
			
		||||
            delay = nextUtc - nowUtc;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            delay = Next - nowUtc;
 | 
			
		||||
        }
 | 
			
		||||
        //Console.WriteLine($"Now: {nowUtc} [utc] {now} [{tz}], Next: {next} [{tz}] {nextUtc} [utc], Delay: {delay}");
 | 
			
		||||
        if (delay < TimeSpan.Zero) delay = TimeSpan.Zero;
 | 
			
		||||
        return delay;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Stop()
 | 
			
		||||
    {
 | 
			
		||||
        t.Change(InfiniteTimeSpan, InfiniteTimeSpan);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										49
									
								
								src/CronTimer.csproj
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								src/CronTimer.csproj
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,49 @@
 | 
			
		||||
<Project Sdk="Microsoft.NET.Sdk">
 | 
			
		||||
 | 
			
		||||
  <PropertyGroup>
 | 
			
		||||
    <LangVersion>latest</LangVersion>
 | 
			
		||||
    <TargetFrameworks>net461;netstandard2.1</TargetFrameworks>
 | 
			
		||||
    <PlatformTarget>AnyCPU</PlatformTarget>
 | 
			
		||||
  </PropertyGroup>
 | 
			
		||||
 | 
			
		||||
  <PropertyGroup>
 | 
			
		||||
    <!-- AssemblyFileVersionAttribute -->
 | 
			
		||||
    <FileVersion>2.0.0</FileVersion>
 | 
			
		||||
    <!-- AssemblyInformationalVersionAttribute -->
 | 
			
		||||
    <Version>$(FileVersion)</Version>
 | 
			
		||||
    <!-- AssemblyVersionAttribute -->
 | 
			
		||||
    <AssemblyVersion>2.0.0.0</AssemblyVersion>
 | 
			
		||||
    <!-- Nuget -->
 | 
			
		||||
    <PackageVersion>$(Version)</PackageVersion>
 | 
			
		||||
    <PackageId>CronTimer</PackageId>
 | 
			
		||||
    <Company>https://github.com/ramonsmits</Company>
 | 
			
		||||
    <Authors>ramonsmits</Authors>
 | 
			
		||||
    <Description>Simple .net Timer that is based on cron expressions with second accuracy to fire timer events to a very specific schedule.</Description>
 | 
			
		||||
    <PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
 | 
			
		||||
    <PackageReleaseNotes></PackageReleaseNotes>
 | 
			
		||||
    <PackageProjectUrl>https://github.com/ramonsmits/CronTimer/tree/$(PackageVersion)</PackageProjectUrl>
 | 
			
		||||
    <PackageLicenseExpression>MIT</PackageLicenseExpression>
 | 
			
		||||
    <IncludeSymbols>True</IncludeSymbols>
 | 
			
		||||
    <SymbolPackageFormat>snupkg</SymbolPackageFormat>
 | 
			
		||||
    <IncludeSource>True</IncludeSource>
 | 
			
		||||
    <RepositoryUrl>https://github.com/ramonsmits/CronTimer</RepositoryUrl>
 | 
			
		||||
    <Copyright>Copyright 2022 (c) Ramon Smits</Copyright>
 | 
			
		||||
    <PackageTags>cron timer</PackageTags>
 | 
			
		||||
    <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
 | 
			
		||||
    <PublishRepositoryUrl>true</PublishRepositoryUrl>
 | 
			
		||||
    <ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
 | 
			
		||||
    <EmbedUntrackedSources>true</EmbedUntrackedSources>
 | 
			
		||||
  </PropertyGroup>
 | 
			
		||||
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <PackageReference Include="ncrontab" Version="3.3.1" />
 | 
			
		||||
    <PackageReference Include="TimeZoneConverter" Version="6.0.1" />
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
 | 
			
		||||
  <!--<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.1'">
 | 
			
		||||
    <PackageReference Include="Microsoft.Extensions.Logging.Abstractions">
 | 
			
		||||
      <Version>3.1.4</Version>
 | 
			
		||||
    </PackageReference>
 | 
			
		||||
  </ItemGroup>-->
 | 
			
		||||
 | 
			
		||||
</Project>
 | 
			
		||||
							
								
								
									
										36
									
								
								src/CronTimer.sln
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/CronTimer.sln
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,36 @@
 | 
			
		||||
 | 
			
		||||
Microsoft Visual Studio Solution File, Format Version 12.00
 | 
			
		||||
# Visual Studio Version 16
 | 
			
		||||
VisualStudioVersion = 16.0.30717.126
 | 
			
		||||
MinimumVisualStudioVersion = 10.0.40219.1
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CronTimer", "CronTimer.csproj", "{FB64C227-8615-4AE1-94E3-F9F9DF192B72}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{CCDD0B34-653C-430C-9B17-5129618F8D7D}"
 | 
			
		||||
	ProjectSection(SolutionItems) = preProject
 | 
			
		||||
		..\README.md = ..\README.md
 | 
			
		||||
	EndProjectSection
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Demo", "..\Demo\Demo.csproj", "{C2638357-1621-4422-8701-B55BFB37ACCF}"
 | 
			
		||||
EndProject
 | 
			
		||||
Global
 | 
			
		||||
	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 | 
			
		||||
		Debug|Any CPU = Debug|Any CPU
 | 
			
		||||
		Release|Any CPU = Release|Any CPU
 | 
			
		||||
	EndGlobalSection
 | 
			
		||||
	GlobalSection(ProjectConfigurationPlatforms) = postSolution
 | 
			
		||||
		{FB64C227-8615-4AE1-94E3-F9F9DF192B72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{FB64C227-8615-4AE1-94E3-F9F9DF192B72}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{FB64C227-8615-4AE1-94E3-F9F9DF192B72}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{FB64C227-8615-4AE1-94E3-F9F9DF192B72}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{C2638357-1621-4422-8701-B55BFB37ACCF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{C2638357-1621-4422-8701-B55BFB37ACCF}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{C2638357-1621-4422-8701-B55BFB37ACCF}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{C2638357-1621-4422-8701-B55BFB37ACCF}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
	EndGlobalSection
 | 
			
		||||
	GlobalSection(SolutionProperties) = preSolution
 | 
			
		||||
		HideSolutionNode = FALSE
 | 
			
		||||
	EndGlobalSection
 | 
			
		||||
	GlobalSection(ExtensibilityGlobals) = postSolution
 | 
			
		||||
		SolutionGuid = {A639B164-6581-40DF-ADC4-479DAB467CFE}
 | 
			
		||||
	EndGlobalSection
 | 
			
		||||
EndGlobal
 | 
			
		||||
		Reference in New Issue
	
	Block a user