Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Timezone issues #128

Merged
merged 2 commits into from
Apr 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 9 additions & 14 deletions MFiles.VAF.Extensions.Tests/Configuration/FrequencyTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Runtime.Serialization;
using MFiles.VAF.Extensions.ScheduledExecution;
using MFiles.VAF.Extensions.Tests.ScheduledExecution;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
Expand Down Expand Up @@ -43,17 +44,13 @@ public static IEnumerable<object[]> SplitTriggerType_Data()
{
yield return new object[]
{
new DateTime(2022, 10, 06, 20, 01, 00, DateTimeKind.Utc),
new DateTime(2022, 10, 06, 20, 30, 00, DateTimeKind.Utc)
new DateTimeOffset(2022, 10, 06, 20, 01, 00, TimeSpan.Zero),
new DateTimeOffset(2022, 10, 06, 20, 30, 00, TimeSpan.Zero)
};
yield return new object[]
{
TimeZoneInfo.ConvertTimeBySystemTimeZoneId
(
new DateTime(2022, 10, 26, 20, 31, 00, DateTimeKind.Local),
"GMT Standard Time"
),
new DateTime(2022, 10, 26, 20, 00, 00, DateTimeKind.Utc)
new DateTimeOffset(2022, 10, 06, 20, 01, 00, TimeSpan.FromHours(1)),
new DateTimeOffset(2022, 10, 06, 20, 30, 00, TimeSpan.FromHours(1))
};
}
public static IEnumerable<object[]> DaylightSaving_ClocksGoBackwards_Data()
Expand Down Expand Up @@ -111,7 +108,7 @@ DateTimeOffset expected

