Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
namespace DateOnlyAndTimeOnlyInCSharpExample;
using System.Globalization;

namespace DateOnlyAndTimeOnlyInCSharpExample;

public class Program
{
public static void Main()
{
// The printed output below is culture-dependent, so we state the culture
// instead of letting the host machine decide what the samples look like.
CultureInfo.CurrentCulture = new CultureInfo("en-US");

DemonstrateDateOnly();
DemonstrateTimeOnly();
DemonstrateFromDateTime();
DemonstrateConversions();
DemonstrateBasicOperators();
}

Expand Down Expand Up @@ -62,6 +69,54 @@ public static void DemonstrateFromDateTime()
Console.WriteLine(timeOnly);
}

public static void DemonstrateConversions()
{
var dateOnly = new DateOnly(2022, 1, 1);
var timeOnly = new TimeOnly(11, 30);

// DateOnly back to DateTime, in all three shapes.
var combined = dateOnly.ToDateTime(timeOnly);
var midnight = dateOnly.ToDateTime(TimeOnly.MinValue);
var utc = dateOnly.ToDateTime(timeOnly, DateTimeKind.Utc);

Console.WriteLine($"{combined:O} Kind={combined.Kind}");
Console.WriteLine($"{midnight:O} Kind={midnight.Kind}");
Console.WriteLine($"{utc:O} Kind={utc.Kind}");

// Neither type has a Now or a Today property.
var today = DateOnly.FromDateTime(DateTime.Now);
var timeOfDay = TimeOnly.FromDateTime(DateTime.Now);

Console.WriteLine(today);
Console.WriteLine(timeOfDay);

// TimeSpan in both directions.
var fromTimeSpan = TimeOnly.FromTimeSpan(TimeSpan.FromHours(11.5));
var sinceMidnight = timeOnly.ToTimeSpan();

Console.WriteLine(fromTimeSpan);
Console.WriteLine(sinceMidnight);

// The underlying day count.
var dayNumber = dateOnly.DayNumber;
var fromDayNumber = DateOnly.FromDayNumber(dayNumber);

Console.WriteLine(dayNumber);
Console.WriteLine(fromDayNumber);

// Parsing a string in a known, fixed format.
var parsed = DateOnly.ParseExact("2022-01-01", "yyyy-MM-dd");

Console.WriteLine(parsed);

// AddHours wraps past midnight; the second overload reports the day boundary.
var elevenPM = new TimeOnly(23, 0);
var wrapped = elevenPM.AddHours(2);
var wrappedWithCount = elevenPM.AddHours(2, out var excessDays);

Console.WriteLine($"{wrapped} / {wrappedWithCount} excessDays={excessDays}");
}

public static void DemonstrateBasicOperators()
{
var firstOfJan = new DateOnly(2022, 1, 1);
Expand Down
87 changes: 87 additions & 0 deletions dotnet-datetime/DateOnlyAndTimeOnlyInCSharp/Tests/DateOnlyTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,5 +112,92 @@ public void CanUseGreaterThanOperator()
// Assert.
Assert.True(isAfter);
}
[Fact]
public void CanParseExactWithAKnownFormat()
{
// Arrange.
var text = "2022-01-01";

// Act.
var date = DateOnly.ParseExact(text, "yyyy-MM-dd");

// Assert.
Assert.Equal(new DateOnly(2022, 1, 1), date);
}

[Fact]
public void CanConvertToDateTimeWithATimeOnly()
{
// Arrange.
var dateOnly = new DateOnly(2022, 1, 1);
var timeOnly = new TimeOnly(11, 30);

// Act.
var combined = dateOnly.ToDateTime(timeOnly);

// Assert.
Assert.Equal(new DateTime(2022, 1, 1, 11, 30, 0), combined);
Assert.Equal(DateTimeKind.Unspecified, combined.Kind);
}

[Fact]
public void CanConvertToDateTimeAtMidnight()
{
// Arrange.
var dateOnly = new DateOnly(2022, 1, 1);

// Act.
var midnight = dateOnly.ToDateTime(TimeOnly.MinValue);

// Assert.
Assert.Equal(new DateTime(2022, 1, 1, 0, 0, 0), midnight);
Assert.Equal(DateTimeKind.Unspecified, midnight.Kind);
}

[Fact]
public void CanConvertToDateTimeWithAKnownKind()
{
// Arrange.
var dateOnly = new DateOnly(2022, 1, 1);
var timeOnly = new TimeOnly(11, 30);

// Act.
var utc = dateOnly.ToDateTime(timeOnly, DateTimeKind.Utc);

// Assert.
Assert.Equal(DateTimeKind.Utc, utc.Kind);
Assert.Equal(new DateTime(2022, 1, 1, 11, 30, 0, DateTimeKind.Utc), utc);
}

[Fact]
public void CanGetTodaysDateFromDateTimeNow()
{
// Arrange.
var now = DateTime.Now;

// Act.
var today = DateOnly.FromDateTime(now);

// Assert. CI runs at an arbitrary instant, so we assert consistency, not a value.
Assert.Equal(now.Year, today.Year);
Assert.Equal(now.Month, today.Month);
Assert.Equal(now.Day, today.Day);
}

[Fact]
public void CanRoundTripThroughDayNumber()
{
// Arrange.
var dateOnly = new DateOnly(2022, 1, 1);

// Act.
var dayNumber = dateOnly.DayNumber;
var roundTripped = DateOnly.FromDayNumber(dayNumber);

// Assert.
Assert.Equal(dateOnly, roundTripped);
Assert.Equal(1, dateOnly.DayNumber - new DateOnly(2021, 12, 31).DayNumber);
}

}
}
10 changes: 5 additions & 5 deletions dotnet-datetime/DateOnlyAndTimeOnlyInCSharp/Tests/Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>

<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.9.0" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="4.0.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.1.0">
<PackageReference Include="coverlet.collector" Version="10.0.1">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
Expand Down
62 changes: 62 additions & 0 deletions dotnet-datetime/DateOnlyAndTimeOnlyInCSharp/Tests/TimeOnlyTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,5 +97,67 @@ public void CanUseGreaterThanOperator()
// Assert.
Assert.True(isAfter);
}
[Fact]
public void CanConvertToAndFromTimeSpan()
{
// Arrange.
var timeOnly = new TimeOnly(11, 30);

// Act.
var sinceMidnight = timeOnly.ToTimeSpan();
var roundTripped = TimeOnly.FromTimeSpan(sinceMidnight);

// Assert.
Assert.Equal(TimeSpan.FromHours(11.5), sinceMidnight);
Assert.Equal(timeOnly, roundTripped);
}

[Fact]
public void CanGetTheCurrentTimeOfDayFromDateTimeNow()
{
// Arrange.
var now = DateTime.Now;

// Act.
var timeOfDay = TimeOnly.FromDateTime(now);

// Assert. CI runs at an arbitrary instant, so we assert consistency, not a value.
Assert.Equal(now.Hour, timeOfDay.Hour);
Assert.Equal(now.Minute, timeOfDay.Minute);
Assert.Equal(now.TimeOfDay.Ticks, timeOfDay.Ticks);
}

[Fact]
public void AddHoursWrapsPastMidnightAndCanReportTheExcessDays()
{
// Arrange.
var elevenPM = new TimeOnly(23, 0);

// Act.
var wrapped = elevenPM.AddHours(2);
var wrappedWithCount = elevenPM.AddHours(2, out var excessDays);

// Assert.
Assert.Equal(new TimeOnly(1, 0), wrapped);
Assert.Equal(new TimeOnly(1, 0), wrappedWithCount);
Assert.Equal(1, excessDays);
}

[Fact]
public void SubtractingTwoValuesIsAlwaysPositive()
{
// Arrange.
var elevenPM = new TimeOnly(23, 0);
var oneAM = new TimeOnly(1, 0);

// Act.
var forwards = oneAM - elevenPM;
var backwards = elevenPM - oneAM;

// Assert.
Assert.Equal(TimeSpan.FromHours(2), forwards);
Assert.Equal(TimeSpan.FromHours(22), backwards);
}

}
}
Loading