Skip to content

Commit d9d91cd

Browse files
authored
Allow locally set time references for Date and Faker[T] (#500)
* Move to locally defined DateTime and away from Date.SystemClock. * Add additional unit tests by @garcipat from #508 * Xml comment fix from @garcipat from #508 * Start documentation on new local/global datetime reference seeding. * Small tweaks to documentation; use .DateTimeReference property for Faker. * Give credit to @garcipat for his PR work.
1 parent 85071b2 commit d9d91cd

File tree

13 files changed

+559
-49
lines changed

13 files changed

+559
-49
lines changed

HISTORY.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## vNext
2+
Release Date: Unreleased
3+
* PR 500: Allows locally set time references for Date calculations instead of global statics. See Faker[T].UseDateTimeReference(), Faker.DateTimeReference, and DataSets.Date.LocalSystemClock. Thanks @garcipat!
4+
15
## v35.4.1
26
Release Date: 2024-03-02
37
* PR 529: XML Docs: Add inclusive / exclusive number ranges documentation for Randomizer. Thanks @Mitchman215!

README.md

+21-17
Original file line numberDiff line numberDiff line change
@@ -1032,28 +1032,32 @@ As a general rule of thumb,
10321032

10331033
**Bogus** can generate deterministic dates and times. However, generating deterministic dates and times requires the following:
10341034

1035-
1. Setting up a [local or global](#determinism) seed value.
1036-
1. Setting up a global anchor source of time in `Bogus.DataSets.Date.SystemClock`.
1035+
1. Setting up a [seed value](#determinism).
1036+
1. Setting up a time reference for your Faker object instance.
10371037

10381038
The following code shows how to setup deterministic dates and times:
10391039

10401040
```csharp
1041-
// Setup some kind of seed, global or local. Here, we use a global seed.
1042-
Randomizer.Seed = new Random(1338);
1043-
1044-
// Setup a static source of time.
1045-
Bogus.DataSets.Date.SystemClock = () => DateTime.Parse("8/8/2019 2:00 PM");
1046-
1047-
// Now use Bogus as you normally would. All dates and times
1048-
// generated by Bogus should now be deterministic.
1049-
var p = new Person();
1050-
p.DateOfBirth; // 1996-06-09T15:38:11
1051-
1052-
var f = new Faker();
1053-
f.Date.Past(); // 2018-09-29T07:42:26
1054-
f.Date.Future(); // 2020-02-13T08:10:27
1041+
// Faker[T]: Set a local seed and a time reference
1042+
var fakerT = new Faker<Order>()
1043+
.UseSeed(1338)
1044+
.UseDateTimeReference(DateTime.Parse("1/1/1980"))
1045+
.RuleFor(o => o.SoonValue, f => f.Date.Soon())
1046+
.RuleFor(o => o.RecentValue, f => f.Date.Recent());
1047+
fakerT.Generate().Dump();
1048+
// { "SoonValue": "1980-01-01T17:33:05",
1049+
// "RecentValue": "1979-12-31T14:07:31" }
1050+
1051+
// Faker: Set a local seed and a time reference
1052+
var faker = new Faker
1053+
{
1054+
Random = new Randomizer(1338),
1055+
DateTimeReference = DateTime.Parse("1/1/1980")
1056+
};
1057+
faker.Date.Soon(); // "1980-01-01T17:33:05"
1058+
faker.Date.Recent(); // "1979-12-31T14:07:31"
10551059
```
1056-
With the `Bogus.DataSets.Date.SystemClock` set and a [local or global](#determinism) seed, dates and times should be deterministic across multiple runs of a program.
1060+
With a time reference set and a [seed](#determinism), dates and times should be deterministic across multiple runs of a program.
10571061

10581062

10591063
F# and VB.NET Examples

Source/Bogus.Tests/CloneTests.cs

+19-5
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,50 @@
11
using FluentAssertions;
2+
using System;
23
using Xunit;
34

45
namespace Bogus.Tests;
56

67
public class CloneTests : SeededTest
78
{
9+
public class Order
10+
{
11+
public int OrderId { get; set; }
12+
public string Item { get; set; }
13+
public int Quantity { get; set; }
14+
public int? LotNumber { get; set; }
15+
public DateTime Created { get; set; }
16+
}
17+
818
[Fact]
919
public void can_create_a_simple_clone()
1020
{
11-
var orderFaker = new Faker<Examples.Order>()
21+
var orderFaker = new Faker<Order>()
1222
.UseSeed(88)
23+
.UseDateTimeReference(new DateTime(2022, 2, 2))
1324
.RuleFor(o => o.OrderId, f => f.IndexVariable++)
1425
.RuleFor(o => o.Quantity, f => f.Random.Number(1, 3))
15-
.RuleFor(o => o.Item, f => f.Commerce.Product());
26+
.RuleFor(o => o.Item, f => f.Commerce.Product())
27+
.RuleFor(o => o.Created, f => f.Date.Recent());
1628

1729
var clone = orderFaker.Clone();
1830

1931
var clonedOrder = clone.Generate();
20-
2132
var rootOrder = orderFaker.Generate();
2233

2334
clonedOrder.Should().BeEquivalentTo(rootOrder);
35+
clonedOrder.Created.Should().BeAtLeast(TimeSpan.FromDays(1));
2436
}
2537

2638
[Fact]
2739
public void clone_has_different_rules()
2840
{
29-
var rootFaker = new Faker<Examples.Order>()
41+
var rootFaker = new Faker<Order>()
3042
.UseSeed(88)
43+
.UseDateTimeReference(new DateTime(2022, 2, 2))
3144
.RuleFor(o => o.OrderId, f => f.IndexVariable++)
3245
.RuleFor(o => o.Quantity, f => f.Random.Number(1, 3))
33-
.RuleFor(o => o.Item, f => f.Commerce.Product());
46+
.RuleFor(o => o.Item, f => f.Commerce.Product())
47+
.RuleFor(o => o.Created, f => f.Date.Recent());
3448

3549
var cloneFaker = rootFaker.Clone()
3650
.RuleFor(o => o.Quantity, f => f.Random.Number(4, 6));

Source/Bogus.Tests/DataSetTests/DateTest.cs

+114
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,17 @@ public void can_get_date_in_future()
5050
.BeOnOrAfter(starting);
5151
}
5252

53+
[Fact]
54+
public void can_get_date_in_future_with_set_clock()
55+
{
56+
var refDate = DateTime.Parse("6/7/2015 4:17:41 PM");
57+
date.LocalSystemClock = () => refDate;
58+
date.Future().Should()
59+
.BeOnOrBefore(refDate.AddYears(1))
60+
.And
61+
.BeOnOrAfter(refDate);
62+
}
63+
5364
[Fact]
5465
public void can_get_dateOffset_in_future()
5566
{
@@ -59,6 +70,16 @@ public void can_get_dateOffset_in_future()
5970
.And
6071
.BeOnOrAfter(starting);
6172
}
73+
[Fact]
74+
public void can_get_dateOffset_in_future_with_set_clock()
75+
{
76+
var refDate = DateTimeOffset.Parse("6/7/2015 4:17:41 PM");
77+
date.LocalSystemClock = () => refDate.DateTime;
78+
date.FutureOffset().Should()
79+
.BeOnOrBefore(refDate.AddYears(1))
80+
.And
81+
.BeOnOrAfter(refDate);
82+
}
6283

6384
[Fact]
6485
public void can_get_date_in_future_with_options()
@@ -90,6 +111,17 @@ public void can_get_date_in_past()
90111
.BeOnOrAfter(starting.AddYears(-1));
91112
}
92113

114+
[Fact]
115+
public void can_get_date_in_past_with_set_clock()
116+
{
117+
var refDate = DateTime.Parse("6/7/2015 4:17:41 PM");
118+
date.LocalSystemClock = () => refDate;
119+
date.Past().Should()
120+
.BeOnOrBefore(refDate)
121+
.And
122+
.BeOnOrAfter(refDate.AddYears(-1));
123+
}
124+
93125
[Fact]
94126
public void can_get_dateOffset_in_past()
95127
{
@@ -100,6 +132,17 @@ public void can_get_dateOffset_in_past()
100132
.BeOnOrAfter(starting.AddYears(-1));
101133
}
102134

135+
[Fact]
136+
public void can_get_dateOffset_in_past_with_set_clock()
137+
{
138+
var refDate = DateTimeOffset.Parse("6/7/2015 4:17:41 PM");
139+
date.LocalSystemClock = () => refDate.DateTime;
140+
date.PastOffset().Should()
141+
.BeOnOrBefore(refDate)
142+
.And
143+
.BeOnOrAfter(refDate.AddYears(-1));
144+
}
145+
103146
[Fact]
104147
public void can_get_date_in_past_0_days_results_in_random_time()
105148
{
@@ -149,6 +192,17 @@ public void can_get_date_recently_within_the_year()
149192
.BeOnOrAfter(start.AddDays(-1));
150193
}
151194

195+
[Fact]
196+
public void can_get_date_recently_with_set_clock()
197+
{
198+
var refDate = DateTime.Parse("6/7/2015 4:17:41 PM");
199+
date.LocalSystemClock = () => refDate;
200+
date.Recent().Should()
201+
.BeOnOrBefore(refDate)
202+
.And
203+
.BeOnOrAfter(refDate.AddDays(-1));
204+
}
205+
152206
[Fact]
153207
public void can_get_dateOffset_recently_within_the_year()
154208
{
@@ -160,6 +214,17 @@ public void can_get_dateOffset_recently_within_the_year()
160214
.BeOnOrAfter(start.AddDays(-1));
161215
}
162216

217+
[Fact]
218+
public void can_get_dateOffset_recently_with_set_clock()
219+
{
220+
var refDate = DateTimeOffset.Parse("6/7/2015 4:17:41 PM");
221+
date.LocalSystemClock = () => refDate.DateTime;
222+
date.RecentOffset().Should()
223+
.BeOnOrBefore(refDate)
224+
.And
225+
.BeOnOrAfter(refDate.AddDays(-1));
226+
}
227+
163228
[Fact]
164229
public void can_get_random_time_between_two_dates()
165230
{
@@ -207,13 +272,35 @@ public void get_a_date_that_will_happen_soon()
207272
date.Soon(3).Should().BeAfter(now).And.BeBefore(now.AddDays(3));
208273
}
209274

275+
[Fact]
276+
public void can_get_date_soon_with_set_clock()
277+
{
278+
var refDate = DateTime.Parse("6/7/2015 4:17:41 PM");
279+
date.LocalSystemClock = () => refDate;
280+
date.Soon().Should()
281+
.BeOnOrAfter(refDate)
282+
.And
283+
.BeBefore(refDate.AddDays(1));
284+
}
285+
210286
[Fact]
211287
public void get_a_dateOffsets_that_will_happen_soon()
212288
{
213289
var now = DateTimeOffset.Now;
214290
date.SoonOffset(3).Should().BeAfter(now).And.BeBefore(now.AddDays(3));
215291
}
216292

293+
[Fact]
294+
public void can_get_dateOffset_soon_with_set_clock()
295+
{
296+
var refDate = DateTimeOffset.Parse("6/7/2015 4:17:41 PM");
297+
date.LocalSystemClock = () => refDate.DateTime;
298+
date.SoonOffset().Should()
299+
.BeOnOrAfter(refDate)
300+
.And
301+
.BeBefore(refDate.AddDays(1));
302+
}
303+
217304
[Fact]
218305
public void soon_explicit_refdate_in_utc_should_return_utc_kind()
219306
{
@@ -347,6 +434,33 @@ public void can_set_global_static_time_source()
347434
d.RecentOffset().Offset.Should().Be(DateTimeOffset.Now.Offset);
348435
}
349436

437+
[Fact]
438+
public void use_dataset_localclock_date_if_set()
439+
{
440+
var refDate = new DateTime(2009, 12, 30, 12, 30, 0);
441+
var d = new Date() { LocalSystemClock = () => refDate };
442+
443+
444+
d.Recent(0).Should()
445+
.BeOnOrBefore(refDate)
446+
.And
447+
.BeOnOrAfter(refDate.Date);
448+
}
449+
450+
[Fact]
451+
public void use_now_param_over_localclock_date()
452+
{
453+
var refDate = new DateTime(2009, 12, 30, 12, 30, 0);
454+
var now = DateTime.Now;
455+
456+
var d = new Date { LocalSystemClock = () => refDate };
457+
458+
d.Recent(0, now).Should()
459+
.BeOnOrBefore(now)
460+
.And
461+
.BeOnOrAfter(now.Date);
462+
}
463+
350464
[Fact]
351465
public void can_get_timezone_string()
352466
{

0 commit comments

Comments
 (0)