-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
DateTimeOffset.ToLocalTime()
produces spurious offsets when time is before 1847
#11718
Comments
So if I understand correctly, you're asking for DateTimeOffset.MinValue to have a "floor" at |
I suppose so. I haven't checked explicitly, but I guess (or hope) it would be a value less than Thanks, |
I also see the same behaviour on macOS with dotnet 2.1.402 with the system timezone set to Europe/London |
@tarekgh thoughts ? |
CC @eerhardt dumping the zone data I am seeing the zone data showing a minute and 15 seconds offset as we are reporting it. look at year 1847 data and you can see this one minute offset
|
Looks our calculation if off by 15 seconds. we may need to look deeper why this is happening. here is some more details: We are using the date/time in Utc
which will have offset -75 seconds from the Utc. converting the date to local we are getting |
The comment above it says why.
It is documented on our public API that offsets cannot be fractions of minutes. This is for serialization purposes with things like SQL Server, XML, etc. See also this check: |
I saw the comment but was not getting why we are not allowing the fraction. your serialization comment kind clarified why. looking at the ISO date format, it allows hh:mm only and not having a seconds. or serializing the dates with the ISO formats is not going to work anyway. So now it make sense. by that, I am closing this issue with mentioning we have limitation in our TimeZoneInfo not supporting offsets with minute fraction. This will happen only in the historical zones like the one mentioned in this issue when using years before 1848 |
please reconsider the closing of this issue, this is causing problems with: DateTimeOffset.MinValue.ToLocalTime() whilst i’m sure there aren’t many dates in real world use that get used before 1847, i expect this is used frequently. at least it is so for us..! |
Could you please tell more what is exactly the problem you are facing with DateTimeOffset.MinValue.ToLocalTime()? I mean what is wrong getting 01/01/0001 00:00:00 -00:01? which still looks right I guess. I am reopening the issue till we know exactly what is the ask. |
i’ll provide more detail tomorrow! thank you for reconsidering this. |
@dancrn any news? |
@dancrn closing this one, but feel free to respond back when having any more info. |
@tarekgh sorry for taking forever to respond - i'll give you more information now though. ok, to recap, the issue is being exposed when we have a default-valued // given an input date
string dateTimeString = '0001-01-01 00:00:00.000000+00:00';
// parse it into a DateTimeOffset
DateTimeOffset.TryParse(dateTimeString, out DateTimeOffset parsedDateTimeOffset);
// a lot of libraries have inexplicably decided to return times with the offset of the
// local system, losing the supplied offset from the input..
var localTime = parsedDateTimeOffset.ToLocalTime()
// this call produces the spurious offset - it will return '0001-01-01 00:00:00.000000-00:01'
return parsedDateTimeOffset.ToUniversalTime(); whether or not these libraries should be doing this conversion (it is our strong opinion that they should not be doing this - as it loses information that we've been supplied from our API endpoints, and other reasons), is left as an exercise to the reader. regardless, both i've created a set of tests, which seem to suggest (on macOS 10.14.2, at least), that a lot of timezones exhibit this behavior - 132 time zones fail this test whereas 189 do not. i've tried to be clear and explicit with what i'm doing with these tests, and they should work on a machine in any locale. all it requires is the appropriate nUnit packages. using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
namespace DateTimeTests
{
public class RoundTripTests
{
public static IEnumerable<TimeZoneInfo> GetTimeZones()
=> TimeZoneInfo.GetSystemTimeZones();
[Test, TestCaseSource(nameof(GetTimeZones))]
public void DateTime_RoundTrip_DoesNotModifyValue(TimeZoneInfo timeZone)
{
// starting backward from the year 2000, down to year 0,
for (int i = 2000; i > 0; i--)
{
//create a date in that year, of kind Utc
DateTime initialTime = DateTime.SpecifyKind(new DateTime(i, 1, 1, 0, 0, 0), DateTimeKind.Utc);
//convert it to a local time
DateTime localTime = TimeZoneInfo.ConvertTimeFromUtc(initialTime, timeZone);
//now convert it back to a Utc time
DateTime roundTrippedTime = TimeZoneInfo.ConvertTimeToUtc(localTime, timeZone);
//this should be lossless
Assert.That(roundTrippedTime, Is.EqualTo(initialTime));
}
}
[Test]
public void DateTimeOffset_RoundTrip_DoesNotModifyValue()
{
Assert.That(
GetBadlyBehavedTimeZonesStrings(),
Does.Contain(TimeZoneInfo.Local.StandardName),
"It would appear as though this system will not be affected by this issue."
);
// do the same thing as above, but with a DateTimeOffset
DateTimeOffset initialTime = DateTimeOffset.MinValue;
DateTimeOffset localTime = initialTime.ToLocalTime();
DateTimeOffset utcTime = localTime.ToUniversalTime();
Assert.That(utcTime, Is.EqualTo(initialTime));
}
//gets the set of id strings of timezones that appear to have this issue
private static ISet<string> GetBadlyBehavedTimeZonesStrings()
=> GetTimeZones().Where(TimeZoneIsBadlyBehaved).Select(zone => zone.StandardName).ToHashSet();
//this essentially runs the same code as the first test
private static bool TimeZoneIsBadlyBehaved(TimeZoneInfo zone)
{
DateTime time = DateTime.SpecifyKind(DateTime.MinValue, DateTimeKind.Utc);
return time != TimeZoneInfo.ConvertTimeToUtc(TimeZoneInfo.ConvertTimeFromUtc(time, zone), zone);
}
}
} if there is any other information i can supply, please let me know. again, sorry for being so slow with this.. |
@dancrn thanks for the info. Using '0001-01-01 00:00:00.000000+00:00' as the default is not good idea anyway because the conversion to local for sure will not work. I mean you'll not be able to round trip that with all time zones. I would suggest you use some other defaults (e.g. UnixEpoch). sorry, we have some limitation which would be difficult to change. |
thanks for the reply! we're not trying to round trip this value - it's an odd choice of behavior by libraries that we cannot control :( for various technical reasons, we need to be able to support this value of to leave this as it is could have some real impact:
|
To understand more about your scenario: the date "0001-01-01 00:00:00+00", who originally instantiate this date? is it you or this is something can be passed to you from outside data? looks this date is not really have any meaning more than just default. would it be ok to check if you have such date and at that time always use UTC tz? |
we can consume a we already enforce that it is for the UTC time zone. libraries that we use in our code base perform this round tripping (again, i don't know why they do this) and this is where the issue becomes apparent for us. |
Hi, I'm dan.
I use dotnet at work. Recently, I've come across this.. unusual bug, regarding time zone offsets with the
DateTimeOffset
struct.Description
I have a code sample;
When I run this on my machine, I get this output:
Note that, after calling
.ToLocalTime()
, a spurious offset of -1 minute has been added to the offset time span. More generally, this is an issue because:After updating some libraries we use, we've found that they will call
.ToLocalTime()
(for some reason), and this returned value, as far as our code is concerned, is a non-UTC time zone, in a place where we're really not expecting this to happen.After a bit of investigation, I've found out that the UK switched from LMT (I did not know this was a time zone before now!) to GMT on
01/12/1847
, and that seems to coincide with the issue I'm seeing - I'm certain that is not a pure coincidence :) However, I'm at a total loss of how to proceed with this - short of changing my timezone settings..Reproduction
Now, this is the tricky part. Only I see this bug, and we deploy to AWS, on linux (Docker & EBS, etc.).
I'm running Ubuntu 18.04, and i'm running dotnet 2.1.302. My Locale is set to
en_GB.UTF-8
, with a timezone setting ofEurope/London
.If there's any more information I can supply, please let me know.
The text was updated successfully, but these errors were encountered: