DateTimeからDateTimeOffsetへの暗黙的型変換はやめてくれ

DateTimeからDateTimeOffsetへの暗黙的型変換はやめてくれ
DateTimeOffset? configStartDate = null;  
var jst = TimeZoneInfo.FindSystemTimeZoneById("Tokyo Standard Time");  
var jstNow = TimeZoneInfo.ConvertTime(DateTimeOffset.Now, jst);  
var startDate = configStartDate ?? jstNow.Date; // 👈  
Console.WriteLine(startDate.ToString("o"));  

// configStartDateのTZが不定だから表示用にJSTに矯正しとこ
var jstStartDate = TimeZoneInfo.ConvertTime(startDate, jst);  
Console.WriteLine(jstStartDate.ToString("o"));

何かの開始日を生成するのに、登録された値がなかったら今日からってコードってよく書くと思う。

実行結果

JST

2025-06-22T00:00:00.0000000+09:00  
2025-06-22T00:00:00.0000000+09:00

UTC

2025-06-22T00:00:00.0000000+00:00  
2025-06-22T09:00:00.0000000+09:00  #🤯

やっちまったなあ!見事にずれてまっせ。

根源

バグは既に示してるけど、DateTimeOffset型のjstNowを日付欲しさにDateしてるところ。ここでDateTimeOffsetからDateTime型になっていてオフセットが落ちてる。 自分のPCはJSTだから全然気づかないけどデプロイしてから発覚するやつ。 DateTimeからDateTimeOffsetへの暗黙的型変換にはローカルタイムゾーンが使われると思うけど、dotnetのプログラム内で参照するタイムゾーンを変更する方法がない(知らないだけかも)からUTCにしたかったらマシンの設定を変更しなくちゃいけないし大変。 8でTimeProviderとか追加されたけど、これは暗黙的型変換で起きてる問題だから出る幕ない。 つまるところ、DateTimeOffsetにDateTimeをオフセットしていなしに代入できるのがよくないんじゃない? RoslynAnalyzer書けばいいけど、面倒だから、コンパイルオプションとか警告オプションを標準で出せるようになって欲しいと思う今日この頃です。

一応Issueはあって、Analayzerでやる形ですすんでいるみたい。2022年で止まってるけど。

Feature proposal: remove implicit construction of DateTimeOffset from DateTime · Issue #32954 · dotnet/runtime

Add analyzer for DateTimeOffset implicit operator with DateTime. · Issue #75400 · dotnet/runtime

おまけ

var jst = TimeZoneInfo.FindSystemTimeZoneById("Tokyo Standard Time");  
var jstNow = TimeZoneInfo.ConvertTime(DateTimeOffset.Now, jst);  
var jstDate = new DateTimeOffset(jstNow.Date, jstNow.Offset); // OffsetつけなおしてDateTimeOffsetに戻そうね  
var startDate = configStartDate ?? jstDate;  
Console.WriteLine(startDate.ToString("o"));  
  
var jstStartDate = TimeZoneInfo.ConvertTime(startDate, jst);  
Console.WriteLine(jstStartDate.ToString("o"));