diff --git a/Src/AutoFixture/DataAnnotations/DateTimeRangedRequestRelay.cs b/Src/AutoFixture/DataAnnotations/DateTimeRangedRequestRelay.cs
new file mode 100644
index 000000000..b5c7b7298
--- /dev/null
+++ b/Src/AutoFixture/DataAnnotations/DateTimeRangedRequestRelay.cs
@@ -0,0 +1,65 @@
+using System;
+using System.Globalization;
+using AutoFixture.Kernel;
+
+namespace AutoFixture.DataAnnotations
+{
+ ///
+ /// Handles of DateTime type by forwarding requests
+ /// to the with min and max DateTime as ticks values.
+ ///
+ public class DateTimeRangedRequestRelay : ISpecimenBuilder
+ {
+ ///
+ public object Create(object request, ISpecimenContext context)
+ {
+ if (context == null) throw new ArgumentNullException(nameof(context));
+
+ var rangedRequest = request as RangedRequest;
+
+ if (rangedRequest == null)
+ return new NoSpecimen();
+
+ if (rangedRequest.MemberType != typeof(DateTime))
+ return new NoSpecimen();
+
+ return CreateDateTimeSpecimen(rangedRequest, context);
+ }
+
+ private static object CreateDateTimeSpecimen(RangedRequest rangedRequest, ISpecimenContext context)
+ {
+ if (!(rangedRequest.Minimum is string) || !(rangedRequest.Maximum is string))
+ return new NoSpecimen();
+
+ var range = ParseTimeSpanRange(rangedRequest);
+ return RandomizeDateTimeInRange(range, context);
+ }
+
+ private static DateTimeRange ParseTimeSpanRange(RangedRequest rangedRequest)
+ {
+ return new DateTimeRange
+ {
+ Min = DateTime.Parse((string)rangedRequest.Minimum, CultureInfo.CurrentCulture),
+ Max = DateTime.Parse((string)rangedRequest.Maximum, CultureInfo.CurrentCulture)
+ };
+ }
+
+ private static object RandomizeDateTimeInRange(DateTimeRange range, ISpecimenContext context)
+ {
+ var ticksInRange = context.Resolve(
+ new RangedNumberRequest(typeof(long), range.Min.Ticks, range.Max.Ticks));
+
+ if (ticksInRange is NoSpecimen)
+ return new NoSpecimen();
+
+ return new DateTime((long)ticksInRange);
+ }
+
+ private struct DateTimeRange
+ {
+ public DateTime Min { get; set; }
+
+ public DateTime Max { get; set; }
+ }
+ }
+}
diff --git a/Src/AutoFixture/Fixture.cs b/Src/AutoFixture/Fixture.cs
index 2126d4f02..ae13b10f5 100644
--- a/Src/AutoFixture/Fixture.cs
+++ b/Src/AutoFixture/Fixture.cs
@@ -124,6 +124,7 @@ public Fixture(ISpecimenBuilder engine, MultipleRelay multiple)
new RangeAttributeRelay(),
new NumericRangedRequestRelay(),
new EnumRangedRequestRelay(),
+ new DateTimeRangedRequestRelay(),
new TimeSpanRangedRequestRelay(),
new StringLengthAttributeRelay(),
new MinAndMaxLengthAttributeRelay(),
diff --git a/Src/AutoFixtureUnitTest/DataAnnotations/DateTimeRangedRequestRelayTest.cs b/Src/AutoFixtureUnitTest/DataAnnotations/DateTimeRangedRequestRelayTest.cs
new file mode 100644
index 000000000..23264ca23
--- /dev/null
+++ b/Src/AutoFixtureUnitTest/DataAnnotations/DateTimeRangedRequestRelayTest.cs
@@ -0,0 +1,118 @@
+using System;
+using AutoFixture.DataAnnotations;
+using AutoFixture.Kernel;
+using AutoFixtureUnitTest.Kernel;
+using Xunit;
+
+namespace AutoFixtureUnitTest.DataAnnotations
+{
+ public class DateTimeRangedRequestRelayTest
+ {
+ [Fact]
+ public void SutShouldBeASpecimenBuilder()
+ {
+ // Arrange
+ var sut = new DateTimeRangedRequestRelay();
+
+ // Act & Assert
+ Assert.IsAssignableFrom(sut);
+ }
+
+ [Fact]
+ public void ShouldFailForNullContext()
+ {
+ // Arrange
+ var sut = new DateTimeRangedRequestRelay();
+ var request = new object();
+
+ // Act & Assert
+ Assert.ThrowsAny(() =>
+ sut.Create(request, null));
+ }
+
+ [Fact]
+ public void ShouldNotHandleRequestIfMemberTypeIsNotDateTime()
+ {
+ // Arrange
+ var sut = new DateTimeRangedRequestRelay();
+ var request =
+ new RangedRequest(memberType: typeof(object), operandType: typeof(object), minimum: 0, maximum: 1);
+ var context = new DelegatingSpecimenContext();
+
+ // Act
+ var result = sut.Create(request, context);
+
+ // Assert
+ Assert.Equal(new NoSpecimen(), result);
+ }
+
+ [Fact]
+ public void ShouldHandleRequestOfDateTimeType()
+ {
+ // Arrange
+ var sut = new DateTimeRangedRequestRelay();
+ var request =
+ new RangedRequest(memberType: typeof(DateTime), operandType: typeof(DateTime), minimum: "2020-01-01 00:00:00",
+ maximum: "2020-12-31 23:59:59");
+
+ var context = new DelegatingSpecimenContext
+ {
+ OnResolve = _ => new DateTime(2020, 06, 15, 12, 30, 30).Ticks
+ };
+
+ // Act
+ var actualResult = sut.Create(request, context);
+
+ // Assert
+ Assert.Equal(new DateTime(2020, 06, 15, 12, 30, 30), actualResult);
+ }
+
+ [Fact]
+ public void ShouldReturnNoSpecimenIfUnableToSatisfyRangedRequest()
+ {
+ // Arrange
+ var sut = new DateTimeRangedRequestRelay();
+ var request =
+ new RangedRequest(memberType: typeof(DateTime), operandType: typeof(DateTime), minimum: "2020-01-01 00:00:00",
+ maximum: "2020-12-31 23:59:59");
+
+ var context = new DelegatingSpecimenContext
+ {
+ OnResolve = _ => new NoSpecimen()
+ };
+
+ // Act
+ var actualResult = sut.Create(request, context);
+
+ // Assert
+ Assert.Equal(new NoSpecimen(), actualResult);
+ }
+
+ [Fact]
+ public void ShouldCorrectPassMinimumAndMaximumAsTicks()
+ {
+ // Arrange
+ var sut = new DateTimeRangedRequestRelay();
+
+ var request = new RangedRequest(typeof(DateTime), typeof(DateTime), "2020-01-01 00:00:00", "2020-12-31 23:59:59");
+ RangedNumberRequest capturedNumericRequest = null;
+
+ var context = new DelegatingSpecimenContext
+ {
+ OnResolve = r =>
+ {
+ capturedNumericRequest = (RangedNumberRequest)r;
+ return new NoSpecimen();
+ }
+ };
+
+ // Act
+ sut.Create(request, context);
+
+ // Assert
+ Assert.NotNull(capturedNumericRequest);
+ Assert.Equal(new DateTime(2020, 1, 1, 0, 0, 0).Ticks, capturedNumericRequest.Minimum);
+ Assert.Equal(new DateTime(2020, 12, 31, 23, 59, 59).Ticks, capturedNumericRequest.Maximum);
+ }
+ }
+}
diff --git a/Src/AutoFixtureUnitTest/FixtureTest.cs b/Src/AutoFixtureUnitTest/FixtureTest.cs
index c8e972f5d..ee6384b7b 100644
--- a/Src/AutoFixtureUnitTest/FixtureTest.cs
+++ b/Src/AutoFixtureUnitTest/FixtureTest.cs
@@ -6147,6 +6147,25 @@ IPostprocessComposer ConfigurePropertyField(IPostprocessComposer compos
}
}
+ private class TypeWithRangedDateTimeProperty
+ {
+ [Range(typeof(DateTime), "01/01/1900", "12/31/2020")]
+ public DateTime StringRangedDateTimeProperty { get; set; }
+ }
+
+ [Fact]
+ public void ShouldCorrectlyResolveDateTimePropertiesDecoratedWithRange()
+ {
+ // Arrange
+ var sut = new Fixture();
+
+ // Act
+ var result = sut.Create();
+
+ // Assert
+ Assert.InRange(result.StringRangedDateTimeProperty, new DateTime(1900, 1, 1), new DateTime(2020, 12, 31));
+ }
+
private class TypeWithRangedTimeSpanProperty
{
[Range(typeof(TimeSpan), "02:00:00", "12:00:00")]
@@ -6386,4 +6405,4 @@ public void ShouldResolveFixedNumberOfItemsForRangedSequenceRequest()
Assert.Equal(expectedLength, resultSeq.Count());
}
}
-}
\ No newline at end of file
+}