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

DateTimeAxis.ToDateTime doesn't behave as intended in .NET 8 #2061

Open
VisualMelon opened this issue Feb 13, 2024 · 5 comments
Open

DateTimeAxis.ToDateTime doesn't behave as intended in .NET 8 #2061

VisualMelon opened this issue Feb 13, 2024 · 5 comments

Comments

@VisualMelon
Copy link
Contributor

A DateTimeAxis with a step that required a reciprocating representation (e.g. 4hours = 1/6) results in unintended tick values.

image

I can't reproduce this with .NET Framework or .NET 6; observed it with .NET 7 also.

From the immediate window (across two debug sessions, one with .NET 7 and the other net framework 462):

DateTimeAxis.ToDateTime(45334.166666666664).ToString()
"12/02/2024 03:59:59"
DateTimeAxis.ToDateTime(45334.166666666664).ToString()
"12/02/2024 04:00:00"

I assume this is because of https://learn.microsoft.com/en-us/dotnet/core/compatibility/core-libraries/7.0/datetime-add-precision where millisecond rounding was removed.

I don't have a suggestion for a fix this minute, but will try to work out a workaround soon for typical use-cases (probably just putting the implicit rounding back in).

@alcre8or
Copy link

I am also experiencing the same problem.

test

@VisualMelon
Copy link
Contributor Author

VisualMelon commented Feb 25, 2024

Anyone using .NET 7 or greater will be seeing this. There is no trivial work around, but you can use something like this which rounds to the given precision before formatting (bypasses a load of internal logic which depends on the behaviour of DateTimeAxis.ToDouble and DateTimeAxis.ToDateTime which have issues)

    public class DateTimeAxisWorkaround : DateTimeAxis
    {
        public TimeSpan PrecisionLimit { get; set; } = TimeSpan.FromMilliseconds(1);

        protected override string FormatValueOverride(double x)
        {
            DateTime value = (DateTime)GetValue(x);
            return value.ToString(this.ActualStringFormat);
        }

        public override object GetValue(double x)
        {
            DateTime unrounded = (DateTime)base.GetValue(x);
            var remainder = unrounded.Ticks % PrecisionLimit.Ticks;
            if (remainder < PrecisionLimit.Ticks / 2)
                return unrounded.AddTicks(-remainder);
            else
                return unrounded.AddTicks(PrecisionLimit.Ticks - remainder);
        }
    }

This has not been properly tested; the rounding should mean it gives back the same sort of precision the axis used to have anyway.

@alcre8or
Copy link

@VisualMelon , thank you. It works well.

@chk-mk
Copy link

chk-mk commented May 7, 2024

The core issue as I understand it is that no exact conversions exist between fractional days-since-1900 and DateTime's 100-nanosecond ticks. The old millisecond rounding in .NET could be pretty limiting but it masked this problem.

With a base date of year ~1900, dates around the present have a ~45000-day offset. Around 45000, doubles have a resolution of very roughly 0.00000000001, which sounds great, but that's in days still. In seconds that's 864 nanoseconds, or ~9 ticks. So you can easily end up a few ticks below what you wanted after converting back and forth. OP's example is 638433071999999997 ticks.

I agree with rounding the ticks, there's really no other clear fix, and a configurable resolution would be very convenient - I've been using a custom axis that rounds to microseconds in ToDateTime (actually I think microsecond rounding is still buggy, it needed to be at least 2us), but some users may want the old millisecond behaviour.

@VisualMelon
Copy link
Contributor Author

I concur: configurable rounding defaulting to the old behaviour is probably the best route.

Hopefully I can find time to write a proper fix sometime, then we can release a new minor version of some sort.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants