Разобрать ISO 8601 как в базовом, так и в расширенном формате в С#

Насколько я вижу, DateTime.ParseExact с параметрами «o» и «s» способен анализировать только расширенный формат ISO 8601.

Как разобрать ISO 8601 как в базовом, так и в расширенном формате с помощью С#/.Net?


person IT Hit WebDAV    schedule 03.12.2013    source источник


Ответы (2)


Просто передайте массив форматов, которые вы хотите разрешить.

string[] formats = new[] {"o","s","yyyyMMddHHmmss"}; // whatever you want
DateTime dt = DateTime.ParseExact(yourDateString, formats,
                                  CultureInfo.InvariantCulture,
                                  DateTimeStlyes.None);
person Matt Johnson-Pint    schedule 03.12.2013

Вот класс, который я, наконец, создал для разбора как базового, так и расширенного форматов:

static class Iso8601Formats
{
    /// <summary>
    /// Represents ISO 8601 extended time format.
    /// </summary>
    public static readonly string[] TimeExtended;

    /// <summary>
    /// Represents ISO 8601 basic time format.
    /// </summary>
    public static readonly string[] TimeBasic;

    /// <summary>
    /// Represents ISO 8601 both basic and extended time format.
    /// </summary>
    public static readonly string[] Time;


    /// <summary>
    /// Represents ISO 8601 extended date format.
    /// </summary>
    public static readonly string[] DateExtended;

    /// <summary>
    /// Represents ISO 8601 basic date format.
    /// </summary>
    public static readonly string[] DateBasic;

    /// <summary>
    /// Represents ISO 8601 both basic and extended time format.
    /// </summary>
    public static readonly string[] Date;


    /// <summary>
    /// Represents ISO 8601 extended date and time format.
    /// </summary>
    public static readonly string[] DateAndTimeExtended;

    /// <summary>
    /// Represents ISO 8601 basic date and time format.
    /// </summary>
    public static readonly string[] DateAndTimeBasic;

    /// <summary>
    /// Represents ISO 8601 both basic and extended date and time format.
    /// </summary>        
    public static readonly string[] DateAndTime;

    /// <summary>
    /// Represents ISO 8601 extended date and/or time format.
    /// </summary>
    public static readonly string[] DateAndOrTimeExtended;

    /// <summary>
    /// Represents ISO 8601 basic date and/or time format.
    /// </summary>
    public static readonly string[] DateAndOrTimeBasic;

    /// <summary>
    /// Represents ISO 8601 both basic and extended date and/or time format.
    /// </summary>
    public static readonly string[] DateAndOrTime;

    static Iso8601Formats()
    {
        // date format extended
        IList<string> dateExtended = new List<string>{
                                      "yyyy'-'MM'-'dd"  // 1985-04-12
                                  };
        DateExtended = dateExtended.ToArray();

        // date format basic
        IList<string> dateBasic = new List<string>{
                                      "yyyyMMdd"        // 19850412
                                      ,"yyyy'-'MM"      // 1985-04
                                      ,"yyyy"           // 1985
                                      ,"'--'MMdd"       // --0412
                                      ,"'---'dd"        // ---12
                                  };
        DateBasic = dateBasic.ToArray();

        // date both basic and extended format
        List<string> date = new List<string>();
        date.AddRange(dateBasic);
        date.AddRange(dateExtended);
        Date = date.ToArray();


        // time format extended
        IList<string> timeExtended = new List<string>{                                          
                                      "HH':'mm':'sszzz"       // 10:22:00-0800
                                      ,"HH':'mm':'sszz"       // 10:22:00-08
                                      ,"HH':'mm':'ssZ"        // 10:22:00Z
                                      ,"HH':'mm':'ss"         // 10:22:05
                                      ,"HH':'mm"              // 10:22
                                      ,"HH"                   // 10
                                      ,"'-:'mm':'ss"          // -22:07
                                      ,"'-:-:'ss"             // -:-:07
        };
        TimeExtended = timeExtended.ToArray();

        // time format basic
        IList<string> timeBasic = new List<string>{                                          
                                      "HHmmsszzz"       // 102200-0800
                                      ,"HHmmsszz"       // 102200-08
                                      ,"HHmmssZ"        // 102200Z
                                      ,"HHmmss"         // 102205
                                      ,"HHmm"           // 1022
                                      ,"HH"             // 10
                                      ,"'-'mmss"        // -2207
                                      ,"'--'ss"         // --07
                                  };
        TimeBasic = timeBasic.ToArray();


        // time format both basic and extended
        List<string> time = new List<string>();
        time.AddRange(timeBasic);
        time.AddRange(timeExtended);
        Time = time.ToArray();


        // date-time basic
        IList<string> dateTimeBasic = 
            CombineFormats(dateBasic, timeBasic);
        DateAndTimeBasic = dateTimeBasic.ToArray();

        // date-time extended
        IList<string> dateTimeExtended = 
            CombineFormats(dateExtended, timeExtended);
        DateAndTimeExtended = dateTimeExtended.ToArray();

        // date-time both basic and extended
        List<string> dateTime = new List<string>();
        dateTime.AddRange(dateTimeBasic);
        dateTime.AddRange(dateTimeExtended);
        DateAndTime = dateTime.ToArray();


        // date-and-or-time basic format
        List<string> dateAndOrTimeBasic = new List<string>();
        dateAndOrTimeBasic.AddRange(dateTimeBasic);
        dateAndOrTimeBasic.AddRange(dateBasic);
        foreach (string timeFormat in timeBasic)
        {
            dateAndOrTimeBasic.Add("'T'" + timeFormat);
        }
        DateAndOrTimeBasic = dateAndOrTimeBasic.ToArray();

        // date-and-or-time extended format
        List<string> dateAndOrTimeExtended = new List<string>();
        dateAndOrTimeExtended.AddRange(dateTimeExtended);
        dateAndOrTimeExtended.AddRange(dateExtended);
        foreach (string timeFormat in timeExtended)
        {
            dateAndOrTimeExtended.Add("'T'" + timeFormat);
        }
        DateAndOrTimeExtended = dateAndOrTimeExtended.ToArray();


        // date-and-or-time basic and extended format
        List<string> dateAndOrTime = new List<string>();
        dateAndOrTime.AddRange(dateAndOrTimeBasic);
        dateAndOrTime.AddRange(dateAndOrTimeExtended);
        DateAndOrTime = dateAndOrTime.ToArray();
    }

