using System.Text.RegularExpressions;
public static void Main()
var now = DateTime.UtcNow;
var posix = "EST5EDT,M3.2.0/2,M11.1.0/2";
var cronSchedule = "*/5 9-17 * * *";
var cronScheduleDescription = CronExpressionDescriptor.ExpressionDescriptor.GetDescription(cronSchedule, new CronExpressionDescriptor.Options{DayOfWeekStartIndexZero = true, Use24HourTimeFormat = true, Locale = "en"});
Console.WriteLine(cronScheduleDescription);
var tz = PosixToTimeZoneInfo(posix);
var localNow = TimeZoneInfo.ConvertTimeFromUtc(now,tz);
var wtf = new DateTime(localNow.Year,localNow.Month,localNow.Day,localNow.Hour,localNow.Hour,localNow.Second, DateTimeKind.Local);
var localNowKind = wtf.Kind;
var cron = NCrontab.CrontabSchedule.TryParse(cronSchedule, new NCrontab.CrontabSchedule.ParseOptions { IncludingSeconds = false });
var nextRunLocal=cron.GetNextOccurrence(wtf);
var nextRunUtc = TimeZoneInfo.ConvertTimeToUtc(nextRunLocal);
Console.WriteLine($"UTC: {now}");
Console.WriteLine($"Local: {wtf} {localNowKind}");
Console.WriteLine($"Next: {nextRunLocal}");
Console.WriteLine($"NextUTC: {nextRunUtc}");
private static TimeZoneInfo PosixToTimeZoneInfo(string tz)
var dataParse = Regex.Match(tz, @"((?<TzSet>(?<Id>(?<ST>[a-zA-Z]+)(?<Offset>(-?\d{1,2}(:\d{1,2})?))((?<DT>[a-zA-Z]+))?)(,M)?(?<StartMonth>\d{1,2})?(\.)?(?<StartWeek>\d{1,2})?(\.)?(?<StartDayOfWeek>\d{1,2})?/?(?<StartHour>\d{1,2}(:\d{1,2})?)?(,M)?(?<EndMonth>\d{1,2})?(\.)?(?<EndWeek>\d{1,2})?(\.)?(?<EndDayOfWeek>\d{1,2})?/?(?<EndHour>\d{1,2}(:\d{1,2})?)?))$");
string Id = dataParse.Groups["Id"].Value;
TimeZoneInfo timeZoneInfo;
if (dataParse.Groups["Offset"].Value.Contains(":"))
baseUtcOffset = TimeSpan.Parse(dataParse.Groups["Offset"].Value);
baseUtcOffset = new TimeSpan(int.Parse(dataParse.Groups["Offset"].Value), 0, 0);
if (dataParse.Groups["StartMonth"].Value != string.Empty && dataParse.Groups["EndMonth"].Value != string.Empty)
if (dataParse.Groups["StartHour"].Value == string.Empty)
startTime = new TimeSpan(2, 0, 0);
if (dataParse.Groups["StartHour"].Value.Contains(":"))
startTime = TimeSpan.Parse(dataParse.Groups["StartHour"].Value);
startTime = new TimeSpan(int.Parse(dataParse.Groups["StartHour"].Value), 0, 0);
if (dataParse.Groups["EndHour"].Value == string.Empty)
endTime = new TimeSpan(2, 0, 0);
if (dataParse.Groups["EndHour"].Value.Contains(":"))
endTime = TimeSpan.Parse(dataParse.Groups["EndHour"].Value);
endTime = new TimeSpan(int.Parse(dataParse.Groups["EndHour"].Value), 0, 0);
TimeZoneInfo.TransitionTime startTransition = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, startTime.Hours, startTime.Minutes, 0), int.Parse(dataParse.Groups["StartMonth"].Value), int.Parse(dataParse.Groups["StartWeek"].Value), (DayOfWeek)int.Parse(dataParse.Groups["StartDayOfWeek"].Value));
TimeZoneInfo.TransitionTime endTransition = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, endTime.Hours, endTime.Minutes, 0), int.Parse(dataParse.Groups["EndMonth"].Value), int.Parse(dataParse.Groups["EndWeek"].Value), (DayOfWeek)int.Parse(dataParse.Groups["EndDayOfWeek"].Value));
TimeZoneInfo.AdjustmentRule adjustmentRule = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(DateTime.MinValue.Date, DateTime.MaxValue.Date, new TimeSpan(1, 0, 0), startTransition, endTransition);
timeZoneInfo = TimeZoneInfo.CreateCustomTimeZone(Id, -baseUtcOffset, Id, dataParse.Groups["ST"].Value, dataParse.Groups["DT"].Value, new[]{adjustmentRule});
timeZoneInfo = TimeZoneInfo.CreateCustomTimeZone(Id, -baseUtcOffset, Id, dataParse.Groups["ST"].Value, dataParse.Groups["DT"].Value, null);