Skip to content

Commit

Permalink
docs on localdatetime
Browse files Browse the repository at this point in the history
  • Loading branch information
mattiasnordqvist committed Sep 23, 2024
1 parent 9c02dab commit 7785945
Show file tree
Hide file tree
Showing 8 changed files with 106 additions and 17 deletions.
29 changes: 19 additions & 10 deletions DotNetThoughts.sln
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,6 @@ EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetThoughts.TimeKeeping.Tests", "TimeKeeping\DotNetThoughts.TimeKeeping.Tests\DotNetThoughts.TimeKeeping.Tests.csproj", "{DF62C221-71AD-49A7-A9D8-48E31F06E27B}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TimeKeeping", "TimeKeeping", "{695E363E-055E-4ED5-9613-587E02A67578}"
ProjectSection(SolutionItems) = preProject
TimeKeeping\ReadMe.md = TimeKeeping\ReadMe.md
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetThoughts.LocalTimeKit", "TimeKeeping\DotNetThoughts.LocalTimeKit\DotNetThoughts.LocalTimeKit.csproj", "{03DA1023-FDF4-4EF5-8951-19806434F8CA}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetThoughts.Results", "Results\DotNetThoughts.Results\DotNetThoughts.Results.csproj", "{7032EE8C-FC18-4610-B7D6-613E552C278D}"
EndProject
Expand Down Expand Up @@ -52,6 +47,15 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
ReadMe.md = ReadMe.md
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "LocalTimeKit", "LocalTimeKit", "{11FA58AF-0B7C-4465-8448-AA710D5725AA}"
ProjectSection(SolutionItems) = preProject
LocalTimeKit\ReadMe.md = LocalTimeKit\ReadMe.md
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetThoughts.LocalTimeKit", "LocalTimeKit\DotNetThoughts.LocalTimeKit\DotNetThoughts.LocalTimeKit.csproj", "{0F5838D8-AC76-4C87-8DB6-6AE8140397A8}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNetThoughts.LocalTimeKit.Tests", "LocalTimeKit\DotNetThoughts.LocalTimeKit.Tests\DotNetThoughts.LocalTimeKit.Tests.csproj", "{BA3C3301-7D35-4828-8F00-736CDDEBC800}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -70,10 +74,6 @@ Global
{DF62C221-71AD-49A7-A9D8-48E31F06E27B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DF62C221-71AD-49A7-A9D8-48E31F06E27B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DF62C221-71AD-49A7-A9D8-48E31F06E27B}.Release|Any CPU.Build.0 = Release|Any CPU
{03DA1023-FDF4-4EF5-8951-19806434F8CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{03DA1023-FDF4-4EF5-8951-19806434F8CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{03DA1023-FDF4-4EF5-8951-19806434F8CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{03DA1023-FDF4-4EF5-8951-19806434F8CA}.Release|Any CPU.Build.0 = Release|Any CPU
{7032EE8C-FC18-4610-B7D6-613E552C278D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7032EE8C-FC18-4610-B7D6-613E552C278D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7032EE8C-FC18-4610-B7D6-613E552C278D}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand Down Expand Up @@ -106,6 +106,14 @@ Global
{E0E86A0B-F4CE-4E88-99CF-C54AFC1C0C58}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E0E86A0B-F4CE-4E88-99CF-C54AFC1C0C58}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E0E86A0B-F4CE-4E88-99CF-C54AFC1C0C58}.Release|Any CPU.Build.0 = Release|Any CPU
{0F5838D8-AC76-4C87-8DB6-6AE8140397A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0F5838D8-AC76-4C87-8DB6-6AE8140397A8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0F5838D8-AC76-4C87-8DB6-6AE8140397A8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0F5838D8-AC76-4C87-8DB6-6AE8140397A8}.Release|Any CPU.Build.0 = Release|Any CPU
{BA3C3301-7D35-4828-8F00-736CDDEBC800}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BA3C3301-7D35-4828-8F00-736CDDEBC800}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BA3C3301-7D35-4828-8F00-736CDDEBC800}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BA3C3301-7D35-4828-8F00-736CDDEBC800}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -114,7 +122,6 @@ Global
{6B9CCBB9-73D3-4062-8317-5D54CF6B85D4} = {695E363E-055E-4ED5-9613-587E02A67578}
{917A0D7B-F7FA-4D39-B892-B9C1BA9BE284} = {695E363E-055E-4ED5-9613-587E02A67578}
{DF62C221-71AD-49A7-A9D8-48E31F06E27B} = {695E363E-055E-4ED5-9613-587E02A67578}
{03DA1023-FDF4-4EF5-8951-19806434F8CA} = {695E363E-055E-4ED5-9613-587E02A67578}
{7032EE8C-FC18-4610-B7D6-613E552C278D} = {BECD3795-EDD5-459E-BFCC-017082E1C34D}
{8CA73623-9009-4FEF-885E-84F9BBD61536} = {BECD3795-EDD5-459E-BFCC-017082E1C34D}
{02C498D0-532A-4F1D-9004-7E2E24121DF3} = {BECD3795-EDD5-459E-BFCC-017082E1C34D}
Expand All @@ -123,6 +130,8 @@ Global
{66DCA5D2-4A06-4F1A-8DBB-36118CC01A34} = {BECD3795-EDD5-459E-BFCC-017082E1C34D}
{3D9F5F06-E09D-46B4-8058-1458467E0252} = {BECD3795-EDD5-459E-BFCC-017082E1C34D}
{E0E86A0B-F4CE-4E88-99CF-C54AFC1C0C58} = {BECD3795-EDD5-459E-BFCC-017082E1C34D}
{0F5838D8-AC76-4C87-8DB6-6AE8140397A8} = {11FA58AF-0B7C-4465-8448-AA710D5725AA}
{BA3C3301-7D35-4828-8F00-736CDDEBC800} = {11FA58AF-0B7C-4465-8448-AA710D5725AA}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {CAA5B6BA-B6C8-4A90-B9E6-D7634295532E}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\DotNetThoughts.LocalTimeKit\DotNetThoughts.LocalTimeKit.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
using DotNetThoughts.LocalTimeKit;

namespace DotNetThoughts.TimeKeeping.Tests;
namespace DotNetThoughts.LocalTimeKit.Tests;
public class LocalDateTimeTests
{

[Fact]
public void TestLocalDateTimeOffset()
{
var t = new DateTime(2024,06,06, 0, 0, 0, DateTimeKind.Unspecified);
var t = new DateTime(2024, 06, 06, 0, 0, 0, DateTimeKind.Unspecified);
var tz = TimeZoneInfo.FindSystemTimeZoneById("Central European Standard Time");
var ldt = new LocalDateTime(t, tz);
var expectedDateTimeOffset = DateTimeOffset.Parse("2024-06-06T00:00:00+02:00");
Expand Down Expand Up @@ -67,7 +65,7 @@ public void TestDateConstructor()
var tz = TimeZoneInfo.FindSystemTimeZoneById("Central European Standard Time");
var ldt = new LocalDateTime(t, tz);
var ldt2 = new LocalDateTime(d, tz);

ldt.DateTime.Should().Be(ldt2.DateTime);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<Version>1.0.0</Version>
<Version>1.0.1</Version>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
</PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,40 @@
/// </summary>
public readonly record struct LocalDateTime
{
/// <summary>
/// Creates a LocalDateTime from a DateOnly and a TimeZoneInfo
/// The date represents midnight in the given timezone
///
/// See <see cref="LocalDateTime(DateTime, TimeZoneInfo)"/>
/// </summary>
/// <param name="date"></param>
/// <param name="timeZoneInfo"></param>
public LocalDateTime(DateOnly date, TimeZoneInfo timeZoneInfo)
: this(date.ToDateTime(TimeOnly.MinValue), timeZoneInfo) { }

/// <summary>
/// Creates a LocalDateTime from a DateTimeOffset and a TimeZoneInfo
///
/// See <see cref="LocalDateTime(DateTime, TimeZoneInfo)"/>
/// </summary>
/// <param name="dateTimeOffset"></param>
/// <param name="timeZoneInfo"></param>
public LocalDateTime(DateTimeOffset dateTimeOffset, TimeZoneInfo timeZoneInfo)
: this(TimeZoneInfo.ConvertTime(dateTimeOffset, timeZoneInfo).DateTime, timeZoneInfo) { }

/// <summary>
/// Creates a LocalDateTime from a DateTime and a TimeZoneInfo
/// If the datetime has DateTimeKind.Local, the timezone must be TimeZoneInfo.Local
/// If the datetime has DateTimeKind.Utc, the timezone must be TimeZoneInfo.Utc
/// Otherwise, the timezone must not be TimeZoneInfo.Local
/// In any of the cases above, an ArgumentException is thrown.
///
/// Some times are invalid for some timezones, and some times are ambiguous.
/// Any such time and timezone combination will throw an ArgumentException.
/// </summary>
/// <param name="dateTime"></param>
/// <param name="timeZoneInfo"></param>
/// <exception cref="ArgumentException"></exception>
public LocalDateTime(DateTime dateTime, TimeZoneInfo timeZoneInfo)
{
if (!(dateTime.Kind == DateTimeKind.Local && timeZoneInfo == TimeZoneInfo.Local
Expand All @@ -37,32 +65,57 @@ public LocalDateTime(DateTime dateTime, TimeZoneInfo timeZoneInfo)
public DateTime DateTime { get; }

Check warning on line 65 in LocalTimeKit/DotNetThoughts.LocalTimeKit/LocalDateTime.cs

View workflow job for this annotation

GitHub Actions / create_nuget

Missing XML comment for publicly visible type or member 'LocalDateTime.DateTime'
public TimeZoneInfo TimeZoneInfo { get; }

Check warning on line 66 in LocalTimeKit/DotNetThoughts.LocalTimeKit/LocalDateTime.cs

View workflow job for this annotation

GitHub Actions / create_nuget

Missing XML comment for publicly visible type or member 'LocalDateTime.TimeZoneInfo'

/// <summary>
/// Returns a new LocalDateTime representing the same instance in time, but in relation to a different time zone.
/// </summary>
/// <param name="newTz"></param>
/// <returns></returns>
public LocalDateTime ToTimeZone(TimeZoneInfo newTz)
{
return new LocalDateTime(TimeZoneInfo.ConvertTime(DateTime, TimeZoneInfo, newTz), newTz);
}

/// <summary>
/// Returns a new LocalDateTime representing the midnight of the current date.
/// </summary>
/// <returns></returns>
public LocalDateTime ToMidnight()
{
return new LocalDateTime(DateTime.Date, TimeZoneInfo);
}

/// <summary>
/// Returns a new LocalDateTime representing the beginning of the current year.
/// </summary>
/// <returns></returns>
public LocalDateTime ToBeginningOfYear()
{
return new LocalDateTime(new DateTime(DateTime.Year, 1, 1), TimeZoneInfo);
}

/// <summary>
/// Returns a new LocalDateTime representing the end of the current year.
/// </summary>
/// <returns></returns>
public LocalDateTime ToEndOfYear()
{
return new LocalDateTime(new DateTime(DateTime.Year, 12, 31, 23, 59, 59, 999, 999), TimeZoneInfo);
}

/// <summary>
/// Returns a DateTimeOffset, with offset 0, representing this instance in time.
/// </summary>
/// <returns></returns>
public DateTimeOffset ToDateTimeOffsetUtc()
{
var shouldHaveHadKindUtc = TimeZoneInfo.ConvertTimeToUtc(DateTime, TimeZoneInfo);
return shouldHaveHadKindUtc;
}

/// <summary>
/// Returns a DateTimeOffset, with offset matching the current timezone, representing this instance in time.
/// </summary>
/// <returns></returns>
public DateTimeOffset ToDateTimeOffsetLocal()
{
return new DateTimeOffset(DateTime, TimeZoneInfo.GetUtcOffset(DateTime));
Expand Down
14 changes: 14 additions & 0 deletions LocalTimeKit/ReadMe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Local Time Kit

## LocalDateTime

`LocalDateTime` is an unambiguous representation of an instance in time.
Types like `DateTime` and `DateOnly` does not provide enough information to determine exactly what instance in time they represent. LocalDateTime aims to package all you need into one type.

Don't worry about the word "Local" in the name. LocalDateTime is not limited to local instances in time in the normal sense. LocalDateTime consider even a UTC date to be local. Local to the utc time zone.

While .Net has a lot of shady "helpful" implicit conversions between `DateTime` and `DateTimeOffset`, LocalDateTime is explicit about what it does.

No time-operations, makes any sense without knowing which timezone you are operating in. Remember, daylight savings have the effect of some days lasting only 23 hours, while others last 25.
LocalDateTime therefor requires you to specify a timezone when you create an instance.
LocalDateTime then helps you to convert between timezones, and to calculate timezone-related specific instances of time, like beginning of year, or midnight of current day etc.
3 changes: 3 additions & 0 deletions ReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,8 @@
# TimeKeeping
[TimeKeeping/ReadMe.md](TimeKeeping/ReadMe.md)

# LocalDateTime
[LocalDateTime/ReadMe.md](LocalDateTime/ReadMe.md)

# UserSecrets
[UserSecrets/ReadMe.md](UserSecrets/ReadMe.md)
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">

<ItemGroup>
<ProjectReference Include="..\DotNetThoughts.LocalTimeKit\DotNetThoughts.LocalTimeKit.csproj" />
<ProjectReference Include="..\DotNetThoughts.TimeKeeping\DotNetThoughts.TimeKeeping.csproj" />
</ItemGroup>

Expand Down

0 comments on commit 7785945

Please sign in to comment.