Skip to content

[API Proposal]: Expose the remaining const values in TimeSpan #94545

Description

@IDisposable

Edited By @tarekgh

This proposal has been approved before #94545 (comment). There was some feedback from @tannergooding and @IDisposable.

@tannergooding said: the concern I raised is that there are scenarios this can cause problems since days * TimeSpan.HoursPerDay could overflow. For example, if days > 89,478,485 then this will overflow to a negative int.
HoursPerDay happens to be the only case where it is "sensible" for it to be int, since both MaxDays (10,675,199) and MaxHours (256,204,778) are within int.MaxValue. That is also why the signatures are FromDays(int days, int hours, long minutes, long milliseconds, long microseconds). So other values, like MinutesPerDay (1440), MinutesPerHour (60), or even the existing MillisecondsPerTick (10,000) are all expected to be used in contexts where long is the target type.
When considering TimeSpan, which is where these constants are defined, hours and days are int, all other units are long.


Background and motivation

In the TimeSpan class there are a few public const values that are generally applicable that are usefully exposed.

There are also a bunch more that are internal const but would be generally useful for other code and are not more implementation driven than the already exposed const values.

These values should be exposed publicly on the TimeSpan class.

Note: NOT suggesting we expose the truly internal const values that are related to MinTicks and MaxTicks. They could be included but are just not as naturally useful elsewhere.

API Proposal

namespace System.Collections.Generic;

public class TimeSpan: // elided details
{
        // more elision

        public const long MicrosecondsPerMillisecond = TicksPerMillisecond / TicksPerMicrosecond; //           1,000
        public const long MicrosecondsPerSecond = TicksPerSecond / TicksPerMicrosecond;           //       1,000,000
        public const long MicrosecondsPerMinute = TicksPerMinute / TicksPerMicrosecond;           //      60,000,000
        public const long MicrosecondsPerHour = TicksPerHour / TicksPerMicrosecond;               //   3,600,000,000
        public const long MicrosecondsPerDay = TicksPerDay / TicksPerMicrosecond;                 //  86,400,000,000

        public const long MillisecondsPerSecond = TicksPerSecond / TicksPerMillisecond;           //           1,000
        public const long MillisecondsPerMinute = TicksPerMinute / TicksPerMillisecond;           //          60,000
        public const long MillisecondsPerHour = TicksPerHour / TicksPerMillisecond;               //       3,600,000
        public const long MillisecondsPerDay = TicksPerDay / TicksPerMillisecond;                 //      86,400,000

        public const long SecondsPerMinute = TicksPerMinute / TicksPerSecond;                     //              60
        public const long SecondsPerHour = TicksPerHour / TicksPerSecond;                         //           3,600
        public const long SecondsPerDay = TicksPerDay / TicksPerSecond;                           //          86,400

        public const long MinutesPerHour = TicksPerHour / TicksPerMinute;                         //              60
        public const long MinutesPerDay = TicksPerDay / TicksPerMinute;                           //           1,440

        public const long HoursPerDay = TicksPerDay / TicksPerHour;                               //              24
}

API Usage

if (TimeSpan.MinutesPerHour * numHours) > TimeSpan.MinutesPerDay * 7)
    Console.WriteLine("That's really weeks");

Alternative Designs

N/A

Risks

Since these are compile-time constant values, there would be no runtime cost in either memory or execution overhead. Additionally they would centralize basic real-world facts to one logical location (TimeSpan).

Activity

  1. added
    api-suggestionEarly API idea and discussion, it is NOT ready for implementation
    on Nov 8, 2023
  2. ghost added
    needs-area-labelAn area label is needed to ensure this gets routed to the appropriate area owners
    on Nov 8, 2023
  3. ghost added
    untriagedNew issue has not been triaged by the area owner
    on Nov 8, 2023
  4. added and removed
    needs-area-labelAn area label is needed to ensure this gets routed to the appropriate area owners
    on Nov 9, 2023
  5. added this to the Future milestone on Nov 9, 2023
  6. ghost removed
    untriagedNew issue has not been triaged by the area owner
    on Nov 9, 2023
  7. ghost added
    in-prThere is an active PR which will close this issue when it is merged
    on Nov 11, 2023
  8. ghost removed
    in-prThere is an active PR which will close this issue when it is merged
    on Nov 11, 2023
  9. added a commit that references this issue on Nov 11, 2023
    f6eb869
  10. IDisposable commented on May 3, 2024

    @IDisposable
    ContributorAuthor

    Is this something we can add to the API review?

  11. tannergooding commented on May 9, 2024

    @tannergooding
    Member

    CC. @tarekgh, this is a relatively simple ask and I know I've had to redefine such constants myself in personal projects to help with various extension methods or other time related structs used (such as a Timestamp struct used to track frame timings in a game engine).

  12. 44 remaining items

  13. bartonjs commented on Jun 25, 2024

    @bartonjs
    Member

    Video

    Based on the domain (the TimeSpan type itself), the belief is that long is correct for everything except HoursPerDay (int).

    While that's not so nice for seconds * TimeSpan.MillisecondsPerSecond for passing to (e.g.) new CancellationTokenSource(int), it is more appropriate for working with the TimeSpan type itself.

    namespace System.Collections.Generic;
    
    public class TimeSpan: // elided details
    {
            // more elision
    
            public const long MicrosecondsPerMillisecond = TicksPerMillisecond / TicksPerMicrosecond; //           1,000
            public const long MicrosecondsPerSecond = TicksPerSecond / TicksPerMicrosecond;           //       1,000,000
            public const long MicrosecondsPerMinute = TicksPerMinute / TicksPerMicrosecond;           //      60,000,000
            public const long MicrosecondsPerHour = TicksPerHour / TicksPerMicrosecond;               //   3,600,000,000
            public const long MicrosecondsPerDay = TicksPerDay / TicksPerMicrosecond;                 //  86,400,000,000
    
            public const long MillisecondsPerSecond = TicksPerSecond / TicksPerMillisecond;           //           1,000
            public const long MillisecondsPerMinute = TicksPerMinute / TicksPerMillisecond;           //          60,000
            public const long MillisecondsPerHour = TicksPerHour / TicksPerMillisecond;               //       3,600,000
            public const long MillisecondsPerDay = TicksPerDay / TicksPerMillisecond;                 //      86,400,000
    
            public const long SecondsPerMinute = TicksPerMinute / TicksPerSecond;                     //              60
            public const long SecondsPerHour = TicksPerHour / TicksPerSecond;                         //           3,600
            public const long SecondsPerDay = TicksPerDay / TicksPerSecond;                           //          86,400
    
            public const long MinutesPerHour = TicksPerHour / TicksPerMinute;                         //              60
            public const long MinutesPerDay = TicksPerDay / TicksPerMinute;                           //           1,440
    
            public const int HoursPerDay = TicksPerDay / TicksPerHour;                               //              24
    }
  14. added
    api-approvedAPI was approved in API review, it can be implemented
    and removed
    blockingMarks issues that we want to fast track in order to unblock other important work
    api-ready-for-reviewAPI is ready for review, it is NOT ready for implementation
    on Jun 25, 2024
  15. IDisposable commented on Jun 25, 2024

    @IDisposable
    ContributorAuthor

    That makes perfect sense, let me know when/what the final should be and I will resubmit

  16. tannergooding commented on Jun 25, 2024

    @tannergooding
    Member

    You should feel free to submit a PR matching it at any time now, we're in agreement as to the handling here now.

  17. tarekgh commented on Jun 25, 2024

    @tarekgh
    Member

    @IDisposable

    let me know when/what the final should be and I will resubmit

    It is ready now so you can resurrect or create a new PR now.

  18. IDisposable commented on Jun 25, 2024

    @IDisposable
    ContributorAuthor

    Working on it... do we have a decision about the casting at call-site or modifying the parameters type as mentioned here?

    We had settled on casting at the call site, last I remember... and suspect we should go that route now.

  19. added a commit that references this issue on Jun 25, 2024
    e189d46
  20. added
    in-prThere is an active PR which will close this issue when it is merged
    on Jun 25, 2024
  21. IDisposable commented on Jun 25, 2024

    @IDisposable
    ContributorAuthor

    PR #103993 has hopefully final iteration :)

  22. locked and limited conversation to collaborators on Jul 27, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    api-approvedAPI was approved in API review, it can be implementedarea-System.DateTimein-prThere is an active PR which will close this issue when it is merged

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions