2008-11-12 6 views
33

Wie konvertiere ich eine DateTime-Struktur in die entsprechende RFC 822 date-time formatierte Zeichenfolgendarstellung und parsen Sie diese Zeichenfolgendarstellung zurück zu einer DateTime-Struktur in .NET? Das RFC-822-Datum-Uhrzeit-Format wird in einer Reihe von Spezifikationen wie dem RSS Syndication Format verwendet.Wie analysiere und konvertiere ich DateTime in das RFC-822-Datum/Uhrzeit-Format?

+0

Ich war auf der Suche nach einer .NET-Implementierung. – Oppositional

+0

Ich würde gerne ein Update verwenden, um DateTimeOffset zu verwenden. – Broam

+0

.NET verfügt über einen out-of-Box-RSS-2.0-Formatierer, der die Artikelveröffentlichungsdaten korrekt nach RFC 822 serialisiert. Siehe ['Rss20FeedFormatter'] (http://msdn.microsoft.com/de-de/library/system.servicemodel .syndication.rss20feedformatter (v = vs.110) .aspx) -Klasse in 'System.ServiceModel' Assembly für Details. – whyleee

Antwort

29

Dies ist eine Implementierung in C# zum Analysieren und Konvertieren einer DateTime zu und von ihrer RFC-822-Darstellung. Die einzige Einschränkung ist, dass die DateTime in Coordinated Universal Time (UTC) ist. Ich stimme zu, dass dies kein sehr eleganter Code ist, aber es macht den Job.

/// <summary> 
/// Provides methods for converting <see cref="DateTime"/> structures 
/// to and from the equivalent <a href="http://www.w3.org/Protocols/rfc822/#z28">RFC 822</a> 
/// string representation. 
/// </summary> 
public class Rfc822DateTime 
{ 
    //============================================================ 
    // Private members 
    //============================================================ 
    #region Private Members 
    /// <summary> 
    /// Private member to hold array of formats that RFC 822 date-time representations conform to. 
    /// </summary> 
    private static string[] formats = new string[0]; 
    /// <summary> 
    /// Private member to hold the DateTime format string for representing a DateTime in the RFC 822 format. 
    /// </summary> 
    private const string format  = "ddd, dd MMM yyyy HH:mm:ss K"; 
    #endregion 

    //============================================================ 
    // Public Properties 
    //============================================================ 
    #region Rfc822DateTimeFormat 
    /// <summary> 
    /// Gets the custom format specifier that may be used to represent a <see cref="DateTime"/> in the RFC 822 format. 
    /// </summary> 
    /// <value>A <i>DateTime format string</i> that may be used to represent a <see cref="DateTime"/> in the RFC 822 format.</value> 
    /// <remarks> 
    /// <para> 
    /// This method returns a string representation of a <see cref="DateTime"/> that utilizes the time zone 
    /// offset (local differential) to represent the offset from Greenwich mean time in hours and minutes. 
    /// The <see cref="Rfc822DateTimeFormat"/> is a valid date-time format string for use 
    /// in the <see cref="DateTime.ToString(String, IFormatProvider)"/> method. 
    /// </para> 
    /// <para> 
    /// The <a href="http://www.w3.org/Protocols/rfc822/#z28">RFC 822</a> Date and Time specification 
    /// specifies that the year will be represented as a two-digit value, but the 
    /// <a href="http://www.rssboard.org/rss-profile#data-types-datetime">RSS Profile</a> recommends that 
    /// all date-time values should use a four-digit year. The <see cref="Rfc822DateTime"/> class 
    /// follows the RSS Profile recommendation when converting a <see cref="DateTime"/> to the equivalent 
    /// RFC 822 string representation. 
    /// </para> 
    /// </remarks> 
    public static string Rfc822DateTimeFormat 
    { 
     get 
     { 
      return format; 
     } 
    } 
    #endregion 

    #region Rfc822DateTimePatterns 
    /// <summary> 
    /// Gets an array of the expected formats for RFC 822 date-time string representations. 
    /// </summary> 
    /// <value> 
    /// An array of the expected formats for RFC 822 date-time string representations 
    /// that may used in the <see cref="DateTime.TryParseExact(String, string[], IFormatProvider, DateTimeStyles, out DateTime)"/> method. 
    /// </value> 
    /// <remarks> 
    /// The array of the expected formats that is returned assumes that the RFC 822 time zone 
    /// is represented as or converted to a local differential representation. 
    /// </remarks> 
    /// <seealso cref="ConvertZoneToLocalDifferential(String)"/> 
    public static string[] Rfc822DateTimePatterns 
    { 
     get 
     { 
      if (formats.Length > 0) 
      { 
       return formats; 
      } 
      else 
      { 
       formats = new string[35]; 

       // two-digit day, four-digit year patterns 
       formats[0] = "ddd',' dd MMM yyyy HH':'mm':'ss'.'fffffff zzzz"; 
       formats[1] = "ddd',' dd MMM yyyy HH':'mm':'ss'.'ffffff zzzz"; 
       formats[2] = "ddd',' dd MMM yyyy HH':'mm':'ss'.'fffff zzzz"; 
       formats[3] = "ddd',' dd MMM yyyy HH':'mm':'ss'.'ffff zzzz"; 
       formats[4] = "ddd',' dd MMM yyyy HH':'mm':'ss'.'fff zzzz"; 
       formats[5] = "ddd',' dd MMM yyyy HH':'mm':'ss'.'ff zzzz"; 
       formats[6] = "ddd',' dd MMM yyyy HH':'mm':'ss'.'f zzzz"; 
       formats[7] = "ddd',' dd MMM yyyy HH':'mm':'ss zzzz"; 

       // two-digit day, two-digit year patterns 
       formats[8] = "ddd',' dd MMM yy HH':'mm':'ss'.'fffffff zzzz"; 
       formats[9] = "ddd',' dd MMM yy HH':'mm':'ss'.'ffffff zzzz"; 
       formats[10] = "ddd',' dd MMM yy HH':'mm':'ss'.'fffff zzzz"; 
       formats[11] = "ddd',' dd MMM yy HH':'mm':'ss'.'ffff zzzz"; 
       formats[12] = "ddd',' dd MMM yy HH':'mm':'ss'.'fff zzzz"; 
       formats[13] = "ddd',' dd MMM yy HH':'mm':'ss'.'ff zzzz"; 
       formats[14] = "ddd',' dd MMM yy HH':'mm':'ss'.'f zzzz"; 
       formats[15] = "ddd',' dd MMM yy HH':'mm':'ss zzzz"; 

       // one-digit day, four-digit year patterns 
       formats[16] = "ddd',' d MMM yyyy HH':'mm':'ss'.'fffffff zzzz"; 
       formats[17] = "ddd',' d MMM yyyy HH':'mm':'ss'.'ffffff zzzz"; 
       formats[18] = "ddd',' d MMM yyyy HH':'mm':'ss'.'fffff zzzz"; 
       formats[19] = "ddd',' d MMM yyyy HH':'mm':'ss'.'ffff zzzz"; 
       formats[20] = "ddd',' d MMM yyyy HH':'mm':'ss'.'fff zzzz"; 
       formats[21] = "ddd',' d MMM yyyy HH':'mm':'ss'.'ff zzzz"; 
       formats[22] = "ddd',' d MMM yyyy HH':'mm':'ss'.'f zzzz"; 
       formats[23] = "ddd',' d MMM yyyy HH':'mm':'ss zzzz"; 

       // two-digit day, two-digit year patterns 
       formats[24] = "ddd',' d MMM yy HH':'mm':'ss'.'fffffff zzzz"; 
       formats[25] = "ddd',' d MMM yy HH':'mm':'ss'.'ffffff zzzz"; 
       formats[26] = "ddd',' d MMM yy HH':'mm':'ss'.'fffff zzzz"; 
       formats[27] = "ddd',' d MMM yy HH':'mm':'ss'.'ffff zzzz"; 
       formats[28] = "ddd',' d MMM yy HH':'mm':'ss'.'fff zzzz"; 
       formats[29] = "ddd',' d MMM yy HH':'mm':'ss'.'ff zzzz"; 
       formats[30] = "ddd',' d MMM yy HH':'mm':'ss'.'f zzzz"; 
       formats[31] = "ddd',' d MMM yy HH':'mm':'ss zzzz"; 

       // Fall back patterns 
       formats[32] = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffffK"; // RoundtripDateTimePattern 
       formats[33] = DateTimeFormatInfo.InvariantInfo.UniversalSortableDateTimePattern; 
       formats[34] = DateTimeFormatInfo.InvariantInfo.SortableDateTimePattern; 

       return formats; 
      } 
     } 
    } 
    #endregion 

    //============================================================ 
    // Public Methods 
    //============================================================ 
    #region Parse(string s) 
    /// <summary> 
    /// Converts the specified string representation of a date and time to its <see cref="DateTime"/> equivalent. 
    /// </summary> 
    /// <param name="s">A string containing a date and time to convert.</param> 
    /// <returns> 
    /// A <see cref="DateTime"/> equivalent to the date and time contained in <paramref name="s"/>, 
    /// expressed as <i>Coordinated Universal Time (UTC)</i>. 
    /// </returns> 
    /// <remarks> 
    /// The string <paramref name="s"/> is parsed using formatting information in the <see cref="DateTimeFormatInfo.InvariantInfo"/> object. 
    /// </remarks> 
    /// <exception cref="ArgumentNullException"><paramref name="s"/> is a <b>null</b> reference (Nothing in Visual Basic).</exception> 
    /// <exception cref="ArgumentNullException"><paramref name="s"/> is an empty string.</exception> 
    /// <exception cref="FormatException"><paramref name="s"/> does not contain a valid RFC 822 string representation of a date and time.</exception> 
    public static DateTime Parse(string s) 
    { 
     //------------------------------------------------------------ 
     // Validate parameter 
     //------------------------------------------------------------ 
     if (String.IsNullOrEmpty(s)) 
     { 
      throw new ArgumentNullException("s"); 
     } 

     DateTime result; 
     if (Rfc822DateTime.TryParse(s, out result)) 
     { 
      return result; 
     } 
     else 
     { 
      throw new FormatException(String.Format(null, "{0} is not a valid RFC 822 string representation of a date and time.", s)); 
     } 
    } 
    #endregion 

    #region ConvertZoneToLocalDifferential(string s) 
    /// <summary> 
    /// Converts the time zone component of an RFC 822 date and time string representation to its local differential (time zone offset). 
    /// </summary> 
    /// <param name="s">A string containing an RFC 822 date and time to convert.</param> 
    /// <returns>A date and time string that uses local differential to describe the time zone equivalent to the date and time contained in <paramref name="s"/>.</returns> 
    /// <exception cref="ArgumentNullException"><paramref name="s"/> is a <b>null</b> reference (Nothing in Visual Basic).</exception> 
    /// <exception cref="ArgumentNullException"><paramref name="s"/> is an empty string.</exception> 
    public static string ConvertZoneToLocalDifferential(string s) 
    { 
     string zoneRepresentedAsLocalDifferential = String.Empty; 

     //------------------------------------------------------------ 
     // Validate parameter 
     //------------------------------------------------------------ 
     if (String.IsNullOrEmpty(s)) 
     { 
      throw new ArgumentNullException("s"); 
     } 

     if(s.EndsWith(" UT", StringComparison.OrdinalIgnoreCase)) 
     { 
      zoneRepresentedAsLocalDifferential = String.Concat(s.Substring(0, (s.LastIndexOf(" UT") + 1)), "+00:00"); 
     } 
     else if (s.EndsWith(" GMT", StringComparison.OrdinalIgnoreCase)) 
     { 
      zoneRepresentedAsLocalDifferential = String.Concat(s.Substring(0, (s.LastIndexOf(" GMT") + 1)), "+00:00"); 
     } 
     else if (s.EndsWith(" EST", StringComparison.OrdinalIgnoreCase)) 
     { 
      zoneRepresentedAsLocalDifferential = String.Concat(s.Substring(0, (s.LastIndexOf(" EST") + 1)), "-05:00"); 
     } 
     else if (s.EndsWith(" EDT", StringComparison.OrdinalIgnoreCase)) 
     { 
      zoneRepresentedAsLocalDifferential = String.Concat(s.Substring(0, (s.LastIndexOf(" EDT") + 1)), "-04:00"); 
     } 
     else if (s.EndsWith(" CST", StringComparison.OrdinalIgnoreCase)) 
     { 
      zoneRepresentedAsLocalDifferential = String.Concat(s.Substring(0, (s.LastIndexOf(" CST") + 1)), "-06:00"); 
     } 
     else if (s.EndsWith(" CDT", StringComparison.OrdinalIgnoreCase)) 
     { 
      zoneRepresentedAsLocalDifferential = String.Concat(s.Substring(0, (s.LastIndexOf(" CDT") + 1)), "-05:00"); 
     } 
     else if (s.EndsWith(" MST", StringComparison.OrdinalIgnoreCase)) 
     { 
      zoneRepresentedAsLocalDifferential = String.Concat(s.Substring(0, (s.LastIndexOf(" MST") + 1)), "-07:00"); 
     } 
     else if (s.EndsWith(" MDT", StringComparison.OrdinalIgnoreCase)) 
     { 
      zoneRepresentedAsLocalDifferential = String.Concat(s.Substring(0, (s.LastIndexOf(" MDT") + 1)), "-06:00"); 
     } 
     else if (s.EndsWith(" PST", StringComparison.OrdinalIgnoreCase)) 
     { 
      zoneRepresentedAsLocalDifferential = String.Concat(s.Substring(0, (s.LastIndexOf(" PST") + 1)), "-08:00"); 
     } 
     else if (s.EndsWith(" PDT", StringComparison.OrdinalIgnoreCase)) 
     { 
      zoneRepresentedAsLocalDifferential = String.Concat(s.Substring(0, (s.LastIndexOf(" PDT") + 1)), "-07:00"); 
     } 
     else if (s.EndsWith(" Z", StringComparison.OrdinalIgnoreCase)) 
     { 
      zoneRepresentedAsLocalDifferential = String.Concat(s.Substring(0, (s.LastIndexOf(" Z") + 1)), "+00:00"); 
     } 
     else if (s.EndsWith(" A", StringComparison.OrdinalIgnoreCase)) 
     { 
      zoneRepresentedAsLocalDifferential = String.Concat(s.Substring(0, (s.LastIndexOf(" A") + 1)), "-01:00"); 
     } 
     else if (s.EndsWith(" M", StringComparison.OrdinalIgnoreCase)) 
     { 
      zoneRepresentedAsLocalDifferential = String.Concat(s.Substring(0, (s.LastIndexOf(" M") + 1)), "-12:00"); 
     } 
     else if (s.EndsWith(" N", StringComparison.OrdinalIgnoreCase)) 
     { 
      zoneRepresentedAsLocalDifferential = String.Concat(s.Substring(0, (s.LastIndexOf(" N") + 1)), "+01:00"); 
     } 
     else if (s.EndsWith(" Y", StringComparison.OrdinalIgnoreCase)) 
     { 
      zoneRepresentedAsLocalDifferential = String.Concat(s.Substring(0, (s.LastIndexOf(" Y") + 1)), "+12:00"); 
     } 
     else 
     { 
      zoneRepresentedAsLocalDifferential = s; 
     } 

     return zoneRepresentedAsLocalDifferential; 
    } 
    #endregion 

    #region ToString(DateTime utcDateTime) 
    /// <summary> 
    /// Converts the value of the specified <see cref="DateTime"/> object to its equivalent string representation. 
    /// </summary> 
    /// <param name="utcDateTime">The Coordinated Universal Time (UTC) <see cref="DateTime"/> to convert.</param> 
    /// <returns>A RFC 822 string representation of the value of the <paramref name="utcDateTime"/>.</returns> 
    /// <exception cref="ArgumentException">The specified <paramref name="utcDateTime"/> object does not represent a <see cref="DateTimeKind.Utc">Coordinated Universal Time (UTC)</see> value.</exception> 
    public static string ToString(DateTime utcDateTime) 
    { 
     if (utcDateTime.Kind != DateTimeKind.Utc) 
     { 
      throw new ArgumentException("utcDateTime"); 
     } 

     return utcDateTime.ToString(Rfc822DateTime.Rfc822DateTimeFormat, DateTimeFormatInfo.InvariantInfo); 
    } 
    #endregion 

    #region TryParse(string s, out DateTime result) 
    /// <summary> 
    /// Converts the specified string representation of a date and time to its <see cref="DateTime"/> equivalent. 
    /// </summary> 
    /// <param name="s">A string containing a date and time to convert.</param> 
    /// <param name="result"> 
    /// When this method returns, contains the <see cref="DateTime"/> value equivalent to the date and time 
    /// contained in <paramref name="s"/>, expressed as <i>Coordinated Universal Time (UTC)</i>, 
    /// if the conversion succeeded, or <see cref="DateTime.MinValue">MinValue</see> if the conversion failed. 
    /// The conversion fails if the s parameter is a <b>null</b> reference (Nothing in Visual Basic), 
    /// or does not contain a valid string representation of a date and time. 
    /// This parameter is passed uninitialized. 
    /// </param> 
    /// <returns><b>true</b> if the <paramref name="s"/> parameter was converted successfully; otherwise, <b>false</b>.</returns> 
    /// <remarks> 
    /// The string <paramref name="s"/> is parsed using formatting information in the <see cref="DateTimeFormatInfo.InvariantInfo"/> object. 
    /// </remarks> 
    public static bool TryParse(string s, out DateTime result) 
    { 
     //------------------------------------------------------------ 
     // Attempt to convert string representation 
     //------------------------------------------------------------ 
     bool wasConverted = false; 
     result    = DateTime.MinValue; 

     if (!String.IsNullOrEmpty(s)) 
     { 
      DateTime parseResult; 
      if (DateTime.TryParseExact(Rfc822DateTime.ConvertZoneToLocalDifferential(s), Rfc822DateTime.Rfc822DateTimePatterns, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AdjustToUniversal, out parseResult)) 
      { 
       result   = DateTime.SpecifyKind(parseResult, DateTimeKind.Utc); 
       wasConverted = true; 
      } 
     } 

     return wasConverted; 
    } 
    #endregion 
} 
+24

Dieser Code sieht schrecklich aus. – Esko

+0

Die Verwendung des 'Guard'-Typs in diesem Code erinnert mich an die Lösung im Argotic Syndication Framework. Ist dieser Beta-Code von Argotic? Ihre Lösung für dieses Problem in der neuesten Version von Argotic ist das Beste, was ich gesehen habe. – rasx

+0

Dies ist kein Beta-Code von Argotic, und ich glaube, dass der neueste von Argotic Probobly eine bessere Implementation als der hier beschriebene hat. – Oppositional

38

Diese Versuchen:

DateTime today = DateTime.Now; 
    String rfc822 = today.ToString("r"); 
    Console.WriteLine("RFC-822 date: {0}", rfc822); 

    DateTime parsedRFC822 = DateTime.Parse(rfc822); 
    Console.WriteLine("Date: {0}", parsedRFC822); 

Das "r" Format-Spezifizierer in Datetime ToString() übergeben Verfahren ergibt tatsächlich eine RFC-1123-Format DATETIME string, sondern geht als RFC-822-Datum als Nun, basierend auf dem Lesen der Spezifikation gefunden bei http://www.w3.org/Protocols/rfc822/#z28. Ich habe diese Methode bei der Erstellung von RSS-Feeds verwendet und sie bestehen die Validierung basierend auf dem Validator, der unter http://validator.w3.org/feed/check.cgi verfügbar ist.

Der Nachteil ist, dass bei der Konvertierung die Datetime in GMT konvertiert wird. Um zurück in die lokale Zeit zu konvertieren, müssen Sie Ihren lokalen Zeitzonen-Offset anwenden. Dazu könnten Sie die Time-Zone-Klasse verwenden, um Ihre aktuelle Zeitzone versetzt zu bekommen, und ersetzen „GMT“ mit einer Zonenversatz string:

TimeZone tz = TimeZone.CurrentTimeZone; 

String offset = tz.GetUtcOffset().ToString(); 
// My locale is Mountain time; offset is set to "-07:00:00" 
// if local time is behind utc time, offset should start with "-". 
// otherwise, add a plus sign to the beginning of the string. 
if (!offset.StartsWith("-")) 
    offset = "+" + offset; // Add a (+) if it's a UTC+ timezone 
offset = offset.Substring(0,6); // only want the first 6 chars. 
offset = offset.Replace(":", ""); // remove colons. 
// offset now looks something like "-0700". 
rfc822 = rfc822.Replace("GMT", offset); 
// The rfc822 string can now be parsed back to a DateTime object, 
// with the local time accounted for. 
DateTime new = DateTime.Parse(rfc822); 
+0

Einfach ... Ich mag es! –

2

Hier ist, wie Microsoft tut es in der Rss20FeedFormatter. Der oppositionelle Code wird das ":" im GMT-Offset-Bereich nicht los. Jeff Woodman scheint das zu tun. Der Code unten tut dies auch (wenn Atom10FeedFormatter.zeroOffset nicht verwendet wird).

private string AsString(DateTimeOffset dateTime) 
{ 
    if (dateTime.Offset == Atom10FeedFormatter.zeroOffset) 
    { 
     return dateTime.ToUniversalTime().ToString("ddd, dd MMM yyyy HH:mm:ss Z", CultureInfo.InvariantCulture); 
    } 
    StringBuilder builder = new StringBuilder(dateTime.T)oString("ddd, dd MMM yyyy HH:mm:ss zzz", CultureInfo.InvariantCulture)); 
    builder.Remove(builder.Length - 3, 1); 
    return builder.ToString(); 
} 
+0

Kann 'Atom10FeedFormatter' nicht auflösen. Wie behebe ich das unter VS2015? – Adrian

+0

Es war 7 Jahre, also wird das vielleicht nicht funktionieren, aber versuchen Sie Folgendes, wenn Sie es noch nicht getan haben. Fügen Sie System.ServiceModel.dll als Assemblyverweis hinzu, und fügen Sie eine using-Anweisung für System.ServiceModel.Syndication hinzu. Siehe auch: https://msdn.microsoft.com/en-us/library/system.servicemodel.syndication.atom10feedformatter(v=vs.110).aspx –

1

Basierend auf der Antwort von Kirk Liemohn, habe ich diese Methode mit Erfolg:

private DateTimeOffset? ParseDate(string date) 
    { 
     const string FORMAT = "ddd, d MMM yyyy HH:mm:ss zzz"; 
     const string FORMAT2 = "ddd, dd MMM yyyy HH:mm:ss zzz"; 
     const string FORMAT3 = "dd MMM yyyy HH:mm:ss zzz"; 
     const string FORMAT4 = "d MMM yyyy HH:mm:ss zzz"; 
     DateTimeOffset d; 
     if (DateTimeOffset.TryParseExact(date, new string[] { FORMAT, FORMAT2, FORMAT3, FORMAT4 }, CultureInfo.InvariantCulture, DateTimeStyles.AllowLeadingWhite | DateTimeStyles.AllowTrailingWhite, out d)) 
      return d; 
     return null; 
    } 

