web-dev-qa-db-ja.com

日付文字列を特定のタイムゾーンに解析します(夏時間をサポート)

さて、私はここ数週間非常に一生懸命働いてきました、そして私は小さな問題に遭遇しました。私の心は今のところ完全に仕事に向いているとは思わない:)だから私はいくつかのヒント/助けが必要です!非常に単純な可能性がありますが、私の頭はまだクリックしていません。

ユーザーはAESTに日付と時刻を入力します。アプリケーションセットの「デフォルト」タイムゾーン(変更が必要な場合があるため)もあり、現在は「オーストラリア東部標準時」に設定されています。

したがって、米国のサーバーには、タイムゾーンのないユーザー文字列と定義済みのシステムタイムゾーンがあります(したがって、ローカルは一致せず、変更または使用できません)

ここで必要なのは、「タイムゾーンXを使用してこのユーザーが入力した文字列を解析する」と言う方法です。日付が夏時間の内外にある可能性があるため、オフセットとして+10または+11を入力することはできません。はい、同じタイムゾーンでも+10と+11の間で変更されます。

現在のAEST時刻もDSTの内外にある可能性があるため、UTC日付を現在のAEST時刻に変換して「zzz」文字列を取得し、入力したものの日付が1時間ずれているため、それを添付することはできません。現在のDST設定の。

今のところ、コードは実際にはそれを実行します。

TimeZoneInfo ConvTo = TimeZoneInfo.FindSystemTimeZoneById(ConfigurationManager.AppSettings["DefaultTimeZone"]);
DateTimeOffset getDate = TimeZoneInfo.ConvertTime(DateTimeOffset.UtcNow, ConvTo);
string TimeZoneId = " " + getDate.ToString("zzz");
DateTimeOffset cvStartDate = DateTimeOffset.MinValue; DateTimeOffset.TryParse(StartDate + TimeZoneId, out cvStartDate);

次に、日付がまだ== DateTimeOffset.MinValueであるかどうかを確認するか、UTCに変換してデータベースに追加することにより、日付が無効かどうかを確認します。表示されると、AESTに変換されます。ただし、一部の日付は1時間ずれており、その他の日付は(予想どおり)完璧です:)

これを解決するための最もエレガントな方法は何ですか?

編集:

問題を説明するのを助けるために、私はいくつかのテストコードをWindowsテストアプリケーションとして書きました:

// User entered date
string EnteredDate = "2011/01/01 10:00:00 AM";

// Get the timezone we want to use
TimeZoneInfo myTimeZone = TimeZoneInfo.FindSystemTimeZoneById("AUS Eastern Standard Time");

// Find the timezone string of the selected timezone to parse the user string
// This is the part that is incorrect and what i need help with.
DateTimeOffset getDate = TimeZoneInfo.ConvertTime(DateTimeOffset.UtcNow, myTimeZone);
string TimeZoneId = " " + getDate.ToString("zzz");

// Parse the string into the date object
DateTimeOffset cvEnteredDate = DateTimeOffset.MinValue; DateTimeOffset.TryParse(EnteredDate + TimeZoneId, out cvEnteredDate);

// Display
textBox1.Text += "Parsed: " + cvEnteredDate.ToString("yyyy/MM/dd HH:mm:ss zzz") + Environment.NewLine;

// Convert to UTC and display
cvEnteredDate = cvEnteredDate.ToUniversalTime();
textBox1.Text += "UTC: " + cvEnteredDate.ToString("yyyy/MM/dd HH:mm:ss zzz") + Environment.NewLine;

// Convert back to AEST and display
cvEnteredDate = TimeZoneInfo.ConvertTime(cvEnteredDate, myTimeZone);
textBox1.Text += "Changed Back: " + cvEnteredDate.ToString("yyyy/MM/dd HH:mm:ss zzz") + Environment.NewLine;

これの出力は何ですか?

解析済み:2011/01/01 10:00:00 +10:00 
 UTC:2011/01/01 00:00:00 +00:00 
元に戻しました: 2011/01/01 11:00:00 +11:00 

時間は1時間ずれており、オフセットが異なることに注意してください。さらに、入力した日付を次のように変更した場合はどうなりますか。

string EnteredDate = "2011/04/20 10:00:00 AM";

我々が得る:

解析済み:2011/04/20 10:00:00 +10:00 
 UTC:2011/04/20 00:00:00 +00:00 
元に戻しました: 2011/04/20 10:00:00 +10:00 

同じコードを使用して、入力した日付を変えるだけで、これは完全に問題ありません。

これは、現在のDST設定と入力された日付のDST設定が異なるために発生します。これは、私が解決策を求めているものです:)

鶏が先か卵が先かという問題のように考えてください。入力した文字列を解析する前に正しいタイムゾーンデータが必要です。これは、文字列を解析した後にのみ取得できます(したがって、複雑な解決策になります)

または、myTimeZoneオブジェクトを使用して文字列を解析するために.NETが必要です。これにより、文字列をそれ自体に設定する方法がわかりますが、これを実行する関数が表示されません。allすでに解析され、設定されています。 datetimeまたはdatetimeoffsetオブジェクト

だから私は他の人がしたかもしれないエレガントな解決策を探していますか?確かにこれに気付いたのは私だけではありませんか?

EDIT2:

さて、私が思う問題を解決する「動作する」関数を作成しました。これが例です(c#Windowsアプリにテキストボックスを追加し、以下のコードを使用して自分自身をテストします)。

private void Form1_Load(object sender, EventArgs e)
{
    TimeZoneInfo myTimeZone = TimeZoneInfo.FindSystemTimeZoneById("AUS Eastern Standard Time");

    DateTimeOffset get1Date = ReadStringWithTimeZone("2011/01/01 10:00:00 AM", myTimeZone);
    textBox1.Text += "Read1: " + get1Date.ToString("yyyy/MM/dd HH:mm:ss zzz") + Environment.NewLine;
    get1Date = get1Date.ToUniversalTime();
    textBox1.Text += "Read1 - UTC: " + get1Date.ToString("yyyy/MM/dd HH:mm:ss zzz") + Environment.NewLine;
    get1Date = TimeZoneInfo.ConvertTime(get1Date, myTimeZone);
    textBox1.Text += "Changed Back: " + get1Date.ToString("yyyy/MM/dd HH:mm:ss zzz") + Environment.NewLine + Environment.NewLine;

    DateTimeOffset get2Date = ReadStringWithTimeZone("2011/04/20 10:00:00 AM", myTimeZone);
    textBox1.Text += "Read2: " + get2Date.ToString("yyyy/MM/dd HH:mm:ss zzz") + Environment.NewLine;
    get2Date = get2Date.ToUniversalTime();
    textBox1.Text += "Read2 - UTC: " + get2Date.ToString("yyyy/MM/dd HH:mm:ss zzz") + Environment.NewLine;
    get2Date = TimeZoneInfo.ConvertTime(get2Date, myTimeZone);
    textBox1.Text += "Changed Back: " + get2Date.ToString("yyyy/MM/dd HH:mm:ss zzz") + Environment.NewLine + Environment.NewLine;
}

public DateTimeOffset ReadStringWithTimeZone(string EnteredDate, TimeZoneInfo tzi)
{
    DateTimeOffset cvUTCToTZI = TimeZoneInfo.ConvertTime(DateTimeOffset.UtcNow, tzi);
    DateTimeOffset cvParsedDate = DateTimeOffset.MinValue; DateTimeOffset.TryParse(EnteredDate + " " + cvUTCToTZI.ToString("zzz"), out cvParsedDate);
    if (tzi.SupportsDaylightSavingTime)
    {
        TimeSpan getDiff = tzi.GetUtcOffset(cvParsedDate);
        string MakeFinalOffset = (getDiff.Hours < 0 ? "-" : "+") + (getDiff.Hours > 9 ? "" : "0") + getDiff.Hours + ":" + (getDiff.Minutes > 9 ? "" : "0") + getDiff.Minutes;
        textBox1.Text += "Diff: " + MakeFinalOffset + Environment.NewLine;
        DateTimeOffset.TryParse(EnteredDate + " " + MakeFinalOffset, out cvParsedDate);
        return cvParsedDate;
    }
    else
    {
        return cvParsedDate;
    }
}

そして出力:

差分:+11:00 
 Read1:2011/01/01 10:00:00 +11:00 
 Read1-UTC:2010/12/31 23:00: 00 +00:00 
元に戻しました:2011/01/01 10:00:00 +11:00 
 
差分:+10:00 
 Read2: 2011/04/20 10:00:00 +10:00 
 Read2-UTC:2011/04/20 00:00:00 +00:00 
元に戻しました:2011/04/20 10:00:00 +10:00 

唯一の問題は、ユーザーが入力した日付がDSTの時間の変更に正しい場合、現在のオフセットを読み取ってそれを使用し、それが夏時間であるかどうかを確認するため、1時間ずれている可能性があることです。そうではなく、そこにあると、正しく読み取られません。しかし、それは私が今持っているものよりもはるかに優れています。

誰かがこの機能のクリーンアップを手伝ってくれるでしょうか?これは私が必要とするもののために行く最良のルートですか?アイデア?

24
White Dragon

事前定義されたフォーマットの簡単なソリューションを次に示します。これも動的にすることができます。私は個人的にこれをjavascriptと話すために使用します:

public DateTimeOffset ParseDateExactForTimeZone(string dateTime, TimeZoneInfo timezone)
{
    var parsedDateLocal = DateTimeOffset.ParseExact(dateTime, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture);
    var tzOffset = timezone.GetUtcOffset(parsedDateLocal.DateTime);
    var parsedDateTimeZone = new DateTimeOffset(parsedDateLocal.DateTime, tzOffset);
    return parsedDateTimeZone;
}
17

これは、既知のタイムゾーンからローカルのタイムゾーンに日時文字列を解析する必要がある場合に機能するようです。多くの場合、テストは行っていませんが、これまでのところ、EU内のどこかのサーバーで時間を解析するのに最適です。

 TimeZoneInfo.ConvertTime(DateTime.Parse( "2012-05-25 23:17:15"、CultureInfo.CreateSpecificCulture( "en-EU"))、TimeZoneInfo.FindSystemTimeZoneById( "W。EuropeStandardTime") 、TimeZoneInfo.Local)
2
randomcoder

誰もより良い解決策を提供していないので(これは非常に驚くべきことです!)、私は自分の関数を答えとして受け入れますが、関数をより簡潔にして少し後でやり直すかもしれません:

public DateTimeOffset ReadStringWithTimeZone(string EnteredDate, TimeZoneInfo tzi)
{
    DateTimeOffset cvUTCToTZI = TimeZoneInfo.ConvertTime(DateTimeOffset.UtcNow, tzi);
    DateTimeOffset cvParsedDate = DateTimeOffset.MinValue;
    DateTimeOffset.TryParse(EnteredDate + " " + cvUTCToTZI.ToString("zzz"), out cvParsedDate);
    if (tzi.SupportsDaylightSavingTime)
    {
        TimeSpan getDiff = tzi.GetUtcOffset(cvParsedDate);
        string MakeFinalOffset = (getDiff.Hours < 0 ? "-" : "+") + (getDiff.Hours > 9 ? "" : "0") + getDiff.Hours + ":" + (getDiff.Minutes > 9 ? "" : "0") + getDiff.Minutes;
        DateTimeOffset.TryParse(EnteredDate + " " + MakeFinalOffset, out cvParsedDate);
        return cvParsedDate;
    }
    else
    {
        return cvParsedDate;
    }
}
1
White Dragon

最も簡単な解決策:最初に文字列をローカルのDateTimeに解析し、任意の解析メソッドを使用してから呼び出します

new DateTimeOffset(dateTimeLocal.Ticks, timezone.BaseUtcOffset)
0
Otabek Kholikov

TimeZoneInfoには静的メソッドConvertTimeToUtcがあり、日時とタイムゾーンを指定できます。これにより、時間調整が行われ、UTC日付が返されます。

MSDNのドキュメントは http://msdn.Microsoft.com/en-us/library/bb495915.aspx で、例が含まれています。

0
David McEwing