.net 如何解析日期时间并将其转换为 RFC 822 日期时间格式?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/284775/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 10:35:51  来源:igfitidea点击:

How do I parse and convert DateTime’s to the RFC 822 date-time format?

.netdatetimerssrfc822

提问by Oppositional

How do I convert a DateTime structure to its equivalent RFC 822 date-timeformatted string representation andparse this string representation back to a DateTime structure in .NET? The RFC-822 date-time format is used in a number of specifications such as the RSS Syndication Format.

如何将 DateTime 结构转换为其等效的RFC 822 日期时间格式字符串表示形式,并将此字符串表示形式解析回 .NET 中的 DateTime 结构?RFC-822 日期时间格式用于许多规范,例如RSS Syndication Format

采纳答案by Oppositional

This is an implementation in C# of how to parse and convert a DateTime to and from its RFC-822 representation. The only restriction it has is that the DateTime is in Coordinated Universal Time (UTC). I agree that this is not very elegant code, but it does the job.

这是在 C# 中如何解析 DateTime 和从其 RFC-822 表示转换的实现。它的唯一限制是 DateTime 使用协调世界时 (UTC)。我同意这不是很优雅的代码,但它可以完成工作。

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

回答by Jeff Woodman

Try this:

尝试这个:

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

The "r" format specifier passed into DateTime's ToString() method actually yields an RFC-1123-formatted datetime string, but passes as an RFC-822 date as well, based on reading the specification found at http://www.w3.org/Protocols/rfc822/#z28. I've used this method in creating RSS feeds, and they pass validation based on the validator available at http://validator.w3.org/feed/check.cgi.

传递到 DateTime 的 ToString() 方法中的“r”格式说明符实际上产生了一个 RFC-1123 格式的日期时间字符串,但基于阅读http://www.w3上的规范,它也作为 RFC-822 日期传递。组织/协议/rfc822/#z28。我在创建 RSS 提要时使用了这种方法,并且它们通过了基于http://validator.w3.org/feed/check.cgi上可用的验证器的验证。

The downside is that, in the conversion, it converts the datetime to GMT. To convert back to local time you would need to apply your local timezone offset. For that, you might use the TimeZone class to get your current timezone offset, and replace "GMT" with a timezone offset string:

缺点是,在转换中,它将日期时间转换为 GMT。要转换回本地时间,您需要应用本地时区偏移量。为此,您可以使用 TimeZone 类来获取当前时区偏移量,并用时区偏移量字符串替换“GMT”:

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

回答by Sasha

Following Kirk's idea, I decompiled sources for System.ServiceModel.Syndication.Rss20FeedFormatterclass (System.ServiceModel.dll) and here is Microsoft internal parser for RFC 822 dates format(I slightly simplified their exception handling logic to reduce dependencies):

按照 Kirk 的想法,我反编译了System.ServiceModel.Syndication.Rss20FeedFormatter类 ( System.ServiceModel.dll) 的源代码,这里是RFC 822 日期格式的 Microsoft 内部解析器(我稍微简化了它们的异常处理逻辑以减少依赖性):

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

The first thing which may look unusual is that they return [DateTimeOffset][1]class instead of DateTime. But when we read more about it, it appears to be completely logical - DateTimeOffsetstores date, time and timezoneinfo (exactly as string in RFC 822 format). If you were returning just DateTime object, which timezone it would be in: UTC, local, or the one specified in parsed string - any answer would be wrong for some cases. So DateTimeOffsetsolves an important uncertainty problem. And you can convert it to timezone you need later using methods DateTimeOffset.ToUniversalTime(), DateTimeOffset.ToLocalTime().

可能看起来不寻常的第一件事是它们返回[DateTimeOffset][1]class 而不是DateTime. 但是当我们阅读更多关于它的信息时,它似乎是完全合乎逻辑的——DateTimeOffset存储日期、时间和时区信息(完全是 RFC 822 格式的字符串)。如果您只返回 DateTime 对象,它将位于哪个时区:UTC、本地或已解析字符串中指定的时区 - 在某些情况下,任何答案都是错误的。所以DateTimeOffset解决了一个重要的不确定性问题。您可以使用方法将其转换为您稍后需要的时区DateTimeOffset.ToUniversalTime()DateTimeOffset.ToLocalTime()

I tested it on few cases and it seems it does the job perfectly.

我在少数情况下对其进行了测试,它似乎完美地完成了这项工作。

I'm not sure though, why Microsoft decided to make this implementation private - it doesn't seem to require a lot of support.

我不确定,为什么微软决定将这个实现私有化——它似乎不需要很多支持。

回答by Kirk Liemohn

Here is how Microsoft does it in the Rss20FeedFormatter. Oppositional's code doesn't get rid of the ":" in the GMT offset portion. Jeff Woodman's appears to do this. The code below does this as well (if not using Atom10FeedFormatter.zeroOffset).

以下是 Microsoft 在 Rss20FeedFormatter 中的做法。反对派的代码没有去掉 GMT 偏移部分中的“:”。杰夫伍德曼似乎做到了这一点。下面的代码也这样做(如果不使用 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();
}

回答by Eric Boumendil

Based on the answer of Kirk Liemohn, I used this method with success:

根据 Kirk Liemohn 的回答,我成功地使用了这种方法:

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

Example:

例子:

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

It does not respect full spec of RFC but it works for my use cases.

它不遵守 RFC 的完整规范,但适用于我的用例。

Specifically, it does not work with timezone express like : "GMT", "CST", etc. (see "zone" in RFC822 Section 5.1). See the better answer of Oleksandr Pshenychnyy.

具体来说,它不适用于时区表达,例如:“GMT”、“CST”等(参见RFC822 第 5.1 节中的“区域”)。请参阅Oleksandr Pshenychnyy更好答案

回答by Raymond Powell

Here is my implementation using an extension method:

这是我使用扩展方法的实现:

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

To use:

使用:

 using MyNamespace;

 ....

 string MyRFC822String = DateTime.Now.ToRFC822String();