public bool Equals(DateTag obj) { if (obj == null) { return(false); } return(m_dateTicks == obj.m_dateTicks && TimeZone.Equals(obj.TimeZone) && Precision == obj.Precision); }
/// <summary> /// Parses a metadata date tag into a <see cref="DateTag"/> including <see cref="DateTime"/>, <see cref="TimeZoneTag"/>, and significant digits. /// </summary> /// <param name="dateTag">The value to be parsed in <see cref="https://www.w3.org/TR/NOTE-datetime">W3CDTF</see> format.</param> /// <param name="result">The result of the parsing.</param> /// <returns>True if successful, else false.</returns> /// <remarks> /// <para>The <see cref="https://www.w3.org/TR/NOTE-datetime">W3CDTF</see> format has date and timezone portions. /// This method parses both.</para> /// <para>If the timezone portion is not included in the input string then the resulting <paramref name="timezone"/> /// will have <see cref="Kind"/> set to <see cref="TimeZoneKind.ForceLocal"/>. /// </para> /// <para>If the timezone portion is set to "Z" indicating UTC, then the resulting <paramref name="timezone"/> /// will have <see cref="Kind"/> set to <see cref="TimeZoneKind.ForceUtc"/> and the <see cref="UtcOffset"/> /// will be zero. /// </para> /// <para>The W2CDTF format permits partial date-time values. For example "2018" is just a year with no /// other information. The <paramref name="precision"/> value indicates how much detail is included /// as follows: 4 = year, 6 = month, 8 = day, 10 = hour, 12 = minute, 14 = second, 17 = millisecond, 20 = microsecond, /// 21 = tick (100 nanoseconds). /// </para> /// </remarks> public static bool TryParse(string dateTag, out DateTag result) { // Init values for failure case result = new DateTag(ZeroDate, TimeZoneTag.Zero, 0); // Init parts int year = 0; int month = 1; int day = 1; int hour = 12; // Noon int minute = 0; int second = 0; long ticks = 0; // Track position int pos = 0; if (dateTag.Length < 4) { return(false); } if (!int.TryParse(dateTag.Substring(0, 4), out year) || year < 1 || year > 9999) { return(false); } int precision = PrecisionYear; pos = 4; if (dateTag.Length > 5 && dateTag[4] == '-') { if (!int.TryParse(dateTag.Substring(5, 2), out month) || month < 1 || month > 12) { return(false); } precision = PrecisionMonth; pos = 7; if (dateTag.Length > 8 && dateTag[7] == '-') { if (!int.TryParse(dateTag.Substring(8, 2), out day) || day < 1 || day > DateTime.DaysInMonth(year, month)) { return(false); } precision = PrecisionDay; pos = 10; if (dateTag.Length > 11 && (dateTag[10] == 'T' || dateTag[10] == ' ')) // Even though W3CDTF and ISO 8601 specify 'T' separating date and time, tolerate a space as an alternative. { if (!int.TryParse(dateTag.Substring(11, 2), out hour) || hour < 0 || hour > 23) { return(false); } precision = PrecisionHour; pos = 13; if (dateTag.Length > 14 && dateTag[13] == ':') { if (!int.TryParse(dateTag.Substring(14, 2), out minute) || minute < 0 || minute > 59) { return(false); } precision = PrecisionMinute; pos = 16; if (dateTag.Length > 17 && dateTag[16] == ':') { if (!int.TryParse(dateTag.Substring(17, 2), out second) || second < 0 || second > 59) { return(false); } precision = PrecisionSecond; pos = 19; if (dateTag.Length > 20 && dateTag[19] == '.') { ++pos; int anchor = pos; while (pos < dateTag.Length && char.IsDigit(dateTag[pos])) { ++pos; } precision = PrecisionSecond + (pos - anchor); if (precision > PrecisionMax) { precision = PrecisionMax; } double d; if (!double.TryParse(dateTag.Substring(anchor, pos - anchor), out d)) { return(false); } ticks = (long)(d * Math.Pow(10.0, 7.0 - (pos - anchor))); } } } } } } // Attempt to parse the timezone TimeZoneTag timezone; DateTimeKind dtk = DateTimeKind.Unspecified; if (pos < dateTag.Length) { if (!TimeZoneTag.TryParse(dateTag.Substring(pos), out timezone)) { return(false); } dtk = (timezone.Kind == TimeZoneKind.ForceUtc) ? DateTimeKind.Utc : DateTimeKind.Local; } else { timezone = TimeZoneTag.ForceLocal; dtk = DateTimeKind.Local; } result = new DateTag(new DateTime(year, month, day, hour, minute, second, dtk).AddTicks(ticks), timezone, precision); return(true); }