Beispiel:

DateTimeOffset? date = ParseDate("Thu, 5 Apr 2012 23:47:37 +0200"); 
Console.WriteLine(date.ToString()); 
// => 05/04/2012 23:47:37 +02:00 

Es ist nicht voll spec von RFC respektieren, aber es funktioniert für meine Anwendungsfälle.

Insbesondere funktioniert es nicht mit Timezone Express wie: "GMT", "CST", etc. (siehe "zone" in RFC822 Section 5.1). Siehe better answer of Oleksandr Pshenychnyy.

1

Hier ist meine Implementierung eine Erweiterung Methode:

namespace MyNamespace 
{ 
    public static partial class ExtensionMethods 
    { 
     public static string ToRFC822String(this DateTime timestamp) 
     { 
      return timestamp.ToString("ddd',' d MMM yyyy HH':'mm':'ss") 
       + " " 
       + timestamp.ToString("zzzz").Replace(":", ""); 
     } 
    } 
} 

zu benutzen:

using MyNamespace; 

.... 

string MyRFC822String = DateTime.Now.ToRFC822String(); 
+1

Bitte geben Sie nicht nur Code in Ihre Antwort ein – Cybermaxs

+0

Dokumentation hinzugefügt. –

1

Nach Kirk Idee, ich Quellen für System.ServiceModel.Syndication.Rss20FeedFormatter Klasse (System.ServiceModel.dll) dekompilierten und Hier ist Microsoft internen Parser für RFC 822 Datumsformat (Ich vereinfachte leicht ihre Ausnahmebehandlung Logik, um Abhängigkeiten zu reduzieren):

public static class DateTimeParser 
{ 
    public static DateTimeOffset ParseDateTimeRFC822(string dateTimeString) 
    { 
     StringBuilder dateTimeStringBuilder = new StringBuilder(dateTimeString.Trim()); 
     if (dateTimeStringBuilder.Length < 18) 
     { 
      throw new FormatException("Invalid date format. Expected date in RFC 822 format"); 
     } 
     if (dateTimeStringBuilder[3] == ',') 
     { 
      // There is a leading (e.g.) "Tue, ", strip it off 
      dateTimeStringBuilder.Remove(0, 4); 
      // There's supposed to be a space here but some implementations dont have one 
      RemoveExtraWhiteSpaceAtStart(dateTimeStringBuilder); 
     } 
     ReplaceMultipleWhiteSpaceWithSingleWhiteSpace(dateTimeStringBuilder); 
     if (char.IsDigit(dateTimeStringBuilder[1])) 
     { 
      // two-digit day, we are good 
     } 
     else 
     { 
      dateTimeStringBuilder.Insert(0, '0'); 
     } 
     if (dateTimeStringBuilder.Length < 19) 
     { 
      throw new FormatException("Invalid date format. Expected date in RFC 822 format"); 
     } 
     bool thereAreSeconds = (dateTimeStringBuilder[17] == ':'); 
     int timeZoneStartIndex; 
     if (thereAreSeconds) 
     { 
      timeZoneStartIndex = 21; 
     } 
     else 
     { 
      timeZoneStartIndex = 18; 
     } 
     string timeZoneSuffix = dateTimeStringBuilder.ToString().Substring(timeZoneStartIndex); 
     dateTimeStringBuilder.Remove(timeZoneStartIndex, dateTimeStringBuilder.Length - timeZoneStartIndex); 
     bool isUtc; 
     dateTimeStringBuilder.Append(NormalizeTimeZone(timeZoneSuffix, out isUtc)); 
     string wellFormattedString = dateTimeStringBuilder.ToString(); 

     DateTimeOffset theTime; 
     string parseFormat; 
     if (thereAreSeconds) 
     { 
      parseFormat = "dd MMM yyyy HH:mm:ss zzz"; 
     } 
     else 
     { 
      parseFormat = "dd MMM yyyy HH:mm zzz"; 
     } 
     if (DateTimeOffset.TryParseExact(wellFormattedString, parseFormat, 
      CultureInfo.InvariantCulture.DateTimeFormat, 
      (isUtc ? DateTimeStyles.AdjustToUniversal : DateTimeStyles.None), out theTime)) 
     { 
      return theTime; 
     } 
     throw new FormatException("Invalid date format. Expected date in RFC 822 format"); 
    } 

