Странное поведение при преобразовании времени Unix в DateTime в .NET

У меня проблема с итальянским летним временем. Преобразование unix 687481200000 в .NET выглядит непредсказуемо (конечно, я что-то упускаю).

Как вы можете видеть, набрав в консоли браузера:

console.info(new Date(687481200000).toLocaleString('it-IT', {timeZone: 'Europe/Rome', timeZoneName: 'short'}));

значение временной метки unix 687481200000 должно возвращать итальянскую дату 1991-10-15 00:00:00 (в Италии дата вводится 15.10.1991, но мы всегда говорим о 15 октября).

Теперь я хочу, чтобы вы взглянули на этот фрагмент

https://dotnetfiddle.net/jKShoE

Когда я запускаю его в браузере с помощью компилятора: .NET 6, я получаю

Данные e ora Italiane: 15.10.1991 00:00:00

изменение компилятора на .NET 4.7.2

https://dotnetfiddle.net/GV7FYD

я получил

Данные e ora Italiane: 15.10.1991, 1:00:00

И если я скопирую этот код в новый проект .NET 6 в Visual Studio 2022

Данные e ora Italiane: 15.10.1991 01:00:00

Кто-нибудь может объяснить мне, почему? Есть ли способ получить правильный час 00:00:00 без использования внешних библиотек (например, NodaTime)?

Ps: работает на Windows 10.0.19045 сборка 19045, итальянская локализация

Обновлено: вот код из скрипки.

using System;

public class Program
{
    public static void Main()
    {
        long unixMilliseconds = 687481200000;

        DateTimeOffset dateTimeOffset = DateTimeOffset.FromUnixTimeMilliseconds(unixMilliseconds);

        TimeZoneInfo italianTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Central European Standard Time");

        DateTimeOffset italianDateTimeOffset = TimeZoneInfo.ConvertTime(dateTimeOffset, italianTimeZone);

        DateTime italianDateTime = italianDateTimeOffset.DateTime;

        Console.WriteLine("Data e ora italiane: " + italianDateTime);
    }
}

Редактировать 2: пересмотренный код

Мне пришлось копать глубже и найти больше ответов, когда я искал TimeZoneInfo.AdjustmentRule в stackoverflow.

using System;
using System.Linq;
public class Program
{
    public static void Main()
    {
        // A representation for UTC 14/10/1991 23:00:00 +00:00  
        long unixMilliseconds = 687481200000;
        // Looking at epochconverter.com 
        // UTC 14/10/1991 23:00:00 +00:00  
        // corresponds to italian 15 october 1991 00:00:00 GMT+01:00

        // Store the UTC time in dateTimeOffset
        DateTimeOffset dateTimeOffset = DateTimeOffset.FromUnixTimeMilliseconds(unixMilliseconds);
        Console.WriteLine(dateTimeOffset);
        // .NET 6:       10/14/1991 23:00:00 +00:00
        // .NET 4.7.2:   10/14/1991 11:00:00 PM +00:00


        // Applying the following pattern:
        // https://learn.microsoft.com/en-us/dotnet/api/system.datetimeoffset.fromunixtimemilliseconds?view=net-8.0#remarks

        // I was wrong, my current timezone is "W. Europe Standard Time" but the nature of the problem remains unchanged.
        // I retrieved my TimeZoneInfo this way:
        TimeZoneInfo italianTimeZone = TimeZoneInfo.Local;
        Console.WriteLine("TimeZoneInfo.Local.Id: " + TimeZoneInfo.Local.Id); // I got here W. Europe Standard Time

        // so retrieving it by ID to make it work on fiddler
        italianTimeZone = TimeZoneInfo.FindSystemTimeZoneById("W. Europe Standard Time");
        Console.WriteLine("TimeZoneInfo Id: " + italianTimeZone.Id); // Once again to check

        Console.WriteLine("TimeZoneInfo: " + italianTimeZone.ToString());
        // .NET 6:       (UTC+01:00) Central European Time (Berlin)
        // .NET 4.7.2:   (UTC+01:00) Amsterdam, Berlino, Berna, Roma, Stoccolma, Vienna


        Console.WriteLine("Daylight : " + italianTimeZone.IsDaylightSavingTime(dateTimeOffset));
        // fiddler .NET 6 :   False
        // fiddler .NET 4.7 : True
        // "my PC" .NET 6 :   True

        // Now look at this:
        Console.WriteLine("Rules count: " + italianTimeZone.GetAdjustmentRules().Length);
        // fiddler .NET 6 :   79
        // fiddler .NET 4.7 : 1
        // "my PC" .NET 6 :   1

        // Only in the fiddler .NET 6 the full set of rules is available.
        
        // BONUS
        string iso8601 = "1991-10-14T23:00:00Z";
        long myDate = DateTimeOffset.Parse(iso8601).ToUnixTimeMilliseconds();
        Console.WriteLine("My date: " + myDate); // My date: 687481200000 on every system
    }
}

Мне до сих пор неясно, как и кто управляет базой данных часовых поясов, будь то операционная система или платформа .NET, но мне кажется, что эта конфигурация достаточно сложна, чтобы заставить меня выбрать использование NodaTime (я не знаю, Я не хочу вмешиваться в конфигурацию машины, чтобы получить достоверные результаты).

Вы можете увидеть перечень правил дневного света здесь: Показаны времена перехода

Пожалуйста, включите код в сам вопрос, а не просто ссылку на dotnetfiddle.

Sweeper 08.04.2024 09:23

Почему вы вообще используете временную метку Unix? И Javascript, и .NET имеют правильные типы дат и поддерживают единственное стандартное представление — ISO8601. Временные метки Unix также всегда указаны в формате UTC. Если вы обнаружите разницу в местном времени, это связано с тем, что было использовано неправильное преобразование часового пояса.

Panagiotis Kanavos 08.04.2024 09:32
I'm missing something да. Проблема в коде JavaScript. Как показано в документации конструктора даты , временная метка — это UTC the number of milliseconds since midnight at the beginning of January 1, 1970, UTC, но конструктор Date рассматривает ее как локальную. The parameter values are all evaluated against the local time zone, rather than UTC. Date.UTC() accepts similar parameters but interprets the components as UTC and returns a timestamp.
Panagiotis Kanavos 08.04.2024 09:39
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
4
3
73
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Дело в светлом времени:

В .Net 6: https://dotnetfiddle.net/qP1icN

Daylight : False
Data e ora italiane: 10/15/1991 00:00:00

В .Net 4.7.2: https://dotnetfiddle.net/7eBu3y

Daylight : True
Data e ora italiane: 10/15/1991 1:00:00 AM

Людям платят за решение подобных проблем по всему миру...

Не совсем - код JS с самого начала неверен, поскольку временная метка UTC обрабатывается так, как если бы она была локальной.

Panagiotis Kanavos 08.04.2024 09:40

Действительно, но почему один и тот же код C# дает два разных результата в зависимости от версии .Net?

gobes 08.04.2024 09:47

Потому что код .NET тоже неправильный и ошибочно предполагает, что временная метка является локальной. Для переключения смещений вы используете DateTimeOffset.ToOffset, а не арифметику дат. В вашей скрипке 4.7.2 попробуйте Console.WriteLine(dateTimeOffset); и посмотрите, как она напечатана 10/14/1991 11:00:00 PM +00:00. После этого все остальное не так People get paid to handle this kind of problem worldwide... нет, старшие разработчики раздражаются, когда люди повторно вводят те же ошибки, которые они исправили в прошлом году и позапрошлом году.

Panagiotis Kanavos 08.04.2024 09:53

Так это связано с локализацией dotnetfiddle? Я не понимаю, почему существует разница между версиями .Net.

gobes 08.04.2024 10:06

Другие вопросы по теме