Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expand API surface for DateTime, DateTimeOffset, DateOnly, TimeOnly, and TimeSpan #113033

Open
jscarle opened this issue Mar 1, 2025 · 8 comments
Labels
api-suggestion Early API idea and discussion, it is NOT ready for implementation area-System.DateTime
Milestone

Comments

@jscarle
Copy link

jscarle commented Mar 1, 2025

Background and Motivation

Almost every project I've worked on, from small personal projects to large enterprise ones, ends up with various extension methods designed to make date and time manipulation more concise and less repetitive. Many of these are born out of inconsistencies and/or shortcomings of the current API surface on the five core date and time types: DateTime, DateTimeOffset, DateOnly, TimeOnly, and TimeSpan. Specifically, DateOnly and TimeOnly do not feel like first class citizens next to DateTime and DateTimeOffset.

In an ecosystem where version after version, compiler and syntax changes have been consitently made to reduce the amount of syntax needed to create types (ie: collection expressions), I feel like these core system types have not been given the same amount of attention. Yet date and time manipulation is probably one of the most common operations and omni present in almost every single project a .NET developer will be involved in.

Proposed API

To avoid creating a large amount of issues that are all centered around the same background and motivation, I opted instead on creating a list of propose APIs with additional notes where appropriate. I have tried to group them as best as I can.

Missing APIs for consistency

1) DateTimeOffset.FromDateTime(DateTime dateTime) static method

  • DateOnly and TimeOnly both already expose FromDateTime().
  • A DateTime can already be cast into a DateTimeOffset.

2) DateTime.FromDateTimeOffset() static method

  • There is already a constructor overload on DateTimeOffset which supports a DateTime.
  • With proposal 1, DateTimeOffset would also expose the similar FromDateTime().

3) dateTime.ToDateTimeOffset(TimeSpan offset) instance method

  • There is already a constructor overload on DateTimeOffset which supports a DateTime and TimeSpan.
  • DateOnly instances already expose the similar ToDateTime.

4) DateTimeOffset.Today and DateOnly.Today property

  • DateTimeOffset already exposes Now and UtcNow like DateTime.
  • DateTime already exposes Today.

5) TimeOnly.Now and TimeOnly.UtcNow property

  • DateTime and DateTimeOffset already expose Now and UtcNow.

6) timeOnly.AddSeconds(double value), timeOnly.AddMilliseconds(double value), timeOnly.AddMicroseconds(double value), and timeOnly.AddTicks(long value) instance method

  • DateTime and DateTimeOffset instances both already expose AddSeconds(), AddMilliseconds(), AddMicroseconds(), and AddTicks().

7) DateOnly.FromDateTimeOffset(DateTimeOffset dateTimeOffset) and TimeOnly.FromDateTimeOffset(DateTimeOffset dateTimeOffset) static methods

  • DateOnly and TimeOnly both already expose FromDateTime().

8) DateTime.FromDateOnly(DateOnly dateOnly) and DateTimeOffset.FromDateOnly(DateOnly dateOnly) static methods

  • DateOnly and TimeOnly both already expose FromDateTime().

9) dateTime.ToDateOnly() instance method

  • DateOnly instances already expose ToDateTime().

10) dateTimeOffset.ToDateOnly() instance method

  • DateOnly instances already expose ToDateTime().
  • With proposal 9, DateTime instances would also expose ToDateOnly().

11) dateOnly.ToDateTimeOffset() and dateOnly.ToDateTimeOffset(TimeSpan offset) instance methods

  • DateOnly instances already expose ToDateTime().
  • With proposal 9 and 10, DateTime instances would also expose ToDateOnly() and DateTimeOffset instances would also expose .ToDateOnly().

12) dateTime.ToTimeOnly() and dateTimeOffset.ToTimeOnly() instance methods

  • DateOnly instances already expose ToDateTime().
  • With proposal 9, 10, and 11, DateTime instances would also expose ToDateOnly(), DateTimeOffset instances would also expose .ToDateOnly(), and DateOnly instances would also expose .ToDateTimeOffset().

13) Implicit cast from DateOnly instances to DateTimeOffset instances

  • DateTime instances already implicitly cast to DateTimeOffset.

Expanding Today API

14) DateTime.UtcToday, DateTimeOffset.UtcToday, DateOnly.UtcToday

  • DateTime and DateTimeOffset already expose the properties Now and UtcNow, this would be natural pairing to the already exposed Today property.
  • With proposal 4, both DateTimeOffset and DateOnly would also expose Today.

New Substract APIs

