Browse Source

fix block start time calculation

pull/2587/head
Jason Dove 9 months ago
parent
commit
1e57caff93
No known key found for this signature in database
  1. 1
      CHANGELOG.md
  2. 79
      ErsatzTV.Core.Tests/Scheduling/BlockScheduling/EffectiveBlockTests.cs
  3. 17
      ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/PlayoutBuilderTestBase.cs
  4. 36
      ErsatzTV.Core/Scheduling/BlockScheduling/EffectiveBlock.cs

1
CHANGELOG.md

@ -21,6 +21,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). @@ -21,6 +21,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Fix playback failure when seeking content with certain DTS audio (e.g. DTS-HD MA)
- Properly set explicit audio decoder on combined audio and video input file
- Fix building sequential schedules across a UTC offset change
- Fix block start time calculation across a UTC offset change
## [25.8.0] - 2025-10-26
### Added

79
ErsatzTV.Core.Tests/Scheduling/BlockScheduling/EffectiveBlockTests.cs

@ -101,9 +101,82 @@ public static class EffectiveBlockTests @@ -101,9 +101,82 @@ public static class EffectiveBlockTests
result[2].Start.Date.ShouldBe(GetLocalDate(2024, 1, 19).Date);
}
// TODO: test when clocks spring forward
// TODO: test when clocks fall back
[Test]
public void Should_Handle_Spring_Forward()
{
DateTimeOffset now = DateTimeOffset.Now;
List<PlayoutTemplate> templates =
[
new()
{
Index = 1,
DaysOfWeek = PlayoutTemplate.AllDaysOfWeek(),
DaysOfMonth = PlayoutTemplate.AllDaysOfMonth(),
MonthsOfYear = PlayoutTemplate.AllMonthsOfYear(),
Template = SingleBlockTemplate(now), // 9am block
DateUpdated = now.UtcDateTime
}
];
// In 2024, DST starts on March 10 for America/Chicago
// For Windows, this would be "Central Standard Time"
var tz = TimeZoneInfo.FindSystemTimeZoneById("America/Chicago");
var start = new DateTime(2024, 3, 9, 0, 0, 0, DateTimeKind.Unspecified);
var dto = new DateTimeOffset(start, tz.GetUtcOffset(start));
List<EffectiveBlock> result = EffectiveBlock.GetEffectiveBlocks(templates, dto, 5);
result.Count.ShouldBe(5);
// Saturday March 9, 9am is CST (-6)
var blockOnSat = result.Single(r => r.Start.Day == 9);
blockOnSat.Start.Hour.ShouldBe(9);
blockOnSat.Start.Offset.ShouldBe(TimeSpan.FromHours(-6));
// Sunday March 10, 9am is CDT (-5)
var blockOnSun = result.Single(r => r.Start.Day == 10);
blockOnSun.Start.Hour.ShouldBe(9);
blockOnSun.Start.Offset.ShouldBe(TimeSpan.FromHours(-5));
}
// TODO: offset may be incorrect on days with time change, since start offset is re-used
[Test]
public void Should_Handle_Fall_Back()
{
DateTimeOffset now = DateTimeOffset.Now;
List<PlayoutTemplate> templates =
[
new()
{
Index = 1,
DaysOfWeek = PlayoutTemplate.AllDaysOfWeek(),
DaysOfMonth = PlayoutTemplate.AllDaysOfMonth(),
MonthsOfYear = PlayoutTemplate.AllMonthsOfYear(),
Template = SingleBlockTemplate(now), // 9am block
DateUpdated = now.UtcDateTime
}
];
// In 2024, DST ends on Nov 3 for America/Chicago
// For Windows, this would be "Central Standard Time"
var tz = TimeZoneInfo.FindSystemTimeZoneById("America/Chicago");
var start = new DateTime(2024, 11, 2, 0, 0, 0, DateTimeKind.Unspecified);
var dto = new DateTimeOffset(start, tz.GetUtcOffset(start));
List<EffectiveBlock> result = EffectiveBlock.GetEffectiveBlocks(templates, dto, 5);
result.Count.ShouldBe(5);
// Saturday Nov 2, 9am is CDT (-5)
var blockOnSat = result.Single(r => r.Start.Day == 2);
blockOnSat.Start.Hour.ShouldBe(9);
blockOnSat.Start.Offset.ShouldBe(TimeSpan.FromHours(-5));
// Sunday Nov 3, 9am is CST (-6)
var blockOnSun = result.Single(r => r.Start.Day == 3);
blockOnSun.Start.Hour.ShouldBe(9);
blockOnSun.Start.Offset.ShouldBe(TimeSpan.FromHours(-6));
}
}
}

17
ErsatzTV.Core.Tests/Scheduling/ClassicScheduling/PlayoutBuilderTestBase.cs

@ -37,14 +37,15 @@ public abstract class PlayoutBuilderTestBase @@ -37,14 +37,15 @@ public abstract class PlayoutBuilderTestBase
protected static DateTimeOffset HoursAfterMidnight(int hours)
{
DateTimeOffset now = DateTimeOffset.Now;
return now - now.TimeOfDay + TimeSpan.FromHours(hours);
// // pick a timezone that has DST and a known offset on a specific date
// TimeZoneInfo eastern = TimeZoneInfo.FindSystemTimeZoneById("America/New_York");
// DateTime date = new DateTime(2025, 11, 2, 0, 0, 0, DateTimeKind.Unspecified);
// DateTimeOffset now = new DateTimeOffset(date, eastern.GetUtcOffset(date));
// return now.Date + TimeSpan.FromHours(hours);
// DateTimeOffset now = DateTimeOffset.Now;
// return now - now.TimeOfDay + TimeSpan.FromHours(hours);
// pick a timezone that has DST and a known offset on a specific date
TimeZoneInfo eastern = TimeZoneInfo.FindSystemTimeZoneById("America/New_York");
//DateTime date = new DateTime(2025, 11, 2, 0, 0, 0, DateTimeKind.Unspecified);
DateTime date = new DateTime(2025, 10, 4, 0, 0, 0, DateTimeKind.Unspecified);
DateTimeOffset now = new DateTimeOffset(date, eastern.GetUtcOffset(date));
return now.Date + TimeSpan.FromHours(hours);
}
protected TestData TestDataFloodForItems(

36
ErsatzTV.Core/Scheduling/BlockScheduling/EffectiveBlock.cs

@ -9,6 +9,8 @@ internal record EffectiveBlock(Block Block, BlockKey BlockKey, DateTimeOffset St @@ -9,6 +9,8 @@ internal record EffectiveBlock(Block Block, BlockKey BlockKey, DateTimeOffset St
DateTimeOffset start,
int daysToBuild)
{
var timeZone = TimeZoneInfo.Local;
DateTimeOffset finish = start.AddDays(daysToBuild);
var effectiveBlocks = new List<EffectiveBlock>();
@ -26,14 +28,15 @@ internal record EffectiveBlock(Block Block, BlockKey BlockKey, DateTimeOffset St @@ -26,14 +28,15 @@ internal record EffectiveBlock(Block Block, BlockKey BlockKey, DateTimeOffset St
DateTimeOffset today = current;
var newBlocks = playoutTemplate.Template.Items
.Map(i => ToEffectiveBlock(playoutTemplate, i, today, start))
.Map(i => ToEffectiveBlock(playoutTemplate, i, today))
.Map(NormalizeGuideMode)
.ToList();
effectiveBlocks.AddRange(newBlocks);
}
current = current.AddDays(1);
var localDate = TimeZoneInfo.ConvertTime(current, timeZone).Date;
current = new DateTimeOffset(localDate.AddDays(1), timeZone.GetUtcOffset(localDate.AddDays(1)));
}
effectiveBlocks.RemoveAll(b => b.Start.AddMinutes(b.Block.Minutes) < start || b.Start > finish);
@ -45,20 +48,27 @@ internal record EffectiveBlock(Block Block, BlockKey BlockKey, DateTimeOffset St @@ -45,20 +48,27 @@ internal record EffectiveBlock(Block Block, BlockKey BlockKey, DateTimeOffset St
private static EffectiveBlock ToEffectiveBlock(
PlayoutTemplate playoutTemplate,
TemplateItem templateItem,
DateTimeOffset current,
DateTimeOffset start) =>
new(
DateTimeOffset current)
{
var blockStartTime = new DateTime(
current.Year,
current.Month,
current.Day,
templateItem.StartTime.Hours,
templateItem.StartTime.Minutes,
0,
DateTimeKind.Unspecified);
// use the system's local time zone
var timeZone = TimeZoneInfo.Local;
var blockStart = new DateTimeOffset(blockStartTime, timeZone.GetUtcOffset(blockStartTime));
return new EffectiveBlock(
templateItem.Block,
new BlockKey(templateItem.Block, templateItem.Template, playoutTemplate),
new DateTimeOffset(
current.Year,
current.Month,
current.Day,
templateItem.StartTime.Hours,
templateItem.StartTime.Minutes,
0,
start.Offset),
blockStart,
templateItem.Id);
}
private static EffectiveBlock NormalizeGuideMode(EffectiveBlock effectiveBlock)
{

Loading…
Cancel
Save