/// <inheritdoc/> protected override uint DeserializePayload(INTV.Core.Utility.BinaryReader reader) { var parsedDateMetadata = reader.ParseDateTimeFromMetadata(Length); Date = parsedDateMetadata; return(Length); }
/// <summary> /// Creates the final <see cref="MetadataDateTime"/> instance. /// </summary> /// <returns>A valid instance of a <see cref="MetadataDateTime"/>. See remarks.</returns> /// <exception cref="System.ArgumentOutOfRangeException">Thrown if a year is specified that cannot be supported.</exception> /// <remarks>Other than the value provided via the year, out-of-range values for other portions of the date time will be /// ignored, replaced using the appropriate default value defined by the <see cref="MetadataDateTime"/> type.</remarks> public MetadataDateTime Build() { ValidateYear(); ValidateMonth(); ValidateDay(); ValidateHour(); ValidateMinute(); ValidateSecond(); ValidateOffset(); var offset = _flags.HasFlag(MetadataDateTimeFlags.UtcOffset) ? new System.TimeSpan(_offsetHours, _offsetMinutes, 0) : System.TimeSpan.Zero; var date = new System.DateTimeOffset(_year, _month, _day, _hour, _minute, _second, offset); var metadataDateTime = new MetadataDateTime(date, _flags); return(metadataDateTime); }
/// <inheritdoc/> protected override void Parse(string payload) { var dateTimeString = GetCleanPayloadString(payload); if (!string.IsNullOrEmpty(dateTimeString)) { /* Date format information snapshot from as1600.txt * ------------------------------------------------------------------------------ * Dates are variable-precision quantities. They can specify as little as just * the year, or they can specify all the way down to seconds, including timezone. * The full date format is one of the following four patterns: * * YYYY-MM-DD HH:MM:SS +hhmm * YYYY-MM-DD HH:MM:SS +hh:mm * YYYY/MM/DD HH:MM:SS +hhmm * YYYY/MM/DD HH:MM:SS +hh:mm * * That is: * * -- Year: 4 digits, 1900 - 2155 * -- Month: 2 digits, 01 - 12 * -- Day: 2 digits, 01 - 31 * -- Hour: 2 digits, 00 - 23 * -- Minutes: 2 digits, 00 - 59 * -- Seconds: 2 digits, 00 - 60 (leap second permitted) * -- + or - to indicate east/west of UTC * -- Hours ahead/behind UTC (0 - 12) * -- Minutes ahead/behind UTC * * Either slashes or dashes are permitted between the YYYY-MM-DD; however, you * must be consistent. You can specify lower precision dates by leaving off * later fields. For example: * * YYYY Year only * YYYY-MM Year and month only * YYYY-MM-DD HH:MM Year, month, day, hours, and minute * * For dates, years below 100 are interpreted as 19xx. So 80 means 1980, while * 17 means 1917. Be mindful of Y2K. The valid range of years is 1901 to 2155, * with 1 through 99 serving as aliased for 1901 to 1999. A year of 0 or any * other out-of-range value is treated as an invalid/missing date. */ // With the above in mind, we then have up to three parts in the string, separated by whitespace: // The date, the time, and UTC offset. We'll take these values and do the byte parsing. // We'll need to take care though for four-digit years, as the byte parser expects offset // from 1900. var dateTimeParts = dateTimeString.Split(new[] { ' ' }, System.StringSplitOptions.RemoveEmptyEntries); var numberOfParts = dateTimeParts.Length; if (numberOfParts > 0) { var flags = MetadataDateTimeFlags.None; var year = MetadataDateTime.DefaultYear; var month = MetadataDateTime.DefaultMonth; var day = MetadataDateTime.DefaultDay; var dateString = dateTimeParts[0]; string[] parts; if (dateString.IndexOf('/') > 0) { parts = dateString.Split(new[] { '/' }, System.StringSplitOptions.RemoveEmptyEntries); } else if (dateString.IndexOf('-') > 0) { parts = dateString.Split(new[] { '-' }, System.StringSplitOptions.RemoveEmptyEntries); } else { parts = new[] { dateString }; } if (parts.Length > 0) { ushort shortYear; if (ushort.TryParse(parts[0], out shortYear)) { var shortYearRange = new Range <int>(1, 99); var fullYearRange = new Range <int>(1901, 1900 + 255); if (shortYearRange.IsValueInRange(shortYear)) { year = 1900 + shortYear; flags |= MetadataDateTimeFlags.Year; } else if (fullYearRange.IsValueInRange(shortYear)) { year = shortYear; flags |= MetadataDateTimeFlags.Year; } } } if (flags.HasFlag(MetadataDateTimeFlags.Year)) { if (parts.Length > 1) { byte byteMonth; if (byte.TryParse(parts[1], out byteMonth)) { var monthRange = new Range <byte>(1, 12); if (monthRange.IsValueInRange(byteMonth)) { month = byteMonth; flags |= MetadataDateTimeFlags.Month; } } } } if (flags.HasFlag(MetadataDateTimeFlags.Month) && (parts.Length > 2)) { byte byteDay; if (byte.TryParse(parts[2], out byteDay)) { var dayRange = new Range <byte>(1, (byte)System.DateTime.DaysInMonth(year, month)); if (dayRange.IsValueInRange(byteDay)) { day = byteDay; flags |= MetadataDateTimeFlags.Day; } } } var hour = MetadataDateTime.DefaultHour; var minute = MetadataDateTime.DefaultMinute; var second = MetadataDateTime.DefaultMinute; var timeString = numberOfParts > 1 ? dateTimeParts[1] : string.Empty; parts = timeString.Split(new[] { ':' }, System.StringSplitOptions.RemoveEmptyEntries); if (flags.HasFlag(MetadataDateTimeFlags.Day) && (parts.Length > 0)) { byte byteHour; if (byte.TryParse(parts[0], out byteHour)) { var hourRange = new Range <byte>(0, 23); if (hourRange.IsValueInRange(byteHour)) { hour = byteHour; flags |= MetadataDateTimeFlags.Hour; } } } if (flags.HasFlag(MetadataDateTimeFlags.Hour) && (parts.Length > 1)) { byte byteMinute; if (byte.TryParse(parts[1], out byteMinute)) { var minuteRange = new Range <byte>(0, 59); if (minuteRange.IsValueInRange(byteMinute)) { minute = byteMinute; flags |= MetadataDateTimeFlags.Minute; } } } if (flags.HasFlag(MetadataDateTimeFlags.Minute) && (parts.Length > 2)) { byte byteSecond; if (byte.TryParse(parts[2], out byteSecond)) { var secondRange = new Range <byte>(0, 60); if (secondRange.IsValueInRange(byteSecond)) { second = byteSecond; flags |= MetadataDateTimeFlags.Second; if (byteSecond == 60) { // For leap second, set flag and set to 59, since C# does not support it. --second; flags |= MetadataDateTimeFlags.LeapSecond; } } } } var utcOffsetString = numberOfParts > 2 ? dateTimeParts[2] : string.Empty; var utcOffsetHours = MetadataDateTime.DefaultUtcOffsetHours; var utcOffsetMinutes = MetadataDateTime.DefaultUtcOffsetMinutes; if (flags.HasFlag(MetadataDateTimeFlags.Second) && (utcOffsetString.Length > 2) && ((utcOffsetString[0] == '-') || (utcOffsetString[0] == '+'))) { const short MaxUtcOffset = 12 * 60; if (utcOffsetString.IndexOf(':') > 0) { parts = utcOffsetString.Split(new[] { ':' }, System.StringSplitOptions.RemoveEmptyEntries); if (parts.Length > 0) { sbyte byteUtcOffsetHours; if (sbyte.TryParse(parts[0], out byteUtcOffsetHours)) { var offsetHoursRange = new Range <sbyte>(-12, 12); if (offsetHoursRange.IsValueInRange(byteUtcOffsetHours)) { utcOffsetHours = byteUtcOffsetHours; flags |= MetadataDateTimeFlags.UtcOffset; } } } if (flags.HasFlag(MetadataDateTimeFlags.UtcOffset)) { if (parts.Length > 1) { byte byteUtcOffsetMinutes; if (byte.TryParse(parts[1], out byteUtcOffsetMinutes)) { var offsetMinutesRange = new Range <byte>(0, 59); if (offsetMinutesRange.IsValueInRange(byteUtcOffsetMinutes)) { utcOffsetMinutes = byteUtcOffsetMinutes; if ((utcOffsetHours < 0) || (utcOffsetString[0] == '-')) { utcOffsetMinutes = -utcOffsetMinutes; } } // Range check the total offset, so things like -12:40 are rejected. var utcTotalDeltaInMinutes = (utcOffsetHours * 60) + utcOffsetMinutes; if (System.Math.Abs(utcTotalDeltaInMinutes) > MaxUtcOffset) { utcOffsetHours = 0; utcOffsetMinutes = 0; flags &= ~MetadataDateTimeFlags.UtcOffset; } } } } } else { // Treat as a single number, with HHMM. short shortUtcOffset; if (short.TryParse(utcOffsetString, out shortUtcOffset)) { var offsetMinutes = shortUtcOffset < 0 ? shortUtcOffset % -100 : shortUtcOffset % 100; var offsetHours = shortUtcOffset / 100; var utcTotalDeltaInMinutes = (offsetHours * 60) + offsetMinutes; if (System.Math.Abs(utcTotalDeltaInMinutes) <= MaxUtcOffset) { utcOffsetMinutes = utcTotalDeltaInMinutes; flags |= MetadataDateTimeFlags.UtcOffset; } } } } if (flags != MetadataDateTimeFlags.None) { var offset = flags.HasFlag(MetadataDateTimeFlags.UtcOffset) ? new System.TimeSpan(utcOffsetHours, utcOffsetMinutes, 0) : System.TimeSpan.Zero; var date = new System.DateTimeOffset(year, month, day, hour, minute, second, offset); Date = new MetadataDateTime(date, flags); } } } }