KEMBAR78
Add Microseconds and Nanoseconds to TimeStamp, DateTime, DateTimeOffset, and TimeOnly · Issue #23799 · dotnet/runtime · GitHub
Skip to content

Add Microseconds and Nanoseconds to TimeStamp, DateTime, DateTimeOffset, and TimeOnly #23799

@ChristopherHaws

Description

@ChristopherHaws

Currently, the value of a single Tick in .NET is 100ns, however there is no API surface for accessing microseconds or nanoseconds other than using Ticks to manually calculate these numbers. In order to allow for working with these higher precision times, I am proposing the following new APIs:

Approved API

namespace System {
    public struct DateTime {
        /// <exception cref="ArgumentOutOfRangeException">When microsecond is not between 0 and 999.</exception>
        public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond);
        /// <exception cref="ArgumentOutOfRangeException">When microsecond is not between 0 and 999.</exception>
        public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond, System.DateTimeKind kind);
        /// <exception cref="ArgumentOutOfRangeException">When microsecond is not between 0 and 999.</exception>
        public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond, System.Globalization.Calendar calendar);
        /// <returns>The microseconds component, expressed as a value between 0 and 999.</returns>
        public int Microsecond { get; }
        /// <returns>The nanoseconds component, expressed as a value between 0 and 900.</returns>
        public int Nanosecond { get; }
        /// <exception cref="ArgumentOutOfRangeException">The resulting DateTime is less than DateTime.MinValue or greater than DateTime.MaxValue.</exception>
        public DateTime AddMicroseconds(double value);
    }
    public struct DateTimeOffset {
        /// <exception cref="ArgumentOutOfRangeException">When microseconds is not between 0 and 999.</exception>
        public DateTimeOffset(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond, System.TimeSpan offset);
        /// <exception cref="ArgumentOutOfRangeException">When microseconds is not between 0 and 999.</exception>
        public DateTimeOffset(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond, System.TimeSpan offset, System.Globalization.Calendar calendar);
        /// <returns>The microseconds component, expressed as a value between 0 and 999.</returns>
        public int Microsecond { get; }
        /// <returns>The nanoseconds component, expressed as a value between 0 and 900.</returns>
        public int Nanosecond { get; }
        /// <exception cref="ArgumentOutOfRangeException">The resulting DateTimeOffset is less than DateTimeOffset.MinValue or greater than DateTimeOffset.MaxValue.</exception>
        public DateTimeOffset AddMicroseconds(double value);
    }
    public struct TimeSpan {
        public const long TicksPerMicrosecond = 10L;
        public const long NanosecondsPerTick = 100L;
        /// <exception cref="ArgumentOutOfRangeException">When microseconds is not between 0 and 999.</exception>
        public TimeSpan(int days, int hours, int minutes, int seconds, int milliseconds, int microseconds);
        /// <returns>The microseconds component, expressed as a value between 0 and 999.</returns>
        public int Microseconds { get; }
        /// <returns>The nanoseconds component, expressed as a value between 0 and 900.</returns>
        public int Nanoseconds { get; }
        /// <returns>The total number of microseconds represented by this instance.</returns>
        public double TotalMicroseconds { get; }
        /// <returns>The total number of nanoseconds represented by this instance.</returns>
        public double TotalNanoseconds { get; }
        /// <exception cref="OverflowException">When value is less than TimeSpan.MinValue or greater than TimeSpan.MaxValue.</exception>
        public static TimeSpan FromMicroseconds(double microseconds);
    }
    public struct TimeOnly {
        /// <param name="microsecond">The microsecond (0 through 999).</param>
        /// <exception cref="ArgumentOutOfRangeException">When microsecond is not between 0 and 999.</exception>
        public TimeOnly(int day, int hour, int minute, int second, int millisecond, int microsecond);
        /// <returns>The microsecond component of the time represented by this instant, expressed as a value between 0 and 999.</returns>
        public int Microsecond { get; }
        /// <returns>The nanosecond component of the time represented by this instant, expressed as a value between 0 and 900.</returns>
        public int Nanosecond { get; }
    }
}

Original API Proposal