    /// <summary>
    /// Produces all combinations of date and time formats
    /// </summary>
    /// <param name="dates">List of dates formats</param>
    /// <param name="times">List of time formats</param>
    private static IList<string> CombineFormats(IList<string> dates, IList<string> times)
    {
        List<string> dt = new List<string>();
        foreach (string dateFormat in dates)
        {
            foreach (string timeFormat in times)
            {
                // year/month must be present if time zone is specified
                if (dateFormat.StartsWith("'-") && (timeFormat.IndexOfAny(new[] { 'z', 'Z' }) != -1))
                    continue;

                dt.Add(dateFormat + "'T'" + timeFormat);
            }
        }
        return dt;
    }
}

Статическое свойство Iso8601Formats.DateAndOrTime, вероятно, является наиболее полезным в этом классе. Используя код:

DateTimeOffset.ParseExact("1985-04-12T10:22:00Z", Iso8601Formats.DateAndOrTime, CultureInfo.InvariantCulture, DateTimeStyles.None);

Мне удалось разобрать следующую дату/время ISO 8601:

19850412
1985-04
1985
--0412
---12
1985-04-12
T102205
T1022
T10
T-2207
T--07
T102200Z
T102200-08
T102200-0830
T10:22:05
T10:22
T10
T-:22:07
T-:-:07
T10:22:00Z
T10:22:00-08
T10:22:00-0830
19850412T102205
19850412T1022
19850412T10
19850412T-2207
19850412T--07
19850412T102200Z
19850412T102200-08
19850412T102200-0830
1985-04T102205
1985-04T1022
1985-04T10
1985-04T-2207
1985-04T--07
1985-04T102200Z
1985-04T102200-08
1985-04T102200-0830
1985T102205
1985T1022
1985T10
1985T-2207
1985T--07
1985T102200Z
1985T102200-08
1985T102200-0830
--0412T102205
--0412T1022
--0412T10
--0412T-2207
--0412T--07
---12T102205
---12T1022
---12T10
---12T-2207
---12T--07
1985-04-12T10:22:05
1985-04-12T10:22
1985-04-12T10
1985-04-12T-:22:07
1985-04-12T-:-:07
1985-04-12T10:22:00Z
1985-04-12T10:22:00-08
1985-04-12T10:22:00-0830

Однако этот класс по-прежнему не может анализировать дату/время с указанным часовым поясом и отсутствующим годом, например:

--0412T102200Z
--0412T102200-08
--0412T102200-0830
person IT Hit WebDAV    schedule 06.12.2013
comment
Некоторая конструктивная критика: 1) Этот класс может достичь того, что вы намеревались сделать, но он довольно неэффективен. Поскольку конечный результат всегда один и тот же, вы могли бы просто иметь некоторые константы в статическом классе. 2) Почему вы хотите проанализировать так много разных входных данных в один результат? Вы можете получить какой-то результат, но для конкретной цели потребуется определенный ввод. Например, если кто-то предоставляет только "T10:22:07", результат может иметь это время, но также содержать сегодняшнюю дату и смещение локальной машины. Вы не можете просто рассматривать это как DateTimeOffset в этот момент, - person Matt Johnson-Pint; 06.12.2013
comment
вы бы хотели смотреть только на компоненты времени, но теперь у вас был бы способ узнать это. 3) Если вы планируете делать что-то сложное, подобное этому, вы можете вместо этого просто посмотреть на Noda Time. Есть много нюансов, подобных этим, которые заманивают вас в ловушку. Noda Time заставляет вас идти по пути, избегая ловушек. 4) Удачи! - person Matt Johnson-Pint; 06.12.2013
comment
Мне нужно проанализировать так много типов форматов даты/времени, потому что это то, что указано в vCard RFC 6350 и RFC 2426, которые мне нужно реализовать. Поле, которое я анализирую, может быть датой или временем или датой + временем в базовом или расширенном форматах. - person IT Hit WebDAV; 06.12.2013
comment
Спасибо за ссылку на nodatime. Я быстро просмотрел его, похоже, он поддерживает только один расширенный формат: nodatime. org/1.2.x/userguide/offsetdatetime-patterns.html - person IT Hit WebDAV; 06.12.2013
comment
Да, вы можете сделать все эти поля формата даты и времени постоянными. Но массивы инициализации будут довольно большими (64 только для DateAndOrTime). Будет тонна дубликатов, поддерживать такой класс будет довольно сложно. Добавление поддержки только одного нового формата потребует изменений во многих местах. - person IT Hit WebDAV; 07.12.2013