[DynamicData(nameof(SplitTriggerType_Data), DynamicDataSourceType.Method)]
[TestMethod]
public void SplitTriggerType(DateTime now, DateTime expected)
public void SplitTriggerType(DateTimeOffset now, DateTimeOffset expected)
{
var frequency = Newtonsoft.Json.JsonConvert.DeserializeObject<Frequency>(@"{
""Triggers"": [
Expand Down Expand Up @@ -210,11 +207,9 @@ public void SplitTriggerType(DateTime now, DateTime expected)
""TriggerTimeType"": ""UTC""
}
");
{
var nextRun = frequency.GetNextExecution(now);
Assert.IsNotNull(nextRun.Value);
Assert.AreEqual(expected, nextRun.Value.ToUniversalTime());
}
var nextRun = frequency.GetNextExecution(now);
Assert.IsNotNull(nextRun.Value);
Assert.AreEqual(expected, nextRun.Value.ToUniversalTime());
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
using MFiles.VAF.Extensions;
using MFiles.VAF.Extensions.ScheduledExecution;
using MFiles.VAF.Extensions.ScheduledExecution;
using MFiles.VAF.Extensions.Tests.Configuration;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MFiles.VAF.Extensions.Tests.ScheduledExecution
{
Expand All @@ -22,14 +19,15 @@ public class DailyTriggerTests
public void GetNextExecution
(
IEnumerable<TimeSpan> triggerTimes,
DateTime? after,
DateTime? expected
DateTimeOffset? after,
DateTimeOffset? expected,
TimeZoneInfo timezoneInfo
)
{
var execution = new DailyTrigger()
{
TriggerTimes = triggerTimes.ToList()
}.GetNextExecution(after);
TriggerTimes = triggerTimes.ToList(),
}.GetNextExecution(after, timezoneInfo);
Assert.AreEqual(expected?.ToUniversalTime(), execution?.ToUniversalTime());
}

Expand All @@ -39,72 +37,109 @@ public static IEnumerable<object[]> GetNextExecutionData()
yield return new object[]
{
new []{ new TimeSpan(17, 0, 0) }, // Scheduled for Wednesday at 5pm.
new DateTime(2021, 03, 17, 01, 00, 00), // Wednesday @ 1am
new DateTime(2021, 03, 17, 17, 00, 00), // Wednesday @ 5pm
new DateTimeOffset(new DateTime(2021, 03, 17, 01, 00, 00), TimeZoneInfo.Local.BaseUtcOffset), // Wednesday @ 1am
new DateTimeOffset(new DateTime(2021, 03, 17, 17, 00, 00), TimeZoneInfo.Local.BaseUtcOffset), // Wednesday @ 5pm
null,
};

// Execution later same day (one passed).
yield return new object[]
{
new []{ new TimeSpan(0, 0, 0), new TimeSpan(17, 0, 0) },
new DateTime(2021, 03, 17, 01, 00, 00), // Wednesday @ 1am
new DateTime(2021, 03, 17, 17, 00, 00), // Wednesday @ 5pm
new DateTimeOffset(new DateTime(2021, 03, 17, 01, 00, 00), TimeZoneInfo.Local.BaseUtcOffset), // Wednesday @ 1am
new DateTimeOffset(new DateTime(2021, 03, 17, 17, 00, 00), TimeZoneInfo.Local.BaseUtcOffset), // Wednesday @ 5pm
null,
};

// Execution later same day (multiple matching, returns first).
yield return new object[]
{
new []{ new TimeSpan(14, 0, 0), new TimeSpan(17, 0, 0) },
new DateTime(2021, 03, 17, 01, 00, 00), // Wednesday @ 1am
new DateTime(2021, 03, 17, 14, 00, 00), // Wednesday @ 2pm
new DateTimeOffset(new DateTime(2021, 03, 17, 01, 00, 00), TimeZoneInfo.Local.BaseUtcOffset), // Wednesday @ 1am
new DateTimeOffset(new DateTime(2021, 03, 17, 14, 00, 00), TimeZoneInfo.Local.BaseUtcOffset), // Wednesday @ 2pm
null,
};

// Execution next day.
yield return new object[]
{
new []{ new TimeSpan(14, 0, 0) },
new DateTime(2021, 03, 17, 15, 00, 00), // Wednesday @ 3pm
new DateTime(2021, 03, 18, 14, 00, 00), // Thursday @ 2pm
new DateTimeOffset(new DateTime(2021, 03, 17, 15, 00, 00), TimeZoneInfo.Local.BaseUtcOffset), // Wednesday @ 3pm
new DateTimeOffset(new DateTime(2021, 03, 18, 14, 00, 00), TimeZoneInfo.Local.BaseUtcOffset), // Thursday @ 2pm
null,
};

// Execution next day (multiple items, returns first).
yield return new object[]
{
new []{ new TimeSpan(0, 0, 0), new TimeSpan(17, 0, 0) },
new DateTime(2021, 03, 17, 18, 00, 00), // Wednesday @ 6pm
new DateTime(2021, 03, 18, 00, 00, 00), // Thursday @ midnight
new DateTimeOffset(new DateTime(2021, 03, 17, 18, 00, 00), TimeZoneInfo.Local.BaseUtcOffset), // Wednesday @ 6pm
new DateTimeOffset(new DateTime(2021, 03, 18, 00, 00, 00), TimeZoneInfo.Local.BaseUtcOffset), // Thursday @ midnight
null,
};

// No valid executions = null.
yield return new object[]
{
new TimeSpan[0],
new DateTime(2021, 03, 17, 18, 00, 00), // Wednesday @ 6pm
(DateTime?)null
new DateTimeOffset(new DateTime(2021, 03, 17, 18, 00, 00), TimeZoneInfo.Local.BaseUtcOffset), // Wednesday @ 6pm
(DateTimeOffset?)null,
null,
};

// Exact current time now.
yield return new object[]
{
new []{ new TimeSpan(17, 0, 0) },
new DateTime(2021, 03, 17, 17, 00, 00), // Wednesday @ 6pm
new DateTime(2021, 03, 17, 17, 00, 00), // Thursday @ midnight
new DateTimeOffset(new DateTime(2021, 03, 17, 17, 00, 00), TimeZoneInfo.Local.BaseUtcOffset), // Wednesday @ 6pm
new DateTimeOffset(new DateTime(2021, 03, 17, 17, 00, 00), TimeZoneInfo.Local.BaseUtcOffset), // Thursday @ midnight
null,
};

// One minute past returns next day.
yield return new object[]
{
new []{ new TimeSpan(17, 0, 0) },
new DateTime(2021, 03, 17, 17, 01, 00), // Wednesday @ 6pm
new DateTime(2021, 03, 18, 17, 00, 00), // Thursday @ midnight
new DateTimeOffset(new DateTime(2021, 03, 17, 17, 01, 00), TimeZoneInfo.Local.BaseUtcOffset), // Wednesday @ 6pm
new DateTimeOffset(new DateTime(2021, 03, 18, 17, 00, 00), TimeZoneInfo.Local.BaseUtcOffset), // Thursday @ midnight
null,
};

// Daylight savings changes
yield return new object[]
{
new []{ new TimeSpan(17, 0, 0) },
new DateTime(2021, 03, 17, 17, 01, 00), // Wednesday @ 6pm
new DateTime(2021, 03, 18, 17, 00, 00), // Thursday @ midnight
new DateTimeOffset(new DateTime(2021, 03, 17, 17, 01, 00), TimeZoneInfo.Local.BaseUtcOffset), // Wednesday @ 6pm
new DateTimeOffset(new DateTime(2021, 03, 18, 17, 00, 00), TimeZoneInfo.Local.BaseUtcOffset), // Thursday @ midnight
null,
};

// Custom Timezone different day in UTC
var auTimezone = TimeZoneInfo.FindSystemTimeZoneById("W. Australia Standard Time");
yield return new object[]
{
new []{ new TimeSpan(10, 0, 0) },
TimeZoneInfo.ConvertTime(new DateTimeOffset(2024, 04, 16, 22, 00, 00, TimeSpan.Zero), auTimezone), // Tuesday @ 6am
TimeZoneInfo.ConvertTime(new DateTimeOffset(2024, 04, 17, 2, 00, 00, TimeSpan.Zero), auTimezone), // Wednesday @ 10am
auTimezone,
};

// Custom Timezone different day in local
yield return new object[]
{
new []{ new TimeSpan(10, 0, 0) },
TimeZoneInfo.ConvertTime(new DateTimeOffset(2024, 04, 16, 14, 00, 00,TimeSpan.Zero), auTimezone), // Tuesday @ 6am
TimeZoneInfo.ConvertTime(new DateTimeOffset(2024, 04, 17, 2, 00, 00, TimeSpan.Zero), auTimezone), // Wednesday @ 10am
auTimezone,
};

// UTC Timezone different day
yield return new object[]
{
new []{ new TimeSpan(10, 0, 0) },
new DateTimeOffset(2024, 04, 16, 14, 00, 00,TimeSpan.Zero), // Tuesday @ 6am
new DateTimeOffset(2024, 04, 17, 10, 00, 00, TimeSpan.Zero), // Wednesday @ 10am
TimeZoneInfo.Utc,
};
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
using MFiles.VAF.Extensions;
using MFiles.VAF.Extensions.ScheduledExecution;
using MFiles.VAF.Extensions.ScheduledExecution;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace MFiles.VAF.Extensions.Tests.ScheduledExecution
{
[TestClass]
public class ScheduleTests
{
public ScheduleTests()
{
Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-US");
Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("en-US");
}

[TestMethod]
public void ScheduleIsEnabledByDefault()
{
Expand All @@ -24,13 +28,53 @@ public void ScheduleTriggersAreNotNullByDefault()
Assert.IsNotNull(new Schedule().Triggers);
}

[TestMethod]
// https://community.m-files.com/groups-1602070992/developers/f/developer-forum/9824/trouble-with-daily-scheduling-repeating
public void ForumPost9824()
{
using (var x = new LocalTimeZoneInfoMocker("FLE Standard Time"))
{
var schedule = new Schedule()
{
Enabled = true,
Triggers = new List<Trigger>()
{
new DailyTrigger()
{
TriggerTimes = new List<TimeSpan>()
{
TimeSpan.FromHours(1)
}
}
},
RunOnVaultStartup = false
};

Assert.AreEqual
(
// Lets say we run just after the time above.
new DateTime(2024, 01, 02, 01, 00, 00, DateTimeKind.Local),
// We expect to run the next day at 1am local time.
schedule.GetNextExecution(new DateTime(2024, 01, 01, 01, 00, 01, DateTimeKind.Local))
);

Assert.AreEqual
(
// Lets say we run just after the time above but without an explicit datetimekind.
new DateTime(2024, 01, 02, 01, 00, 00),
// We expect to run the next day at 1am local time.
schedule.GetNextExecution(new DateTime(2024, 01, 01, 01, 00, 01))
);
}
}

[TestMethod]
[DynamicData(nameof(GetNextExecutionData_UTC), DynamicDataSourceType.Method)]
public void GetNextExecution
public void GetNextExecution_UTC
(
IEnumerable<TriggerBase> triggers,
DateTime? after,
DateTime? expected
DateTimeOffset? after,
DateTimeOffset? expected
)
{
Assert.AreEqual
Expand All @@ -42,7 +86,8 @@ public void GetNextExecution
Triggers = triggers
.Select(t => new Trigger(t))
.Where(t => t != null)
.ToList()
.ToList(),
TriggerTimeType = TriggerTimeType.Utc,
}.GetNextExecution(after)
);
}
Expand Down Expand Up @@ -141,8 +186,8 @@ public void GetNextExecution_GMT
public void GetNextExecution_NotEnabled
(
IEnumerable<TriggerBase> triggers,
DateTime? after,
DateTime? expected
DateTimeOffset? after,
DateTimeOffset? expected
)
{
// We use the same data as the GetNextExecution, but
Expand Down Expand Up @@ -177,8 +222,8 @@ public static IEnumerable<object[]> GetNextExecutionData_UTC()
}.ToList()
}
},
new DateTime(2021, 03, 17, 01, 00, 00, DateTimeKind.Utc), // Wednesday @ 1am
new DateTime(2021, 03, 17, 17, 00, 00, DateTimeKind.Utc), // Wednesday @ 5pm
new DateTimeOffset(2021, 03, 17, 01, 00, 00, TimeSpan.Zero), // Wednesday @ 1am
new DateTimeOffset(2021, 03, 17, 17, 00, 00, TimeSpan.Zero), // Wednesday @ 5pm
};

// Multiple triggers returns earliest.
Expand All @@ -199,16 +244,16 @@ public static IEnumerable<object[]> GetNextExecutionData_UTC()
}.ToList()
}
},
new DateTime(2021, 03, 17, 01, 00, 00, DateTimeKind.Utc), // Wednesday @ 1am
new DateTime(2021, 03, 17, 12, 00, 00, DateTimeKind.Utc), // Wednesday @ 5pm
new DateTimeOffset(2021, 03, 17, 01, 00, 00, TimeSpan.Zero), // Wednesday @ 1am
new DateTimeOffset(2021, 03, 17, 12, 00, 00, TimeSpan.Zero), // Wednesday @ 5pm
};

// No triggers = null.
yield return new object[]
{
new TriggerBase[0],
new DateTime(2021, 03, 17, 01, 00, 00, DateTimeKind.Utc), // Wednesday @ 1am
(DateTime?)null
new DateTimeOffset(2021, 03, 17, 01, 00, 00, TimeSpan.Zero), // Wednesday @ 1am
(DateTimeOffset?)null
};

// Trigger at exact current time returns now.
Expand All @@ -223,8 +268,8 @@ public static IEnumerable<object[]> GetNextExecutionData_UTC()
}.ToList()
}
},
new DateTime(2021, 03, 17, 17, 00, 00, DateTimeKind.Utc), // Wednesday @ 1am
new DateTime(2021, 03, 17, 17, 00, 00, DateTimeKind.Utc)
new DateTimeOffset(2021, 03, 17, 17, 00, 00, TimeSpan.Zero), // Wednesday @ 1am
new DateTimeOffset(2021, 03, 17, 17, 00, 00, TimeSpan.Zero)
};
}

Expand Down Expand Up @@ -278,8 +323,8 @@ public static IEnumerable<object[]> GetNextExecutionData_GMT()
}.ToList()
}
},
new DateTimeOffset(2022, 10, 30, 02, 00, 00, 0, TimeSpan.Zero), // This is 0100 BST
new DateTimeOffset(2022, 10, 30, 02, 30, 00, 0, TimeSpan.Zero), // So it should run at 0030UTC / 0130 BST
new DateTimeOffset(2022, 10, 30, 00, 00, 00, 0, TimeSpan.FromHours(1)), // This is 0100 BST
new DateTimeOffset(2022, 10, 30, 02, 30, 00, 0, TimeSpan.Zero), // So it should run at 0230 GMT
};

// Just before clocks go backwards.
Expand Down
Loading
Loading