15) Substract instance method variants of Add to DateTime, DateTimeOffset, DateOnly, and TimeOnly

  • Add the SubstractYears(int value) instance method to DateTime, DateTimeOffset, and DateOnly.
  • Add the SubstractMonths(int value) instance method to DateTime, DateTimeOffset, and DateOnly.
  • Add the SubstractDays(double value) instance method to DateTime, DateTimeOffset, and SubstractDays(int value) instance method DateOnly.
  • Add the SubstractHours(double value) instance method to DateTime, DateTimeOffset, and TimeOnly.
  • Add the SubstractMinutes(double value) instance method to DateTime, DateTimeOffset, and TimeOnly.
  • Add the SubstractSeconds(double value) instance method to DateTime, DateTimeOffset, and TimeOnly.
  • Add the SubstractMilliseconds(double value) instance method to DateTime, DateTimeOffset, and TimeOnly.
  • Add the SubstractMicroseconds(double value) instance method to DateTime, DateTimeOffset, and TimeOnly.
  • Add the SubstractTicks(long value) instance method to DateTime, DateTimeOffset, and TimeOnly.

Expand TimeSpan API

#### 16) TimeSpan.FromYears(int value) and TimeSpan.FromMonths(int value) static methods

  • TimeSpan already exposes all other From methods.

17) TimeSpan.Add variants and TimeSpan.Substract variants

  • Adding the AddYears(), AddMonths(), AddDays(), AddHours(), AddMinutes(), AddSeconds(), AddMilliseconds(), AddMicroseconds(), AddTicks() instance methods would avoid needing to create an intermediate TimeSpan to manipulate an existing TimeSpan.
  • Also in the same vein, adding the instance methods from proposal 15.
@dotnet-policy-service dotnet-policy-service bot added the untriaged New issue has not been triaged by the area owner label Mar 1, 2025
Copy link
Contributor

Tagging subscribers to this area: @dotnet/area-system-datetime
See info in area-owners.md if you want to be subscribed.

@tarekgh tarekgh added this to the Future milestone Mar 1, 2025
@tarekgh tarekgh added api-suggestion Early API idea and discussion, it is NOT ready for implementation and removed untriaged New issue has not been triaged by the area owner labels Mar 1, 2025
@KalleOlaviNiemitalo
Copy link

  • Add the SubstractDays(double value) instance method to DateTime, DateTimeOffset, and DateOnly.

There is DateOnly.AddDays(Int32) but no DateOnly.AddDays(Double), so I don't think there should be DateOnly.SubtractDays(Double) either.

@KalleOlaviNiemitalo
Copy link

  1. TimeSpan.FromYears(int value) and TimeSpan.FromMonths(int value) static methods

Not feasible because years and months do not have a constant number of days and thus not a constant number of ticks either.

@jscarle
Copy link
Author

jscarle commented Mar 1, 2025

  • Add the SubstractDays(double value) instance method to DateTime, DateTimeOffset, and DateOnly.

There is DateOnly.AddDays(Int32) but no DateOnly.AddDays(Double), so I don't think there should be DateOnly.SubtractDays(Double) either.

I adjusted the overload.

@jscarle
Copy link
Author

jscarle commented Mar 1, 2025

  1. TimeSpan.FromYears(int value) and TimeSpan.FromMonths(int value) static methods

Not feasible because years and months do not have a constant number of days and thus not a constant number of ticks either.

Makes sense. Withdrawn.

@Clockwork-Muse
Copy link
Contributor

  1. TimeSpan.FromYears(int value) and TimeSpan.FromMonths(int value) static methods

Not feasible because years and months do not have a constant number of days and thus not a constant number of ticks either.

Strictly speaking days don't either, but that only happens every ~18 months or so (and support is spotty in programming for leap seconds even then)


If you're going to be working with date/time extensively, you may want to look at NodaTime

@colejohnson66
Copy link

I agree with the premise behind this proposal. When I try to do things right (like record a calibration date with a DateOnly), I’m forced to jump through a few hoops just to do the math I want. Especially DateTimeOffset; if I try to do date time correct using TimeProvider, I’m “encouraged” by the lacking API surface to abandon my attempt and use a normal DateTime.

NodaTime, while the “correct” approach to many date/time problems, is complete overkill for those who don’t need to work with dates that often.

@jscarle
Copy link
Author

jscarle commented Mar 2, 2025

NodaTime, while the “correct” approach to many date/time problems, is complete overkill for those who don’t need to work with dates that often.

That's sort of the spirit of this request. We shouldn't have to resort to using NodaTime unless we're doing more involved date/time manipulations. The issue at the moment is that the current API surface is inconsistent and lacking with DateOnly and TimeOnly especially feeling like forgotten types.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api-suggestion Early API idea and discussion, it is NOT ready for implementation area-System.DateTime
Projects
None yet
Development

No branches or pull requests

5 participants