    static string NormalizeTimeZone(string rfc822TimeZone, out bool isUtc) 
    { 
     isUtc = false; 
     // return a string in "-08:00" format 
     if (rfc822TimeZone[0] == '+' || rfc822TimeZone[0] == '-') 
     { 
      // the time zone is supposed to be 4 digits but some feeds omit the initial 0 
      StringBuilder result = new StringBuilder(rfc822TimeZone); 
      if (result.Length == 4) 
      { 
       // the timezone is +/-HMM. Convert to +/-HHMM 
       result.Insert(1, '0'); 
      } 
      result.Insert(3, ':'); 
      return result.ToString(); 
     } 
     switch (rfc822TimeZone) 
     { 
      case "UT": 
      case "Z": 
       isUtc = true; 
       return "-00:00"; 
      case "GMT": 
       return "-00:00"; 
      case "A": 
       return "-01:00"; 
      case "B": 
       return "-02:00"; 
      case "C": 
       return "-03:00"; 
      case "D": 
      case "EDT": 
       return "-04:00"; 
      case "E": 
      case "EST": 
      case "CDT": 
       return "-05:00"; 
      case "F": 
      case "CST": 
      case "MDT": 
       return "-06:00"; 
      case "G": 
      case "MST": 
      case "PDT": 
       return "-07:00"; 
      case "H": 
      case "PST": 
       return "-08:00"; 
      case "I": 
       return "-09:00"; 
      case "K": 
       return "-10:00"; 
      case "L": 
       return "-11:00"; 
      case "M": 
       return "-12:00"; 
      case "N": 
       return "+01:00"; 
      case "O": 
       return "+02:00"; 
      case "P": 
       return "+03:00"; 
      case "Q": 
       return "+04:00"; 
      case "R": 
       return "+05:00"; 
      case "S": 
       return "+06:00"; 
      case "T": 
       return "+07:00"; 
      case "U": 
       return "+08:00"; 
      case "V": 
       return "+09:00"; 
      case "W": 
       return "+10:00"; 
      case "X": 
       return "+11:00"; 
      case "Y": 
       return "+12:00"; 
      default: 
       return ""; 
     } 
    } 

