Skip to content

Commit

Permalink
Clock supports NowWithOffset (DateTimeOffset.Now equivalent) (#45)
Browse files Browse the repository at this point in the history
* Fix bug in LocalDateTime not returning DateTimeKind.Local, and losing ticks.
* Introduction of Clock.NowWithOffset(TimezoneInfo).
* To prevent unexpected behaviour, Clock.UtcNow() is always converted to DateTimeKind.Utc if needed.
  • Loading branch information
Corniel Nobel committed Apr 5, 2019
1 parent 4695c35 commit 9ee39a5
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 14 deletions.
35 changes: 30 additions & 5 deletions src/Qowaiv/Clock.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Linq;

namespace Qowaiv
{
Expand Down Expand Up @@ -31,20 +32,44 @@ public static class Clock
/// To be able to stub the clock, this simple class can be used.
///
/// The core if this clock it UTC, see: https://en.wikipedia.org/wiki/Coordinated_Universal_Time)
///
/// To prevent unexpected behaviour, the result is always converted to
/// <see cref="DateTimeKind.Utc"/> if needed.
/// </remarks>
public static DateTime UtcNow() => (threadUtcNow ?? globalUtcNow).Invoke();
public static DateTime UtcNow()
{
var utcNow = (threadUtcNow ?? globalUtcNow).Invoke();
return utcNow.Kind == DateTimeKind.Utc
? utcNow
: new DateTime(utcNow.Ticks, DateTimeKind.Utc);
}

/// <summary>Gets the time zone of the <see cref="Clock"/>.</summary>
public static TimeZoneInfo TimeZone => threadTimeZone ?? globalTimeZone;

/// <summary>Gets the current local <see cref="DateTime"/>.</summary>
public static DateTime Now() => Now(TimeZone);
/// <summary>Gets the current <see cref="LocalDateTime"/>.</summary>
public static LocalDateTime Now() => Now(TimeZone);

/// <summary>Gets the current <see cref="DateTime"/> for the specified time zone.</summary>
/// <summary>Gets the current <see cref="LocalDateTime"/> for the specified time zone.</summary>
/// <param name="timeZone">
/// The specified time zone.
/// </param>
public static DateTime Now(TimeZoneInfo timeZone) => TimeZoneInfo.ConvertTimeFromUtc(UtcNow(), Guard.NotNull(timeZone, nameof(timeZone)));
public static LocalDateTime Now(TimeZoneInfo timeZone) => TimeZoneInfo.ConvertTimeFromUtc(UtcNow(), Guard.NotNull(timeZone, nameof(timeZone)));

/// <summary>Gets the current <see cref="DateTimeOffset"/>.</summary>
public static DateTimeOffset NowWithOffset() => NowWithOffset(TimeZone);

/// <summary>Gets the current <see cref="DateTimeOffset"/> for the specified time zone.</summary>
/// <param name="timeZone">
/// The specified time zone.
/// </param>
public static DateTimeOffset NowWithOffset(TimeZoneInfo timeZone)
{
Guard.NotNull(timeZone, nameof(timeZone));
var utcNow = UtcNow();
var now = TimeZoneInfo.ConvertTimeFromUtc(utcNow, timeZone);
return new DateTimeOffset(now, now - utcNow);
}

/// <summary>Gets the yesterday for the local <see cref="DateTime"/>.</summary>
public static Date Yesterday() => Yesterday(TimeZone);
Expand Down
10 changes: 5 additions & 5 deletions src/Qowaiv/LocalDateTime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ public struct LocalDateTime : ISerializable, IXmlSerializable, IJsonSerializable
/// ticks is less than System.DateTime.MinValue or greater than System.DateTime.MaxValue.
/// </exception>
public LocalDateTime(long ticks)
: this(new DateTime(ticks)) { }
{
m_Value = new DateTime(ticks, DateTimeKind.Local);
}

/// <summary>Initializes a new instance of the local date time structure based on a System.DateTime.
/// </summary>
Expand All @@ -51,7 +53,7 @@ public LocalDateTime(long ticks)
/// <remarks>
/// The date of the date time is taken.
/// </remarks>
private LocalDateTime(DateTime dt) : this(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, dt.Second, dt.Millisecond) { }
private LocalDateTime(DateTime dt) : this(dt.Ticks) { }

/// <summary>Initializes a new instance of the date structure to the specified year, month, and day.</summary>
/// <param name="year">
Expand Down Expand Up @@ -161,9 +163,7 @@ public LocalDateTime(int year, int month, int day, int hour, int minute, int sec
/// more than date.MaxValue.
/// </exception>
public LocalDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond)
{
m_Value = new DateTime(year, month, day, hour, minute, second, millisecond, DateTimeKind.Local);
}
: this(new DateTime(year, month, day, hour, minute, second, millisecond, DateTimeKind.Local)) { }

#endregion

Expand Down
23 changes: 22 additions & 1 deletion test/Qowaiv.UnitTests/ClockTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,34 @@ public void TimeZone_CurrentThread_EqualsTestDateTime()
}
}

[Test]
public void NowWithOffset_WestEuropeanWithoutDaylightSaving_Plus1()
{
using (Clock.SetTimeAndTimeZoneForCurrentThread(() => new DateTime(2019, 02, 14), TimeZoneInfo.FindSystemTimeZoneById("W. Europe Standard Time")))
{
var act = Clock.NowWithOffset();
var exp = new DateTimeOffset(new LocalDateTime(2019, 02, 14, 1, 0, 0), TimeSpan.FromHours(+1));
Assert.AreEqual(exp, act);
}
}
[Test]
public void NowWithOffset_WestEuropeanWithDaylightSaving_Plus2()
{
using (Clock.SetTimeAndTimeZoneForCurrentThread(() => new DateTime(2019, 04, 01), TimeZoneInfo.FindSystemTimeZoneById("W. Europe Standard Time")))
{
var act = Clock.NowWithOffset();
var exp = new DateTimeOffset(new LocalDateTime(2019, 04, 01, 2, 0, 0), TimeSpan.FromHours(+2));
Assert.AreEqual(exp, act);
}
}

[Test]
public void Now_TestTimeZone_10HoursLaterThanUtc()
{
using (Clock.SetTimeAndTimeZoneForCurrentThread(TestTimeFunction, TestTimeZone))
{
var act = Clock.Now();
var exp = new DateTime(2017, 06, 11, 16, 15, 00);
var exp = new LocalDateTime(2017, 06, 11, 16, 15, 00);

Assert.AreEqual(exp, act);
}
Expand Down
6 changes: 3 additions & 3 deletions test/Qowaiv.UnitTests/LocalDateTimeTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -632,10 +632,10 @@ public void Min_LocalDateTimeTime_AreEqual()
}

[Test]
public void AddTicks_4000000000017_AreEqual()
public void AddTicks_4000001700000_AreEqual()
{
var act = TestStruct.AddTicks(4000000000017L);
var exp = new LocalDateTime(1988, 06, 18, 13, 16, 45, 001);
var act = TestStruct.AddTicks(4000001700000L);
var exp = new LocalDateTime(1988, 06, 18, 13, 16, 45, 171);

Assert.AreEqual(exp, act);
}
Expand Down

0 comments on commit 9ee39a5

Please sign in to comment.