Skip to content

Commit d23d4d4

Browse files
chore: add XML docs and test, improve README, add a few tests. (#2)
1 parent fa08cd0 commit d23d4d4

14 files changed

Lines changed: 285 additions & 117 deletions

README.md

Lines changed: 54 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ Timecop is a small library that helps you test DateTime in a static, thread-safe
55

66
Timecop targets .NET Standard 2.0, has no external dependencies, and can be used with .NET Framework 4.5+ and any version of .NET and .NET Core.
77

8+
Timecop is the C# port of the [timecop](https://github.com/travisjeffery/timecop) Ruby gem.
9+
810
## Installation
911

1012
You can install [Timecop](https://www.nuget.org/packages/Timecop/) from NuGet using the .NET CLI:
@@ -13,14 +15,9 @@ You can install [Timecop](https://www.nuget.org/packages/Timecop/) from NuGet us
1315
dotnet add package Timecop
1416
```
1517

16-
## Usage
17-
18-
Timecop allows freezing and travelling in time. It gives you two classes:
19-
1. `Clock` - use it instead of `DateTime.Now` and `DateTime.UtcNow`to get the current time
20-
2. `TimeCop`- use it to manipulate time in tests
21-
18+
## Basic usage
2219

23-
Timecop can be used in a static context, the same way you would use DateTime:
20+
Timecop allows you to freeze and travel in time. Just use the `Clock` class instead of `DateTime`to get the current time via `Now` or `UtcNow` properties, and manipulate time with the `Timecop` class in your tests.
2421

2522
```csharp
2623
string Greet()
@@ -35,17 +32,63 @@ string Greet()
3532

3633
return $"Good {timeOfDay}!";
3734
}
38-
39-
using var tc = Timecop.Frozen(o => o.At(14,0,0).LocalTime());
35+
// freeze at 2pm local time:
36+
using var tc = Timecop.Frozen(o => o.At(14,0,0).LocalTime());
4037

4138
Greet(); // Good afternoon!
42-
43-
tc.TravelBy(TimeSpan.FromHours(6)); // travel to 8pm local time
39+
// travel to 8pm local time:
40+
tc.TravelBy(TimeSpan.FromHours(6));
4441

4542
Greet(); // Good evening!
4643
```
4744

45+
## Available methods
46+
47+
### Freezing and resuming time
48+
49+
Time is frozen with either an instance `Freeze` or a static `Frozen` method, both having the same set of signatures. The instance `Freeze` freezes the instance of `Timecop`, the static `Frozen` creates an already frozen instance.
50+
51+
Frozen time doesn't run for your tests unless you call `Resume` or dispose the `Timecop` instance:
52+
53+
```csharp
54+
using var tc = Timecop.Frozen(1990, 12, 2, 14, 38, 51, DateTimeKind.Local);
55+
Clock.Now; // 1990-12-02 14:38:51
56+
57+
Thread.Sleep(TimeSpan.FromSeconds(3));
58+
Clock.Now; // 1990-12-02 14:38:51 - Still the same value
59+
60+
tc.Resume();
61+
62+
Thread.Sleep(TimeSpan.FromSeconds(3));
63+
Clock.Now; // 1990-12-02 14:38:54 - Time has changed
64+
```
65+
66+
Both `Freeze` and `Frozen` have the multiple overloads:
4867

68+
```csharp
69+
// freeze at the current instant:
70+
tc.Freeze();
71+
// freeze at the specified DateTime:
72+
tc.Freeze(new DateTime(1990, 12, 2, 14, 38, 51, DateTimeKind.Utc));
73+
// freeze at the specified date and time:
74+
tc.Freeze(1990, 12, 2, 14, 38, 51, DateTimeKind.Utc);
75+
// freeze at the specified date:
76+
tc.Freeze(1990, 12, 2, DateTimeKind.Utc);
77+
// freeze at the specified date or time or both:
78+
tc.Freeze(o => o.On(1990, 12, 2)
79+
.At(14, 13, 51)
80+
.LocalTime());
81+
```
82+
83+
### Traveling in time
4984

85+
Use the `TravelBy` method to travel forward and backward in time:
5086

87+
```csharp
88+
using var tc = Timecop.Frozen(1990, 12, 2, 14, 38, 51, DateTimeKind.Local);
89+
tc.TravelBy(TimeSpan.FromDays(1));
90+
Clock.Now; // 1990-12-03 14:38:51 - One day in the future
91+
```
92+
## License
5193

94+
Timecop was created by [Dmytro Khmara](https://dmytrokhmara.com) and is licensed under the [MIT license](LICENSE.txt).
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
namespace TCop.DateTimeUtils
2+
{
3+
internal class DatePart
4+
{
5+
public int Year { get; }
6+
public int Month { get; }
7+
public int Day { get; }
8+
9+
public DatePart(int year, int month, int day)
10+
{
11+
Year = year;
12+
Month = month;
13+
Day = day;
14+
}
15+
}
16+
}

src/Timecop/DateTimeBuilder.cs renamed to src/Timecop/DateTimeUtils/Builder/DateTimeBuilder.cs

Lines changed: 6 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,14 @@
11
using System;
22

3-
namespace TCop;
4-
5-
6-
public class DatePart
7-
{
8-
public int Year { get; }
9-
public int Month { get; }
10-
public int Day { get; }
11-
12-
public DatePart(int year, int month, int day)
13-
{
14-
Year = year;
15-
Month = month;
16-
Day = day;
17-
}
18-
}
19-
20-
public class TimePart
21-
{
22-
public int Hour { get; }
23-
public int Minute { get; }
24-
public int Second { get; }
25-
26-
public TimePart(int hour, int minute, int second)
27-
{
28-
Hour = hour;
29-
Minute = minute;
30-
Second = second;
31-
}
32-
}
33-
34-
public class DateTimeBuilderContext
35-
{
36-
public DatePart? Date { get; set; }
37-
public TimePart? Time { get; set; }
38-
39-
public DateTimeKind? Kind { get; set; }
40-
}
3+
namespace TCop.DateTimeUtils;
414

425
public class DateTimeBuilder
436
{
447
private readonly DateTimeBuilderContext _context = new DateTimeBuilderContext();
458

46-
public DateTimeBuilder()
47-
{
48-
}
49-
50-
public DateTimeBuilder At(int hour, int minute, int second)
9+
public DateTimeBuilder At(int hour, int minute, int second, int millisecond = 0)
5110
{
52-
_context.Time = new TimePart(hour, minute, second);
11+
_context.Time = new TimePart(hour, minute, second, millisecond);
5312
return this;
5413
}
5514

@@ -75,15 +34,15 @@ public DateTime Build()
7534
{
7635
if (_context.Kind == null || _context.Kind == DateTimeKind.Unspecified)
7736
{
78-
throw new InvalidDateTimeKindException();
37+
throw new DateTimeKindNotSpecifiedException();
7938
}
8039

8140
var now = _context.Kind == DateTimeKind.Local ? DateTime.Now : DateTime.UtcNow;
8241

8342
_context.Date ??= new DatePart(now.Year, now.Month, now.Day);
84-
_context.Time ??= new TimePart(now.Hour, now.Minute, now.Second);
43+
_context.Time ??= new TimePart(now.Hour, now.Minute, now.Second, now.Millisecond);
8544

8645
return new DateTime(_context.Date.Year, _context.Date.Month, _context.Date.Day,
87-
_context.Time.Hour, _context.Time.Minute, _context.Time.Second, _context.Kind.Value);
46+
_context.Time.Hour, _context.Time.Minute, _context.Time.Second, _context.Time.Millisecond, _context.Kind.Value);
8847
}
8948
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using System;
2+
3+
namespace TCop.DateTimeUtils
4+
{
5+
internal class DateTimeBuilderContext
6+
{
7+
public DatePart? Date { get; set; }
8+
public TimePart? Time { get; set; }
9+
10+
public DateTimeKind? Kind { get; set; }
11+
}
12+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using System;
2+
3+
namespace TCop.DateTimeUtils
4+
{
5+
public class DateTimeKindNotSpecifiedException : Exception
6+
{
7+
public DateTimeKindNotSpecifiedException() : base($"Specify either {nameof(DateTimeBuilder.LocalTime)}() or {nameof(DateTimeBuilder.UtcTime)}() when configuring time to freeze.")
8+
{
9+
}
10+
}
11+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
namespace TCop.DateTimeUtils
2+
{
3+
internal class TimePart
4+
{
5+
public int Hour { get; }
6+
public int Minute { get; }
7+
public int Second { get; }
8+
public int Millisecond { get; }
9+
10+
public TimePart(int hour, int minute, int second, int millisecond)
11+
{
12+
Hour = hour;
13+
Minute = minute;
14+
Second = second;
15+
Millisecond = millisecond;
16+
}
17+
}
18+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using System;
2+
3+
namespace TCop.DateTimeUtils
4+
{
5+
public class InvalidDateTimeKindException : Exception
6+
{
7+
public InvalidDateTimeKindException() : base("DateTimeKind.Unspecified is not supported. Use DateTimeKind.Utc or DateTimeKind.Local.")
8+
{
9+
}
10+
}
11+
}
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using System;
22

3-
namespace TCop;
3+
namespace TCop.DateTimeUtils;
44

55
public struct UtcDateTime
66
{
@@ -15,11 +15,4 @@ public UtcDateTime(DateTime value)
1515

1616
UtcValue = value.ToUniversalTime();
1717
}
18-
}
19-
20-
public class InvalidDateTimeKindException : Exception
21-
{
22-
public InvalidDateTimeKindException(): base("DateTimeKind.Unspecified is not supported. Use DateTimeKind.Utc or DateTimeKind.Local.")
23-
{
24-
}
2518
}

0 commit comments

Comments
 (0)