-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
API Proposal: Add OffsetMinutes to DateTimeOffset #52081
Comments
I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label. |
FWIW, if we did this, I'd want OffsetMinutes to be typed as |
Tagging subscribers to this area: @tannergooding Issue DetailsBackground and MotivationIn advanced Proposed APInamespace System
{
public readonly struct DateTimeOffset
{
// Existing field
private readonly short _offsetMinutes;
+ public bool IsUTC => _offsetMinutes == 0;
+ public short OffsetMinutes => _offsetMinutes;
}
} Usage ExamplesBefore public Value(DateTimeOffset value)
{
this = default;
TimeSpan offset = value.Offset;
if (offset.Ticks == 0)
{
// This is a UTC time
_union.Ticks = value.Ticks;
_object = TypeFlags.DateTimeOffset;
}
else if (PackedDateTimeOffset.TryCreate(value, offset, out var packed))
{
_union.PackedDateTimeOffset = packed;
_object = TypeFlags.PackedDateTimeOffset;
}
else
{
_object = value;
}
} After public Value(DateTimeOffset value)
{
this = default;
if (value.IsUTC)
{
// This is a UTC time
_union.Ticks = value.Ticks;
_object = TypeFlags.DateTimeOffset;
}
else if (PackedDateTimeOffset.TryCreate(value, value.OffsetMinutes, out var packed))
{
_union.PackedDateTimeOffset = packed;
_object = TypeFlags.PackedDateTimeOffset;
}
else
{
_object = value;
}
} Related to this, cc: @KrzysztofCwalina, @tarekgh, @lonitra
|
I think if we expose |
Also, why thinking I am not seeing much value exposing |
It is about performance, not functionality. Creating a public TimeSpan(int hours, int minutes, int seconds)
{
_ticks = TimeToTicks(hours, minutes, seconds);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static long TimeToTicks(int hour, int minute, int second)
{
// totalSeconds is bounded by 2^31 * 2^12 + 2^31 * 2^8 + 2^31,
// which is less than 2^44, meaning we won't overflow totalSeconds.
long totalSeconds = (long)hour * 3600 + (long)minute * 60 + (long)second;
if (totalSeconds > MaxSeconds || totalSeconds < MinSeconds)
ThrowHelper.ThrowArgumentOutOfRange_TimeSpanTooLong();
return totalSeconds * TicksPerSecond;
} That is a lot of work for checking |
This can be optimized in the implementation by creating |
That is better, but still suboptimal, as I'm still getting out the minutes again. |
... but As an aside |
Accessing the minutes from the offset is not that expensive I guess |
FWIW, here's the current codegen for C.TotalMinutes(System.DateTimeOffset)
L0000: sub rsp, 0x28
L0004: vzeroupper
L0007: movsx rax, word ptr [rcx]
L000b: movsxd rax, eax
L000e: imul rax, 0x3c
L0012: mov rdx, 0xd6bf94d5e5
L001c: cmp rax, rdx
L001f: jg short L0051
L0021: mov rdx, 0xffffff29406b2a1b
L002b: cmp rax, rdx
L002e: jl short L0051
L0030: imul rax, 0x989680
L0037: vxorps xmm0, xmm0, xmm0
L003b: vcvtsi2sd xmm0, xmm0, rax
L0040: vdivsd xmm0, xmm0, [C.TotalMinutes(System.DateTimeOffset)]
L0048: vcvttsd2si eax, xmm0
L004c: add rsp, 0x28
L0050: ret
L0051: call Edit: Had to fix my codegen above since I was calling |
The issue I have is not getting it out multiple times, it is getting it out for many |
@JeremyKuhne got it. if you think exposing it would make noticeable difference then I am ok. I assume we'll not need |
Yes, it is just a convenience if the minutes are exposed so it isn't critical. |
And another thing - minutes are technically not sufficient. Historically there have been timezones with second offsets, usually as the first offset from the zone being set from local solar noon to a more even division. |
Currently we don't support that. we always round to the minutes. |
Or throw an exception on construction. Is this something that's going to be affected by your changes to |
@Clockwork-Muse we can't throw because mostly the TZ data is read from the system when running on Linux. That means if we throw, anyone enumerating the time zones on the system will get exception. Anyway, this is a corner case because it happens with a couple of time zones in the historical old dates which not really a concern. |
@tarekgh - |
@Clockwork-Muse the doc is correct we throw if calling the public constructor. what I meant when I said we don't throw, is when we internally reading the TZ data and encounter fractional minutes, we don't throw. we just round the minutes before using it with the DateTimeOffset. |
I think this raises a question about naming: should the property be called If this confused anybody, they would pretty quickly find there is no |
Sounds like a better term to me. |
@tarekgh, any other feedback here or is this in a shape that could be marked |
The API (after renaming it to |
LGTM also. |
Yes. :) |
I have updated the API name in the original proposal. Should it return |
I'd say int is the better choice given @GrabYourPitchforks comment above. |
Ok, I have updated the proposal and marked it as ready for review. |
namespace System
{
public readonly partial struct DateTimeOffset
{
public int TotalOffsetMinutes => _offsetMinutes;
}
} |
Co-authored-by: Eirik Tsarpalis <[email protected]> Fixes #52081
Background and Motivation
In advanced
DateTimeOffset
scenarios, getting fast access to whether or not theDateTimeOffset
has an offset and what it is in minutes is very useful.Proposed API
Usage Examples
Before
After
Related to this,
DateTimeOffset.Ticks
could be calling an internal constructor to avoid the checks inDateTime
. That isn't public surface area, but worth mentioning.cc: @KrzysztofCwalina, @tarekgh, @lonitra
The text was updated successfully, but these errors were encountered: