Skip to content

Commit a582dec

Browse files
committed
Import
0 parents  commit a582dec

5 files changed

Lines changed: 169 additions & 0 deletions

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.vs
2+
bin
3+
obj

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# CronTimer
2+
3+
Simple .net Timer that is based on cron expressions with second accuracy to fire timer events to a very specific schedule.
4+
5+
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.
6+
7+
## Example
8+
9+
10+
Fire a timer event every 10 minutes from Monday through Friday between 8:00 and 17:00
11+
12+
```
13+
var timer = new CronTimer("*/10 08-17 * * 1-5", "Europe/Amsterdam", includingSeconds: false);
14+
timer.OnOccurence += (s, ea) => Console.Out.WriteLineAsync(ea + " - " + DateTime.Now);
15+
timer.Start();
16+
```

src/CronTimer.cs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
using System;
2+
using System.Threading;
3+
using NCrontab;
4+
5+
public class CronTimer
6+
{
7+
public const string UTC = "Etc/UTC";
8+
9+
static readonly TimeSpan InfiniteTimeSpan = TimeSpan.FromMilliseconds(Timeout.Infinite); // net 3.5
10+
static readonly EventArgs ea = new EventArgs();
11+
12+
readonly CrontabSchedule schedule;
13+
readonly TimeZoneInfo tzi;
14+
readonly string id;
15+
Timer t;
16+
17+
public string tz { get; }
18+
public string Expression { get; }
19+
public event EventHandler<EventArgs> OnOccurence;
20+
21+
public CronTimer(string expression, string tz = UTC, bool includingSeconds = false)
22+
{
23+
Expression = expression;
24+
this.tz = tz;
25+
id = TimeZoneConverter.TZConvert.IanaToWindows(tz);
26+
tzi = TimeZoneInfo.FindSystemTimeZoneById(id);
27+
schedule = CrontabSchedule.Parse(expression, new CrontabSchedule.ParseOptions { IncludingSeconds = includingSeconds });
28+
OnOccurence += OnOccurenceScheduleNext;
29+
}
30+
31+
void OnOccurenceScheduleNext(object sender, EventArgs e)
32+
{
33+
var delay = CalculateDelay();
34+
//Console.WriteLine($"Next for [{tz} {expression}] in {delay}.");
35+
t.Change(delay, InfiniteTimeSpan);
36+
}
37+
38+
public void Start()
39+
{
40+
var delay = CalculateDelay();
41+
//Console.WriteLine($"Next for [{tz} {expression}] in {delay}.");
42+
t = new Timer(s => OnOccurence(this, ea), null, delay, InfiniteTimeSpan);
43+
}
44+
45+
TimeSpan CalculateDelay()
46+
{
47+
TimeSpan delay;
48+
if (tz != UTC)
49+
{
50+
var nowUtc = DateTime.UtcNow;
51+
var now = TimeZoneInfo.ConvertTimeFromUtc(nowUtc, tzi);
52+
var next = schedule.GetNextOccurrence(now);
53+
var nextUtc = next.ToUniversalTime();
54+
delay = nextUtc - nowUtc;
55+
}
56+
else
57+
{
58+
var nowUtc = DateTime.UtcNow;
59+
var nextUtc = schedule.GetNextOccurrence(nowUtc);
60+
delay = nextUtc - nowUtc;
61+
}
62+
//Console.WriteLine($"Now: {nowUtc} [utc] {now} [{tz}], Next: {next} [{tz}] {nextUtc} [utc], Delay: {delay}");
63+
if (delay < TimeSpan.Zero) delay = TimeSpan.Zero;
64+
return delay;
65+
}
66+
67+
public void Stop()
68+
{
69+
t.Dispose();
70+
}
71+
}

src/CronTimer.csproj

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<LangVersion>latest</LangVersion>
5+
<TargetFrameworks>net35;netstandard2.1</TargetFrameworks>
6+
<PlatformTarget>AnyCPU</PlatformTarget>
7+
</PropertyGroup>
8+
9+
<PropertyGroup>
10+
<!-- AssemblyFileVersionAttribute -->
11+
<FileVersion>1.0.0</FileVersion>
12+
<!-- AssemblyInformationalVersionAttribute -->
13+
<Version>$(FileVersion)</Version>
14+
<!-- AssemblyVersionAttribute -->
15+
<AssemblyVersion>1.0.0.0</AssemblyVersion>
16+
<!-- Nuget -->
17+
<PackageVersion>$(Version)</PackageVersion>
18+
<PackageId>CronTimer</PackageId>
19+
<Company>https://github.com/ramonsmits</Company>
20+
<Authors>ramonsmits</Authors>
21+
<Description>Simple .net Timer that is based on cron expressions with second accuracy to fire timer events to a very specific schedule.</Description>
22+
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
23+
<PackageReleaseNotes></PackageReleaseNotes>
24+
<PackageProjectUrl>https://github.com/ramonsmits/CronTimer/tree/$(PackageVersion)</PackageProjectUrl>
25+
<PackageLicenseExpression>MIT</PackageLicenseExpression>
26+
<IncludeSymbols>True</IncludeSymbols>
27+
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
28+
<IncludeSource>True</IncludeSource>
29+
<RepositoryUrl>https://github.com/ramonsmits/CronTimer</RepositoryUrl>
30+
<Copyright>Copyright 2020 (c) Ramon Smits</Copyright>
31+
<PackageTags>cron timer</PackageTags>
32+
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
33+
<PublishRepositoryUrl>true</PublishRepositoryUrl>
34+
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
35+
<EmbedUntrackedSources>true</EmbedUntrackedSources>
36+
</PropertyGroup>
37+
38+
<ItemGroup>
39+
<PackageReference Include="ncrontab" Version="[3.3.1, 4.0.0)" />
40+
<PackageReference Include="TimeZoneConverter" Version="[3.2.0, 4.0.0)" />
41+
</ItemGroup>
42+
43+
<!--<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.1'">
44+
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions">
45+
<Version>3.1.4</Version>
46+
</PackageReference>
47+
</ItemGroup>-->
48+
49+
</Project>

src/CronTimer.sln

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 16
4+
VisualStudioVersion = 16.0.30717.126
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CronTimer", "CronTimer.csproj", "{FB64C227-8615-4AE1-94E3-F9F9DF192B72}"
7+
EndProject
8+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{CCDD0B34-653C-430C-9B17-5129618F8D7D}"
9+
ProjectSection(SolutionItems) = preProject
10+
..\README.md = ..\README.md
11+
EndProjectSection
12+
EndProject
13+
Global
14+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
15+
Debug|Any CPU = Debug|Any CPU
16+
Release|Any CPU = Release|Any CPU
17+
EndGlobalSection
18+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
19+
{FB64C227-8615-4AE1-94E3-F9F9DF192B72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
20+
{FB64C227-8615-4AE1-94E3-F9F9DF192B72}.Debug|Any CPU.Build.0 = Debug|Any CPU
21+
{FB64C227-8615-4AE1-94E3-F9F9DF192B72}.Release|Any CPU.ActiveCfg = Release|Any CPU
22+
{FB64C227-8615-4AE1-94E3-F9F9DF192B72}.Release|Any CPU.Build.0 = Release|Any CPU
23+
EndGlobalSection
24+
GlobalSection(SolutionProperties) = preSolution
25+
HideSolutionNode = FALSE
26+
EndGlobalSection
27+
GlobalSection(ExtensibilityGlobals) = postSolution
28+
SolutionGuid = {A639B164-6581-40DF-ADC4-479DAB467CFE}
29+
EndGlobalSection
30+
EndGlobal

0 commit comments

Comments
 (0)