[API Proposal]: Extend the functionality of `TimeSpan` custom format strings

Open
#109,095 2 comments 4 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
5/5
Estimated time
Over a week
Newbie friendliness
25/100
Issue type
Feature
Clarity
Mostly clear
Activity status
Stale
Tech stack
csharp

Research direction

No source files, tests, or entry points are named. Start by reviewing the existing TimeSpan custom-format behavior and the proposal's examples; done would require an agreed API design and corresponding implementation and tests.

Written by the indexing model from the issue text.

Description

api-suggestion area-System.DateTime
Background and motivation

Custom TimeSpan format strings have limitations which limit their practicality. Notably, it is currently impossible to express any of the Standard TimeSpan format strings as a Custom TimeSpan format string, the biggest limitation being that there is currently no way to express the negative sign for negative TimeSpans, a fact that is explicitly called out on the docs page. The main shortcoming is that TimeSpan custom format strings do not support the section separator, unlike custom numeric format strings.

This limitation makes it difficult to build variations of the standard TimeSpan format strings. Instead, the user must resort to custom TimeSpan formatting code, which is cumbersome and incompatible with APIs that only accept format strings. For example, data annotations such as the DisplayFormatAttribute.

Additionally, there is no way to express the TimeSpan.Total* properties of the TimeSpan, such as TimeSpan.TotalHours, TimeSpan.TotalMinutes, or TimeSpan.TotalSeconds. This limits the flexibility of TimeSpan formatting since users do not always want the number of days to be shown (instead preferring total hours), or for simple use cases with small TimeSpan values, total seconds are often desirable.

These limitations are also motivating other issues such as https://github.com/dotnet/runtime/issues/88062 which suggests a new standard format specifier to workaround an issue that could be overcome with improved custom format string support.

API Proposal

I suggest adding additional custom format specifiers:

  • ;: The section separator, to allow defining sections with separate format strings for positive, negative, and zero TimeSpans. This would allow the user to easily dictate the behaviour of the positive and negative sign, and bring custom TimeSpan formats into parity with Custom numeric format strings.
  • H: The number of whole hours in the time interval. This differs from 'h' in that it represents the total number of hours and can therefore be > 24. It is equivalent to the absolute integer floor of TimeSpan.TotalHours.
  • M: The number of whole minutes in the time interval. This differs from 'm' in that it represents the total number of minutes and can therefore be > 60. It is equivalent to the absolute integer floor of TimeSpan.TotalMinutes.
  • S: The number of whole seconds in the time interval. This differs from 's' in that it represents the total number of seconds and can therefore be > 60. It is equivalent to the absolute integer floor of TimeSpan.TotalSeconds.

Additionally, leading zero variations of the total hours, total minutes, and total seconds specifiers should be available:

  • HH, HHH, HHHH, ... -> 00, 000, 0000, ...
  • MM, MMM, MMMM, ... -> 00, 000, 0000, ...
  • SS, SSS, SSSS, ... -> 00, 000, 0000, ...
API Usage
var exampleNegative = TimeSpan.FromHours(-27.12345);
exampleNegative.ToString("+H:mm:ss.fff;-H:mm:ss.fff;H:mm:ss.fff"); // -27:07:24.419
exampleNegative.ToString("+M:ss.fff;-M:ss.fff;M:ss.fff"); // -1627:22.680
exampleNegative.ToString("+S.fff;-S.fff;S.fff"); // -97644.419

var examplePositive = TimeSpan.FromHours(27.12345);
examplePositive.ToString("+H:mm:ss.fff;-H:mm:ss.fff;H:mm:ss.fff"); // +27:07:24.419
examplePositive.ToString("+M:ss.fff;-M:ss.fff;M:ss.fff"); // +1627:22.680
examplePositive.ToString("+S.fff;-S.fff;S.fff"); // +97644.419

var exampleZero = TimeSpan.Zero;
exampleZero.ToString("+H:mm:ss.fff;-H:mm:ss.fff;H:mm:ss.fff"); // 0:00:00.000
exampleZero.ToString("+M:ss.fff;-M:ss.fff;M:ss.fff"); // 0:00.000
exampleZero.ToString("+S.fff;-S.fff;S.fff"); // 0.000
Alternative Designs

A dedicated format specifier for the TimeSpan sign could be used, instead of the section separator. However, the section separator has the following advantages:

  • Consistency with the existing behaviour in custom numeric format strings.
  • Flexibility in prepending a '+' character for positive values.
  • Flexibility in formatting of the zero condition.

Additional format specifiers could be added for TimeSpan.TotalMilliseconds, TimeSpan.TotalMicroseconds, and TimeSpan.Nanoseconds:

  • A format specifier for TimeSpan.TotalMilliseconds has some obvious use cases, however it is unclear to me which letter should be used to represent it, since both 'f' and 'F' are already taken.

  • The usefulness of format specifiers for TimeSpan.TotalMicroseconds and TimeSpan.Nanoseconds is less clear to me, so I have omitted them.

Risks

Changing the way that custom format strings are interpreted for TimeSpan is potentially breaking, however I believe that any un-escaped/unquoted use of ;, H, M, S in a TimeSpan format string leads to the "Input string was not in a correct format." FormatException, so the impact should be minimal.

Dominant language
C#
Stars
18.3k
Forks
5.6k
PR merge metrics
PR metrics pending

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from dotnet/runtime

All issues in dotnet/runtime

Similar issues

More C# issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.