    static void RemoveExtraWhiteSpaceAtStart(StringBuilder stringBuilder) 
    { 
     int i = 0; 
     while (i < stringBuilder.Length) 
     { 
      if (!char.IsWhiteSpace(stringBuilder[i])) 
      { 
       break; 
      } 
      ++i; 
     } 
     if (i > 0) 
     { 
      stringBuilder.Remove(0, i); 
     } 
    } 

    static void ReplaceMultipleWhiteSpaceWithSingleWhiteSpace(StringBuilder builder) 
    { 
     int index = 0; 
     int whiteSpaceStart = -1; 
     while (index < builder.Length) 
     { 
      if (char.IsWhiteSpace(builder[index])) 
      { 
       if (whiteSpaceStart < 0) 
       { 
        whiteSpaceStart = index; 
        // normalize all white spaces to be ' ' so that the date time parsing works 
        builder[index] = ' '; 
       } 
      } 
      else if (whiteSpaceStart >= 0) 
      { 
       if (index > whiteSpaceStart + 1) 
       { 
        // there are at least 2 spaces... replace by 1 
        builder.Remove(whiteSpaceStart, index - whiteSpaceStart - 1); 
        index = whiteSpaceStart + 1; 
       } 
       whiteSpaceStart = -1; 
      } 
      ++index; 
     } 
     // we have already trimmed the start and end so there cannot be a trail of white spaces in the end 
     Debug.Assert(builder.Length == 0 || builder[builder.Length - 1] != ' ', "The string builder doesnt end in a white space"); 
    } 
} 

Das erste, was ungewöhnlich aussehen kann, ist, dass sie [DateTimeOffset][1] Klasse statt DateTime zurückzukehren.Aber wenn wir mehr darüber lesen, scheint es völlig logisch zu sein - DateTimeOffset speichert Datum, Uhrzeit und Zeitzone info (genau wie Zeichenfolge im RFC 822-Format). Wenn Sie nur das DateTime-Objekt zurückgeben würden, wäre die Zeitzone: UTC, lokal oder die in analysierter Zeichenfolge angegebene - jede Antwort wäre in einigen Fällen falsch. So löst ein wichtiges Unsicherheitsproblem. Und Sie können es in die Zeitzone konvertieren, die Sie später mit den Methoden DateTimeOffset.ToUniversalTime(), DateTimeOffset.ToLocalTime() benötigen.

Ich habe es in wenigen Fällen getestet und es scheint, dass es den Job perfekt macht.

Ich bin mir nicht sicher, warum Microsoft entschied, diese Implementierung privat zu machen - es scheint nicht viel Unterstützung zu benötigen.

Verwandte Themen