diff --git a/AsbCloudApp/ValidationAttributes/DateValidationAttribute.cs b/AsbCloudApp/ValidationAttributes/DateValidationAttribute.cs new file mode 100644 index 00000000..3ad6fce0 --- /dev/null +++ b/AsbCloudApp/ValidationAttributes/DateValidationAttribute.cs @@ -0,0 +1,80 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace AsbCloudApp.ValidationAttributes +{ + /// + /// Атрибут валидации даты-времени + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property)] + public class DateValidationAttribute : ValidationAttribute + { + private DateTime? gtDate; + + /// + /// null разрешен + /// + public bool AllowNull { get; set; } = true; + + /// + /// Разрешена только дат. + /// При наличии времени в DateTime инвалидирует. + /// При наличии UTC тоже инвалидирует. + /// + public bool IsDateOnly { get; set; } = false; + + /// + /// Допустима дата-время в UTC + /// + public bool AllowUtc { get; set; } = true; + + /// + /// Дата больше которой должно быть проверяемое значение. + /// Формат строки - любой поддерживаемый DateTime.Parse. + /// Желательно использовать ISO 8601 формат + /// + public string? GtDate { + get => gtDate.ToString(); + set + { + if(value is null) + gtDate = null; + else + gtDate = DateTime.Parse(value); + } + } + + /// + /// Проверка значения + /// + /// + /// + public override bool IsValid(object? value) + { + if (value is null) + return AllowNull; + + if (value is not DateTime dateTime) + return false; + + if (IsDateOnly) + { + if (dateTime.Hour > 0 || + dateTime.Minute > 0 || + dateTime.Second > 0 || + dateTime.Millisecond > 0 || + dateTime.Kind == DateTimeKind.Utc) + return false; + } + + if (!AllowUtc && dateTime.Kind == DateTimeKind.Utc) + return false; + + if (gtDate.HasValue && dateTime <= gtDate) + return false; + + return true; + } + + } +} diff --git a/AsbCloudWebApi.Tests/AttributeTest/DateValidationAttributeTest.cs b/AsbCloudWebApi.Tests/AttributeTest/DateValidationAttributeTest.cs new file mode 100644 index 00000000..3e4990a2 --- /dev/null +++ b/AsbCloudWebApi.Tests/AttributeTest/DateValidationAttributeTest.cs @@ -0,0 +1,108 @@ +using AsbCloudApp.ValidationAttributes; +using System; +using Xunit; + +namespace AsbCloudWebApi.Tests.AttributeTest +{ + public class DateValidationAttributeTest + { + [Fact] + public void AllowNull_true_on_null_valid() + { + var attribute = new DateValidationAttribute { AllowNull = true }; + var result = attribute.IsValid(null); + Assert.True(result); + } + + [Fact] + public void AllowNull_false_on_null_invalid() + { + var attribute = new DateValidationAttribute { AllowNull = false }; + var result = attribute.IsValid(null); + Assert.False(result); + } + + [Fact] + public void IsDateOnly_true_on_empty_timePart_valid() + { + var attribute = new DateValidationAttribute { IsDateOnly = true }; + var date = new DateTime(2023, 01, 01, 00, 00, 00); + var result = attribute.IsValid(date); + Assert.True(result); + } + + [Fact] + public void IsDateOnly_true_on_timePart_invalid() + { + var attribute = new DateValidationAttribute { IsDateOnly = true }; + var date = new DateTime(2023, 01, 01, 01, 01, 01); + var result = attribute.IsValid(date); + Assert.False(result); + } + + [Fact] + public void IsDateOnly_true_on_utc_invalid() + { + var attribute = new DateValidationAttribute { IsDateOnly = true }; + var date = new DateTime(2023, 01, 01, 00, 00, 00, DateTimeKind.Utc); + var result = attribute.IsValid(date); + Assert.False(result); + } + + [Fact] + public void AllowUtc_true_on_unspecified_valid() + { + var attribute = new DateValidationAttribute { AllowUtc = true }; + var date = new DateTime(2023, 01, 01, 00, 00, 00, DateTimeKind.Unspecified); + var result = attribute.IsValid(date); + Assert.True(result); + } + + [Fact] + public void AllowUtc_true_on_utc_valid() + { + var attribute = new DateValidationAttribute { AllowUtc = true }; + var date = new DateTime(2023, 01, 01, 00, 00, 00, DateTimeKind.Utc); + var result = attribute.IsValid(date); + Assert.True(result); + } + + [Fact] + public void AllowUtc_false_on_utc_invalid() + { + var attribute = new DateValidationAttribute { AllowUtc = false }; + var date = new DateTime(2023, 01, 01, 00, 00, 00, DateTimeKind.Utc); + var result = attribute.IsValid(date); + Assert.False(result); + } + + [Fact] + public void AllowUtc_false_on_unspecified_valid() + { + var attribute = new DateValidationAttribute { AllowUtc = false }; + var date = new DateTime(2023, 01, 01, 00, 00, 00, DateTimeKind.Unspecified); + var result = attribute.IsValid(date); + Assert.True(result); + } + + [Fact] + public void GtDate_on_same_date_invalid() + { + var gtDate = "2023-01-01T00:00:00"; + var date = DateTime.Parse(gtDate); + var attribute = new DateValidationAttribute { GtDate = gtDate }; + var result = attribute.IsValid(date); + Assert.False(result); + } + + [Fact] + public void GtDate_on_greater_date_valid() + { + var gtDate = "2023-01-01T00:00:00"; + var date = DateTime.Parse(gtDate).AddMilliseconds(1); + var attribute = new DateValidationAttribute { GtDate = gtDate }; + var result = attribute.IsValid(date); + Assert.True(result); + } + } +}