namespace System {
    public struct DateTime {
        /// <exception cref="ArgumentOutOfRangeException">When microseconds is not between 0 and 999.</exception>
        public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int microseconds);
        /// <exception cref="ArgumentOutOfRangeException">When microseconds is not between 0 and 999.</exception>
        public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int microseconds, System.DateTimeKind kind);
        /// <exception cref="ArgumentOutOfRangeException">When microseconds is not between 0 and 999.</exception>
        public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int microseconds, System.Globalization.Calendar calendar);
        /// <returns>The microseconds component, expressed as a value between 0 and 999.</returns>
        public int Microsecond { get; }
        /// <returns>The nanoseconds component, expressed as a value between 0 and 900.</returns>
        public int Nanosecond { get; }
        /// <exception cref="ArgumentOutOfRangeException">The resulting DateTime is less than DateTime.MinValue or greater than DateTime.MaxValue.</exception>
        public DateTime AddMicroseconds(int microseconds);
    }
    public struct DateTimeOffset {
        /// <exception cref="ArgumentOutOfRangeException">When microseconds is not between 0 and 999.</exception>
        public DateTimeOffset(int year, int month, int day, int hour, int minute, int second, int millisecond, int microseconds, System.TimeSpan offset);
        /// <exception cref="ArgumentOutOfRangeException">When microseconds is not between 0 and 999.</exception>
        public DateTimeOffset(int year, int month, int day, int hour, int minute, int second, int millisecond, int microseconds, System.TimeSpan offset, System.Globalization.Calendar calendar);
        /// <returns>The microseconds component, expressed as a value between 0 and 999.</returns>
        public int Microsecond { get; }
        /// <returns>The nanoseconds component, expressed as a value between 0 and 900.</returns>
        public int Nanosecond { get; }
        /// <exception cref="ArgumentOutOfRangeException">The resulting DateTimeOffset is less than DateTimeOffset.MinValue or greater than DateTimeOffset.MaxValue.</exception>
        public DateTimeOffset AddMicroseconds(int microseconds);
    }
    public struct TimeSpan {
        public const long TicksPerMicrosecond = 10L;
        public const double TicksPerNanosecond = 0.01D;
        /// <exception cref="ArgumentOutOfRangeException">When microseconds is not between 0 and 999.</exception>
        public TimeSpan(int days, int hours, int minutes, int seconds, int milliseconds, int microseconds);
        /// <returns>The microseconds component, expressed as a value between 0 and 999.</returns>
        public int Microseconds { get; }
        /// <returns>The nanoseconds component, expressed as a value between 0 and 900.</returns>
        public int Nanoseconds { get; }
        /// <returns>The total number of microseconds represented by this instance.</returns>
        public double TotalMicroseconds { get; }
        /// <returns>The total number of nanoseconds represented by this instance.</returns>
        /// <exception cref="OverflowException">When value starts to approach TimeSpan.MinValue or TimeSpan.MaxValue.</exception>
        public double TotalNanoseconds { get; }
        /// <exception cref="OverflowException">When value is less than TimeSpan.MinValue or greater than TimeSpan.MaxValue.</exception>
        public static TimeSpan FromMicroseconds(double value);
    }
    public struct TimeOnly {
        /// <param name="microsecond">The microsecond (0 through 999).</param>
        /// <exception cref="ArgumentOutOfRangeException">When microsecond is not between 0 and 999.</exception>
        public TimeOnly(int day, int hour, int minute, int second, int millisecond, int microsecond);
        /// <returns>The microsecond component of the time represented by this instant, expressed as a value between 0 and 999.</returns>
        public int Microsecond { get; }
        /// <returns>The nanosecond component of the time represented by this instant, expressed as a value between 0 and 900.</returns>
        public int Nanosecond { get; }
    }
}

Example Usage

public class Samples
{
    public void DateTimeSamples()
    {
        new DateTime(0001, 01, 01, 00, 00, 00, 00, 999).Ticks;      // 9990
        DateTime.Parse("0001-01-01 00:00:00.0009990").Microsecond;  // 999
        DateTime.Parse("0001-01-01 00:00:00.0000009").Nanosecond;   // 900
        DateTime.Zero.AddMicroseconds(999).Ticks;                   // 9990
    }

    public void DateTimeOffsetSamples()
    {
        new DateTimeOffset(0001, 01, 01, 00, 00, 00, 00, 999, TimeSpan.FromHours(-7)).Ticks;        // 9990
        DateTimeOffset.Parse("0001-01-01 00:00:00.0009990 -7").Microsecond;                         // 999
        DateTimeOffset.Parse("0001-01-01 00:00:00.0000009 -7").Nanosecond;                          // 900
        new DateTimeOffset().AddMicroseconds(999).Ticks;                                            // 9990
    }

    public void TimeSpanSamples()
    {
        new TimeSpan(999 * TimeSpan.TicksPerMicrosecond).Ticks;            // 9990
        new TimeSpan((long)(900 * TimeSpan.TicksPerNanosecond)).Ticks;  // 9
        new TimeSpan(0001, 01, 01, 00, 00, 00, 00, 999).Ticks;          // 9990
        TimeSpan.Parse("0001-01-01 00:00:00.0009990").Microsecond;      // 999
        TimeSpan.Parse("0001-01-01 00:00:00.0000009").Nanosecond;       // 900
        TimeSpan.Zero.AddMicroseconds(999).Ticks;                       // 9990
    }
}

Notes

  • TimeSpan.TotalMicroseconds and TimeSpan.TotalNanoseconds return double still since that is what the other Total properties return. This could be long but I kept it as double for consistency.
  • TimeSpan.FromMicroseconds takes a double to keep the API consistent with the other From methods.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions