add active date range to block playout templates editor (#1663)
* update dependencies * add active date range to block playout templates editor
This commit is contained in:
@@ -4,6 +4,8 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
### Added
|
||||||
|
- Add `Active Date Range` to block playout template editor to allow limiting templates to a specific date range
|
||||||
|
|
||||||
## [0.8.6-beta] - 2024-04-03
|
## [0.8.6-beta] - 2024-04-03
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -6,4 +6,6 @@ public record ReplacePlayoutTemplate(
|
|||||||
int TemplateId,
|
int TemplateId,
|
||||||
List<DayOfWeek> DaysOfWeek,
|
List<DayOfWeek> DaysOfWeek,
|
||||||
List<int> DaysOfMonth,
|
List<int> DaysOfMonth,
|
||||||
List<int> MonthsOfYear);
|
List<int> MonthsOfYear,
|
||||||
|
DateTimeOffset? StartDate,
|
||||||
|
DateTimeOffset? EndDate);
|
||||||
|
|||||||
@@ -55,6 +55,8 @@ public class ReplacePlayoutTemplateItemsHandler(
|
|||||||
DaysOfWeek = add.DaysOfWeek,
|
DaysOfWeek = add.DaysOfWeek,
|
||||||
DaysOfMonth = add.DaysOfMonth,
|
DaysOfMonth = add.DaysOfMonth,
|
||||||
MonthsOfYear = add.MonthsOfYear,
|
MonthsOfYear = add.MonthsOfYear,
|
||||||
|
StartDate = add.StartDate,
|
||||||
|
EndDate = add.EndDate,
|
||||||
DateUpdated = now
|
DateUpdated = now
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -68,6 +70,8 @@ public class ReplacePlayoutTemplateItemsHandler(
|
|||||||
ex.DaysOfWeek = update.DaysOfWeek;
|
ex.DaysOfWeek = update.DaysOfWeek;
|
||||||
ex.DaysOfMonth = update.DaysOfMonth;
|
ex.DaysOfMonth = update.DaysOfMonth;
|
||||||
ex.MonthsOfYear = update.MonthsOfYear;
|
ex.MonthsOfYear = update.MonthsOfYear;
|
||||||
|
ex.StartDate = update.StartDate;
|
||||||
|
ex.EndDate = update.EndDate;
|
||||||
ex.DateUpdated = now;
|
ex.DateUpdated = now;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,9 @@ internal static class Mapper
|
|||||||
playoutTemplate.Index,
|
playoutTemplate.Index,
|
||||||
playoutTemplate.DaysOfWeek,
|
playoutTemplate.DaysOfWeek,
|
||||||
playoutTemplate.DaysOfMonth,
|
playoutTemplate.DaysOfMonth,
|
||||||
playoutTemplate.MonthsOfYear);
|
playoutTemplate.MonthsOfYear,
|
||||||
|
playoutTemplate.StartDate,
|
||||||
|
playoutTemplate.EndDate);
|
||||||
|
|
||||||
internal static PlayoutItemPreviewViewModel ProjectToViewModel(PlayoutItem playoutItem) =>
|
internal static PlayoutItemPreviewViewModel ProjectToViewModel(PlayoutItem playoutItem) =>
|
||||||
new(
|
new(
|
||||||
|
|||||||
@@ -6,4 +6,6 @@ public record PlayoutTemplateViewModel(
|
|||||||
int Index,
|
int Index,
|
||||||
ICollection<DayOfWeek> DaysOfWeek,
|
ICollection<DayOfWeek> DaysOfWeek,
|
||||||
ICollection<int> DaysOfMonth,
|
ICollection<int> DaysOfMonth,
|
||||||
ICollection<int> MonthsOfYear);
|
ICollection<int> MonthsOfYear,
|
||||||
|
DateTimeOffset? StartDate,
|
||||||
|
DateTimeOffset? EndDate);
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ public class PlayoutTemplate
|
|||||||
public ICollection<DayOfWeek> DaysOfWeek { get; set; }
|
public ICollection<DayOfWeek> DaysOfWeek { get; set; }
|
||||||
public ICollection<int> DaysOfMonth { get; set; }
|
public ICollection<int> DaysOfMonth { get; set; }
|
||||||
public ICollection<int> MonthsOfYear { get; set; }
|
public ICollection<int> MonthsOfYear { get; set; }
|
||||||
public DateTimeOffset StartDate { get; set; }
|
public DateTimeOffset? StartDate { get; set; }
|
||||||
public DateTimeOffset EndDate { get; set; }
|
public DateTimeOffset? EndDate { get; set; }
|
||||||
public DateTime DateUpdated { get; set; }
|
public DateTime DateUpdated { get; set; }
|
||||||
|
|
||||||
// TODO: ICollection<DateTimeOffset> AdditionalDays { get; set; }
|
// TODO: ICollection<DateTimeOffset> AdditionalDays { get; set; }
|
||||||
|
|||||||
@@ -10,6 +10,14 @@ public static class PlayoutTemplateSelector
|
|||||||
{
|
{
|
||||||
foreach (PlayoutTemplate template in templates.OrderBy(x => x.Index))
|
foreach (PlayoutTemplate template in templates.OrderBy(x => x.Index))
|
||||||
{
|
{
|
||||||
|
if (template.StartDate.HasValue && template.EndDate.HasValue)
|
||||||
|
{
|
||||||
|
if (date.Date < template.StartDate.Value.Date || date.Date > template.EndDate.Value.Date)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool daysOfWeek = template.DaysOfWeek.Contains(date.DayOfWeek);
|
bool daysOfWeek = template.DaysOfWeek.Contains(date.DayOfWeek);
|
||||||
if (!daysOfWeek)
|
if (!daysOfWeek)
|
||||||
{
|
{
|
||||||
|
|||||||
+5309
File diff suppressed because it is too large
Load Diff
+55
@@ -0,0 +1,55 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace ErsatzTV.Infrastructure.MySql.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class PlayoutTemplate_OptionalDateRange : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AlterColumn<DateTimeOffset>(
|
||||||
|
name: "StartDate",
|
||||||
|
table: "PlayoutTemplate",
|
||||||
|
type: "datetime(6)",
|
||||||
|
nullable: true,
|
||||||
|
oldClrType: typeof(DateTimeOffset),
|
||||||
|
oldType: "datetime(6)");
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<DateTimeOffset>(
|
||||||
|
name: "EndDate",
|
||||||
|
table: "PlayoutTemplate",
|
||||||
|
type: "datetime(6)",
|
||||||
|
nullable: true,
|
||||||
|
oldClrType: typeof(DateTimeOffset),
|
||||||
|
oldType: "datetime(6)");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AlterColumn<DateTimeOffset>(
|
||||||
|
name: "StartDate",
|
||||||
|
table: "PlayoutTemplate",
|
||||||
|
type: "datetime(6)",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: new DateTimeOffset(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
|
||||||
|
oldClrType: typeof(DateTimeOffset),
|
||||||
|
oldType: "datetime(6)",
|
||||||
|
oldNullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<DateTimeOffset>(
|
||||||
|
name: "EndDate",
|
||||||
|
table: "PlayoutTemplate",
|
||||||
|
type: "datetime(6)",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: new DateTimeOffset(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
|
||||||
|
oldClrType: typeof(DateTimeOffset),
|
||||||
|
oldType: "datetime(6)",
|
||||||
|
oldNullable: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2187,7 +2187,7 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations
|
|||||||
b.Property<string>("DaysOfWeek")
|
b.Property<string>("DaysOfWeek")
|
||||||
.HasColumnType("longtext");
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("EndDate")
|
b.Property<DateTimeOffset?>("EndDate")
|
||||||
.HasColumnType("datetime(6)");
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
b.Property<int>("Index")
|
b.Property<int>("Index")
|
||||||
@@ -2199,7 +2199,7 @@ namespace ErsatzTV.Infrastructure.MySql.Migrations
|
|||||||
b.Property<int>("PlayoutId")
|
b.Property<int>("PlayoutId")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("StartDate")
|
b.Property<DateTimeOffset?>("StartDate")
|
||||||
.HasColumnType("datetime(6)");
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
b.Property<int>("TemplateId")
|
b.Property<int>("TemplateId")
|
||||||
|
|||||||
+5164
File diff suppressed because it is too large
Load Diff
+55
@@ -0,0 +1,55 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace ErsatzTV.Infrastructure.Sqlite.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class PlayoutTemplate_OptionalDateRange : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AlterColumn<DateTimeOffset>(
|
||||||
|
name: "StartDate",
|
||||||
|
table: "PlayoutTemplate",
|
||||||
|
type: "TEXT",
|
||||||
|
nullable: true,
|
||||||
|
oldClrType: typeof(DateTimeOffset),
|
||||||
|
oldType: "TEXT");
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<DateTimeOffset>(
|
||||||
|
name: "EndDate",
|
||||||
|
table: "PlayoutTemplate",
|
||||||
|
type: "TEXT",
|
||||||
|
nullable: true,
|
||||||
|
oldClrType: typeof(DateTimeOffset),
|
||||||
|
oldType: "TEXT");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AlterColumn<DateTimeOffset>(
|
||||||
|
name: "StartDate",
|
||||||
|
table: "PlayoutTemplate",
|
||||||
|
type: "TEXT",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: new DateTimeOffset(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
|
||||||
|
oldClrType: typeof(DateTimeOffset),
|
||||||
|
oldType: "TEXT",
|
||||||
|
oldNullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AlterColumn<DateTimeOffset>(
|
||||||
|
name: "EndDate",
|
||||||
|
table: "PlayoutTemplate",
|
||||||
|
type: "TEXT",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: new DateTimeOffset(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)),
|
||||||
|
oldClrType: typeof(DateTimeOffset),
|
||||||
|
oldType: "TEXT",
|
||||||
|
oldNullable: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,7 +15,7 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations
|
|||||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||||
{
|
{
|
||||||
#pragma warning disable 612, 618
|
#pragma warning disable 612, 618
|
||||||
modelBuilder.HasAnnotation("ProductVersion", "8.0.2");
|
modelBuilder.HasAnnotation("ProductVersion", "8.0.3");
|
||||||
|
|
||||||
modelBuilder.Entity("ErsatzTV.Core.Domain.Actor", b =>
|
modelBuilder.Entity("ErsatzTV.Core.Domain.Actor", b =>
|
||||||
{
|
{
|
||||||
@@ -2076,7 +2076,7 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations
|
|||||||
b.Property<string>("DaysOfWeek")
|
b.Property<string>("DaysOfWeek")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("EndDate")
|
b.Property<DateTimeOffset?>("EndDate")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<int>("Index")
|
b.Property<int>("Index")
|
||||||
@@ -2088,7 +2088,7 @@ namespace ErsatzTV.Infrastructure.Sqlite.Migrations
|
|||||||
b.Property<int>("PlayoutId")
|
b.Property<int>("PlayoutId")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("StartDate")
|
b.Property<DateTimeOffset?>("StartDate")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<int>("TemplateId")
|
b.Property<int>("TemplateId")
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
<PackageReference Include="Blurhash.ImageSharp" Version="3.0.0" />
|
<PackageReference Include="Blurhash.ImageSharp" Version="3.0.0" />
|
||||||
<PackageReference Include="CliWrap" Version="3.6.6" />
|
<PackageReference Include="CliWrap" Version="3.6.6" />
|
||||||
<PackageReference Include="Dapper" Version="2.1.35" />
|
<PackageReference Include="Dapper" Version="2.1.35" />
|
||||||
<PackageReference Include="Elastic.Clients.Elasticsearch" Version="8.12.0" />
|
<PackageReference Include="Elastic.Clients.Elasticsearch" Version="8.12.1" />
|
||||||
<PackageReference Include="Jint" Version="3.0.1" />
|
<PackageReference Include="Jint" Version="3.0.1" />
|
||||||
<PackageReference Include="Lucene.Net" Version="4.8.0-beta00016" />
|
<PackageReference Include="Lucene.Net" Version="4.8.0-beta00016" />
|
||||||
<PackageReference Include="Lucene.Net.Analysis.Common" Version="4.8.0-beta00016" />
|
<PackageReference Include="Lucene.Net.Analysis.Common" Version="4.8.0-beta00016" />
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="MudBlazor" Version="6.17.0" />
|
<PackageReference Include="MudBlazor" Version="6.19.1" />
|
||||||
<PackageReference Include="NaturalSort.Extension" Version="4.2.0" />
|
<PackageReference Include="NaturalSort.Extension" Version="4.2.0" />
|
||||||
<PackageReference Include="Refit.HttpClientFactory" Version="7.0.0" />
|
<PackageReference Include="Refit.HttpClientFactory" Version="7.0.0" />
|
||||||
<PackageReference Include="Serilog" Version="3.1.1" />
|
<PackageReference Include="Serilog" Version="3.1.1" />
|
||||||
|
|||||||
@@ -109,6 +109,16 @@
|
|||||||
</MudSelect>
|
</MudSelect>
|
||||||
</MudCardContent>
|
</MudCardContent>
|
||||||
</MudCard>
|
</MudCard>
|
||||||
|
<MudCard Class="mt-4">
|
||||||
|
<MudCardContent>
|
||||||
|
<MudCheckBox T="bool" Class="mt-3" Label="Limit To Date Range"
|
||||||
|
@bind-Value="_selectedItem.LimitToDateRange" />
|
||||||
|
@if (_selectedItem.LimitToDateRange)
|
||||||
|
{
|
||||||
|
<MudDateRangePicker Class="mt-2" Label="Active Date Range" @bind-DateRange="_selectedItem.ActiveDateRange"/>
|
||||||
|
}
|
||||||
|
</MudCardContent>
|
||||||
|
</MudCard>
|
||||||
<MudCard Class="mt-4">
|
<MudCard Class="mt-4">
|
||||||
<MudCardContent>
|
<MudCardContent>
|
||||||
<MudElement HtmlTag="div" Class="mt-3">
|
<MudElement HtmlTag="div" Class="mt-3">
|
||||||
@@ -277,7 +287,10 @@
|
|||||||
Index = item.Index,
|
Index = item.Index,
|
||||||
DaysOfWeek = item.DaysOfWeek.ToList(),
|
DaysOfWeek = item.DaysOfWeek.ToList(),
|
||||||
DaysOfMonth = item.DaysOfMonth.ToList(),
|
DaysOfMonth = item.DaysOfMonth.ToList(),
|
||||||
MonthsOfYear = item.MonthsOfYear.ToList()
|
MonthsOfYear = item.MonthsOfYear.ToList(),
|
||||||
|
StartDate = item.StartDate,
|
||||||
|
EndDate = item.EndDate,
|
||||||
|
LimitToDateRange = item.StartDate.HasValue && item.EndDate.HasValue
|
||||||
};
|
};
|
||||||
|
|
||||||
private async Task UpdateTemplateGroupItems(TemplateGroupViewModel templateGroup)
|
private async Task UpdateTemplateGroupItems(TemplateGroupViewModel templateGroup)
|
||||||
@@ -438,7 +451,9 @@
|
|||||||
item.Template.Id,
|
item.Template.Id,
|
||||||
item.DaysOfWeek,
|
item.DaysOfWeek,
|
||||||
item.DaysOfMonth,
|
item.DaysOfMonth,
|
||||||
item.MonthsOfYear)).ToList();
|
item.MonthsOfYear,
|
||||||
|
item.LimitToDateRange ? item.StartDate : null,
|
||||||
|
item.LimitToDateRange ? item.EndDate : null)).ToList();
|
||||||
|
|
||||||
Option<BaseError> maybeError = await Mediator.Send(new ReplacePlayoutTemplateItems(Id, items), _cts.Token);
|
Option<BaseError> maybeError = await Mediator.Send(new ReplacePlayoutTemplateItems(Id, items), _cts.Token);
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using ErsatzTV.Application.Scheduling;
|
using ErsatzTV.Application.Scheduling;
|
||||||
|
using MudBlazor;
|
||||||
|
|
||||||
namespace ErsatzTV.ViewModels;
|
namespace ErsatzTV.ViewModels;
|
||||||
|
|
||||||
@@ -10,8 +11,48 @@ public class PlayoutTemplateEditViewModel
|
|||||||
public List<DayOfWeek> DaysOfWeek { get; set; }
|
public List<DayOfWeek> DaysOfWeek { get; set; }
|
||||||
public List<int> DaysOfMonth { get; set; }
|
public List<int> DaysOfMonth { get; set; }
|
||||||
public List<int> MonthsOfYear { get; set; }
|
public List<int> MonthsOfYear { get; set; }
|
||||||
|
public bool LimitToDateRange { get; set; }
|
||||||
|
public DateTimeOffset? StartDate { get; set; }
|
||||||
|
public DateTimeOffset? EndDate { get; set; }
|
||||||
|
public DateRange ActiveDateRange
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (StartDate is null || EndDate is null || StartDate.Value.Year < 2000 || EndDate.Value.Year < 2000)
|
||||||
|
{
|
||||||
|
return new DateRange(
|
||||||
|
DateTime.Today,
|
||||||
|
new DateTime(3000, 1, 1, 0, 0, 0, DateTimeKind.Local));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DateRange(StartDate.Value.LocalDateTime, EndDate.Value.LocalDateTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
set
|
||||||
|
{
|
||||||
|
StartDate = null;
|
||||||
|
if (value?.Start is not null)
|
||||||
|
{
|
||||||
|
DateTime start = value.Start.Value;
|
||||||
|
TimeSpan offset = TimeZoneInfo.Local.GetUtcOffset(
|
||||||
|
new DateTime(start.Year, start.Month, start.Day, 0, 0, 0, DateTimeKind.Local));
|
||||||
|
StartDate = new DateTimeOffset(start.Year, start.Month, start.Day, 0, 0, 0, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
EndDate = null;
|
||||||
|
if (value?.End is not null)
|
||||||
|
{
|
||||||
|
DateTime end = value.End.Value;
|
||||||
|
TimeSpan offset = TimeZoneInfo.Local.GetUtcOffset(
|
||||||
|
new DateTime(end.Year, end.Month, end.Day, 0, 0, 0, DateTimeKind.Local));
|
||||||
|
EndDate = new DateTimeOffset(end.Year, end.Month, end.Day, 0, 0, 0, offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public bool AppliesToDate(DateTime date) =>
|
public bool AppliesToDate(DateTime date) =>
|
||||||
|
(LimitToDateRange is false || StartDate is null || date.Date >= StartDate.Value.Date) &&
|
||||||
|
(LimitToDateRange is false || EndDate is null || date.Date <= EndDate.Value.Date) &&
|
||||||
DaysOfWeek.Contains(date.DayOfWeek) &&
|
DaysOfWeek.Contains(date.DayOfWeek) &&
|
||||||
DaysOfMonth.Contains(date.Day) &&
|
DaysOfMonth.Contains(date.Day) &&
|
||||||
MonthsOfYear.Contains(date.Month);
|
MonthsOfYear.Contains(date.Month);
|
||||||
|
|||||||
Reference in New Issue
Block a user