39 votes

Comment analyser et convertir les DateTime au format date-heure RFC 822 ?

Comment convertir une structure DateTime en son équivalent ? RFC 822 date-heure représentation sous forme de chaîne formatée y analyser cette représentation de chaîne en une structure DateTime dans .NET ? Le format date-heure RFC-822 est utilisé dans un certain nombre de spécifications telles que la norme Format de syndication RSS .

45voto

Jeff Woodman Points 281

Essayez ça :

  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);

Le spécificateur de format "r" passé dans la méthode ToString() de DateTime produit en fait une chaîne de date au format RFC-1123, mais passe également pour une date RFC-822, d'après la lecture de la spécification trouvée à l'adresse suivante http://www.w3.org/Protocols/rfc822/#z28 . J'ai utilisé cette méthode pour créer des flux RSS, et ils passent la validation basée sur le validateur disponible à l'adresse suivante http://validator.w3.org/feed/check.cgi .

L'inconvénient est que, lors de la conversion, il convertit la date en heure GMT. Pour la reconvertir en heure locale, vous devez appliquer le décalage de votre fuseau horaire local. Pour cela, vous pouvez utiliser la classe TimeZone pour obtenir le décalage de votre fuseau horaire actuel, et remplacer "GMT" par une chaîne de décalage de fuseau horaire :

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);

30voto

Oppositional Points 5966

Il s'agit d'une implémentation en C# de l'analyse et de la conversion d'un DateTime depuis et vers sa représentation RFC-822. La seule restriction est que la date et l'heure doivent être en temps universel coordonné (UTC). Je reconnais que ce code n'est pas très élégant, mais il fait l'affaire.

/// <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
}

2voto

Kirk Liemohn Points 3489

Voici comment Microsoft procède dans le Rss20FeedFormatter. Le code d'Oppositional ne se débarrasse pas du " :" dans la partie décalage GMT. Celui de Jeff Woodman semble le faire. Le code ci-dessous le fait également (s'il n'utilise pas Atom10FeedFormatter.zeroOffset).

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();
}

1voto

Eric Boumendil Points 436

Sur la base de la réponse de Kirk Liemohn, j'ai utilisé cette méthode avec succès :

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;
    }

Exemple :

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

Il ne respecte pas toutes les spécifications de la RFC mais il fonctionne pour mes cas d'utilisation.

Plus précisément, il ne fonctionne pas avec les fuseaux horaires express comme : "GMT", "CST", etc. (cf. "zone" dans la section 5.1 du RFC822 ). Voir le meilleure réponse de Oleksandr Pshenychnyy .

1voto

Raymond Powell Points 19

Voici ma mise en œuvre en utilisant une méthode d'extension :

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(":", "");
        }
    }
 }

A utiliser :

 using MyNamespace;

 ....

 string MyRFC822String = DateTime.Now.ToRFC822String();

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X