internal static ExtendedDateTime Add(ExtendedDateTime e, TimeSpan t) { var extendedDateTime = TimeSpanToExtendedDateTime((e - new ExtendedDateTime(1, 1, 1)) + t, e.UtcOffset); extendedDateTime.YearFlags = e.YearFlags; extendedDateTime.MonthFlags = e.MonthFlags; extendedDateTime.DayFlags = e.DayFlags; extendedDateTime.Precision = e.Precision; return(extendedDateTime); }
internal static DayOfWeek DayOfWeek(ExtendedDateTime e) // http://www.stoimen.com/blog/2012/04/24/computer-algorithms-how-to-determine-the-day-of-the-week/ { var yearOfCentury = YearOfCentury(e.Year); var centuryOfYear = CenturyOfYear(e.Year); var monthKey = IsLeapYear(e.Year) ? DayOfWeekMonthKeys366[e.Month] : DayOfWeekMonthKeys365[e.Month]; var centuryKey = DayOfWeekCenturyKeys[centuryOfYear % 4 + centuryOfYear < 0 ? 4 : 0]; var dayOfWeek = (e.Day + monthKey + yearOfCentury + yearOfCentury / 4 + centuryKey) % 7; return((DayOfWeek)dayOfWeek); }
public void CanRoundTrip() { // Date Assert.AreEqual("2001-02-03", ExtendedDateTime.Parse("2001-02-03").ToString()); Assert.AreEqual("2008-12", ExtendedDateTime.Parse("2008-12").ToString()); Assert.AreEqual("2008", ExtendedDateTime.Parse("2008").ToString()); Assert.AreEqual("-0999", ExtendedDateTime.Parse("-0999").ToString()); Assert.AreEqual("0000", ExtendedDateTime.Parse("0000").ToString()); // Date and Time Assert.AreEqual("2001-02-03T09:30:01-08", ExtendedDateTime.Parse("2001-02-03T09:30:01-08").ToString()); Assert.AreEqual("2004-01-01T10:10:10Z", ExtendedDateTime.Parse("2004-01-01T10:10:10Z").ToString()); Assert.AreEqual("2004-01-01T10:10:10+05", ExtendedDateTime.Parse("2004-01-01T10:10:10+05").ToString()); // Uncertain and Approximate Assert.AreEqual("1984?", ExtendedDateTime.Parse("1984?").ToString()); Assert.AreEqual("2004-06?", ExtendedDateTime.Parse("2004-06?").ToString()); Assert.AreEqual("2004-06-11?", ExtendedDateTime.Parse("2004-06-11?").ToString()); Assert.AreEqual("1984~", ExtendedDateTime.Parse("1984~").ToString()); Assert.AreEqual("1984?~", ExtendedDateTime.Parse("1984?~").ToString()); // Years Exceeding Four Digits Assert.AreEqual("y170000002", ExtendedDateTime.Parse("y170000002").ToString()); Assert.AreEqual("y-170000002", ExtendedDateTime.Parse("y-170000002").ToString()); // Season Assert.AreEqual("2001-21", ExtendedDateTime.Parse("2001-21").ToString()); Assert.AreEqual("2001-22", ExtendedDateTime.Parse("2001-22").ToString()); Assert.AreEqual("2001-23", ExtendedDateTime.Parse("2001-23").ToString()); Assert.AreEqual("2001-24", ExtendedDateTime.Parse("2001-24").ToString()); // Partial Uncertain or Approximate Assert.AreEqual("2004?-06-11", ExtendedDateTime.Parse("2004?-06-11").ToString()); Assert.AreEqual("2004-06~-11", ExtendedDateTime.Parse("2004-06~-11").ToString()); Assert.AreEqual("2004-(06)?-11", ExtendedDateTime.Parse("2004-(06)?-11").ToString()); Assert.AreEqual("2004-06-(11)~", ExtendedDateTime.Parse("2004-06-(11)~").ToString()); Assert.AreEqual("2004-(06)?~", ExtendedDateTime.Parse("2004-(06)?~").ToString()); Assert.AreEqual("2004?-06-(11)~", ExtendedDateTime.Parse("2004?-06-(11)~").ToString()); Assert.AreEqual("2004?-(06)?~", ExtendedDateTime.Parse("2004?-(06)?~").ToString()); Assert.AreEqual("(2004)?-06-04~", ExtendedDateTime.Parse("(2004)?-06-04~").ToString()); Assert.AreEqual("(2011)-06-04~", ExtendedDateTime.Parse("(2011)-06-04~").ToString()); Assert.AreEqual("2011-23~", ExtendedDateTime.Parse("2011-23~").ToString()); // Exponential Form of Years Exceeding Four Digits Assert.AreEqual("y17e7", ExtendedDateTime.Parse("y17e7").ToString()); Assert.AreEqual("y-17e7", ExtendedDateTime.Parse("y-17e7").ToString()); Assert.AreEqual("y17101e4p3", ExtendedDateTime.Parse("y17101e4p3").ToString()); }
public override object ConvertFrom(ITypeDescriptorContext context, Globalization.CultureInfo culture, object value) { if (value == null) { throw GetConvertFromException(value); } var source = value as string; if (source != null) { return(ExtendedDateTime.Parse(source)); } return(base.ConvertFrom(context, culture, value)); }
internal static ExtendedDateTime AddYears(ExtendedDateTime e, int count) { if (e.Month == 2 && e.Day == 29 && IsLeapYear(e.Year) && !IsLeapYear(e.Year + count)) { throw new InvalidOperationException("Years cannot be added to a leap day unless the resulting year also has a leap day."); } var extendedDateTime = new ExtendedDateTime(e.Year + count, e.Month, e.Day, e.Hour, e.Minute, e.Second, e.UtcOffset.Hours, e.UtcOffset.Minutes); extendedDateTime.YearFlags = e.YearFlags; extendedDateTime.MonthFlags = e.MonthFlags; extendedDateTime.DayFlags = e.DayFlags; extendedDateTime.Precision = e.Precision; return(extendedDateTime); }
internal static ExtendedDateTime SubtractYears(ExtendedDateTime e, int count) { if (e.Month == 2 && e.Day == 29 && IsLeapYear(e.Year) && !IsLeapYear(e.Year - count)) { throw new InvalidOperationException("The years subtracted from a leap day must result in another leap day."); } var extendedDateTime = new ExtendedDateTime(e.Year - count, e.Month, e.Day, e.Hour, e.Minute, e.Second, e.UtcOffset.Hours, e.UtcOffset.Minutes); extendedDateTime.YearFlags = e.YearFlags; extendedDateTime.MonthFlags = e.MonthFlags; extendedDateTime.DayFlags = e.DayFlags; extendedDateTime.Precision = e.Precision; return(extendedDateTime); }
public void CalculatesCorrectDifference() { Assert.AreEqual(TimeSpan.Zero, (ExtendedDateTime.Parse("2012") - ExtendedDateTime.Parse("2012"))); Assert.AreEqual(TimeSpan.FromDays(1096), (ExtendedDateTime.Parse("2015") - ExtendedDateTime.Parse("2012"))); // 366 for 2012 (leap year) + 365 for 2013 + 365 for 2014 = 1096 days Assert.AreEqual(TimeSpan.Zero, (ExtendedDateTime.Parse("2012-01") - ExtendedDateTime.Parse("2012-01"))); Assert.AreEqual(TimeSpan.FromDays(31), (ExtendedDateTime.Parse("2012-02") - ExtendedDateTime.Parse("2012-01"))); Assert.AreEqual(TimeSpan.FromDays(365), (ExtendedDateTime.Parse("2013-03") - ExtendedDateTime.Parse("2012-03"))); // 31 days for 3/2012 + 30 days for 4/2012 + 31 days for 5/2012 + 30 days for 6/2012 + 31 days for 7/2012 + 31 days for 8/2012 + 30 days for 9/2012 + 31 days for 10/2012 + 30 days for 11/2012 + 31 days for 12/2012 + 31 days for 1/2013 + 28 days for 2/2013 = 365 days Assert.AreEqual(TimeSpan.Zero, (ExtendedDateTime.Parse("2012-02-02") - ExtendedDateTime.Parse("2012-02-02"))); Assert.AreEqual(TimeSpan.FromDays(292), (ExtendedDateTime.Parse("2012-11-20") - ExtendedDateTime.Parse("2012-02-02"))); // 28 days remaining in of February + 31 days in March + 30 days in April + 31 days in May + 30 days in June + 31 days in July + 31 days in August + 30 days in September + 31 days in October + 19 days passed into November = 292 days Assert.AreEqual(TimeSpan.Zero, (ExtendedDateTime.Parse("2012-03-03T03Z") - ExtendedDateTime.Parse("2012-03-03T03Z"))); Assert.AreEqual(new TimeSpan(292, 18, 0, 0), (ExtendedDateTime.Parse("2012-11-20T20Z") - ExtendedDateTime.Parse("2012-02-02T02Z"))); // 20 additional hours passed after the end day - 2 hours in to the beginning day = 18 additional hours Assert.AreEqual(TimeSpan.Zero, (ExtendedDateTime.Parse("2012-03-03T03:03Z") - ExtendedDateTime.Parse("2012-03-03T03:03Z"))); Assert.AreEqual(new TimeSpan(292, 18, 18, 0), (ExtendedDateTime.Parse("2012-11-20T20:20Z") - ExtendedDateTime.Parse("2012-02-02T02:02Z"))); // 20 additional minutes passed after the end hour - 2 minutes in to the beginning hour = 18 additional minutes Assert.AreEqual(TimeSpan.Zero, (ExtendedDateTime.Parse("2012-03-03T03:03:03Z") - ExtendedDateTime.Parse("2012-03-03T03:03:03Z"))); Assert.AreEqual(new TimeSpan(292, 18, 18, 18), (ExtendedDateTime.Parse("2012-11-20T20:20:20Z") - ExtendedDateTime.Parse("2012-02-02T02:02:02Z"))); // 20 additional seconds passed after the end minute - 2 seconds in to the beginning minute = 18 additional seconds Assert.AreEqual(new TimeSpan(291, 16, 0, 0), (ExtendedDateTime.Parse("2012-11-20T00:00:00-08:00") - ExtendedDateTime.Parse("2012-02-02T00:00:00Z"))); // 28 days remaining in of February + 31 days in March + 30 days in April + 31 days in May + 30 days in June + 31 days in July + 31 days in August + 30 days in September + 31 days in October + 19 days passed into November - 8 hours behind = 291.16 days }
public static ExtendedDateTime AddMonths(ExtendedDateTime e, int monthsToAdd, DayExceedsDaysInMonthStrategy dayExceedsDaysInMonthStrategy) { var monthTotal = e.Month + monthsToAdd; var month = monthTotal % 12; if (month == 0) { month = 12; } var year = e.Year + (monthTotal - 1) / 12; var day = e.Day; if (day > DaysInMonth(year, month)) { switch (dayExceedsDaysInMonthStrategy) { case DayExceedsDaysInMonthStrategy.RoundDown: day = DaysInMonth(year, month); break; case DayExceedsDaysInMonthStrategy.Overflow: day = day - DaysInMonth(year, month); monthTotal = month + 1; month = monthTotal % 12; if (month == 0) { month = 12; } year = e.Year + (monthTotal - 1) / 12; break; } } var extendedDateTime = new ExtendedDateTime(year, month, e.Day, e.Hour, e.Minute, e.Second, e.UtcOffset.Hours, e.UtcOffset.Minutes); extendedDateTime.YearFlags = e.YearFlags; extendedDateTime.MonthFlags = e.MonthFlags; extendedDateTime.DayFlags = e.DayFlags; extendedDateTime.Precision = e.Precision; return(extendedDateTime); }
internal static ExtendedDateTime SubtractMonths(ExtendedDateTime e, int count) { var month = e.Month - count % 12; var year = e.Year - count / 12; if (e.Day > DaysInMonth(year, month)) { throw new InvalidOperationException("The day is greater than the number of days in the resulting month."); } var extendedDateTime = new ExtendedDateTime(year, month, e.Day, e.Hour, e.Minute, e.Second, e.UtcOffset.Hours, e.UtcOffset.Minutes); extendedDateTime.YearFlags = e.YearFlags; extendedDateTime.MonthFlags = e.MonthFlags; extendedDateTime.DayFlags = e.DayFlags; extendedDateTime.Precision = e.Precision; return(extendedDateTime); }
internal static TimeSpan Subtract(ExtendedDateTime later, ExtendedDateTime earlier) { return(TimeSpan.FromDays( (later.Year - earlier.Year) * 365 + (later.Year - 1) / 4 - (earlier.Year - 1) / 4 - (later.Year - 1) / 100 + (earlier.Year - 1) / 100 + (later.Year - 1) / 400 - (earlier.Year - 1) / 400 + DaysToMonth(later.Year, later.Month) - DaysToMonth(earlier.Year, earlier.Month) + later.Day - earlier.Day) + TimeSpan.FromHours( later.Hour - earlier.Hour + later.UtcOffset.Hours - earlier.UtcOffset.Hours) + TimeSpan.FromMinutes( later.Minute - earlier.Minute + later.UtcOffset.Minutes - earlier.UtcOffset.Minutes) + TimeSpan.FromSeconds(later.Second - earlier.Second)); }
public double ToPixels(ExtendedDateTime start, ExtendedDateTime end) { switch (TimeRulerUnit) { case TimeRulerUnit.Day: return (end - start).TotalDays * TimeUnitWidth; case TimeRulerUnit.Hour: return (end - start).TotalHours * TimeUnitWidth; case TimeRulerUnit.Minute: return (end - start).TotalMinutes * TimeUnitWidth; case TimeRulerUnit.Second: return (end - start).TotalSeconds * TimeUnitWidth; default: return double.NaN; } }
internal static ExtendedDateTimePossibilityCollection Parse(string extendedDateTimeMaskedPrecisionString) { if (extendedDateTimeMaskedPrecisionString.Length != 4) { throw new ParseException("A masked precision string must be four characters long.", extendedDateTimeMaskedPrecisionString); } if (extendedDateTimeMaskedPrecisionString.StartsWith("xx") || extendedDateTimeMaskedPrecisionString[0] == 'x' || extendedDateTimeMaskedPrecisionString[1] == 'x') { throw new ParseException("Masked precision can only apply to the tens or ones place of the year.", extendedDateTimeMaskedPrecisionString); } var extendedDateTimeRange = new ExtendedDateTimeRange(); var start = new ExtendedDateTime(); var end = new ExtendedDateTime(); if (extendedDateTimeMaskedPrecisionString[2] == 'x') { start.Year = int.Parse(string.Format("{0}{1}00", extendedDateTimeMaskedPrecisionString[0], extendedDateTimeMaskedPrecisionString[1])); end.Year = int.Parse(string.Format("{0}{1}99", extendedDateTimeMaskedPrecisionString[0], extendedDateTimeMaskedPrecisionString[1])); } else { start.Year = int.Parse(string.Format("{0}{1}{2}0", extendedDateTimeMaskedPrecisionString[0], extendedDateTimeMaskedPrecisionString[1], extendedDateTimeMaskedPrecisionString[2])); end.Year = int.Parse(string.Format("{0}{1}{2}9", extendedDateTimeMaskedPrecisionString[0], extendedDateTimeMaskedPrecisionString[1], extendedDateTimeMaskedPrecisionString[2])); } extendedDateTimeRange.Start = start; extendedDateTimeRange.End = end; var possibilityCollection = new ExtendedDateTimePossibilityCollection(); possibilityCollection.Add(extendedDateTimeRange); return(possibilityCollection); }
private static void CommitDateComponent(ref int dateComponentIndex, ref bool hasSeasonComponent, int flags, List <char> componentBuffer, ref ExtendedDateTime extendedDateTime) { if (componentBuffer.Count == 0) { return; } var componentString = new string(componentBuffer.ToArray()); if (dateComponentIndex == 0) // We expect a year to appear first. { var isLongFormYear = false; var isExponent = false; var isPrecision = false; var digits = new List <char>(); for (int i = 0; i < componentString.Length; i++) { if (i == 0 && componentString[i] == 'y') // Component is long-form year. { isLongFormYear = true; } else if (char.IsDigit(componentString[i]) || componentString[i] == '-') // Character is year digit or negative sign. { digits.Add(componentString[i]); } else if (isLongFormYear && componentString[i] == 'e') // Component indicates exponent. { extendedDateTime.Year = int.Parse(new string(digits.ToArray())); digits.Clear(); isExponent = true; } else if (componentString[i] == 'p' && isExponent) // Component indicates precision. { extendedDateTime.YearExponent = int.Parse(new string(digits.ToArray())); digits.Clear(); isPrecision = true; isExponent = false; } else { throw new ParseException("The year is invalid.", componentString); } } if (isExponent) { extendedDateTime.YearExponent = int.Parse(new string(digits.ToArray())); } else if (isPrecision) { extendedDateTime.YearPrecision = int.Parse(new string(digits.ToArray())); } else { if (digits.Count == 0 || (digits.Count == 1 && digits[0] == '-')) { throw new ParseException("The year must have more than zero digits.", componentString); } var year = int.Parse(new string(digits.ToArray())); if (isLongFormYear) { if (isExponent && year == 0) { throw new ParseException("The significand of a long year cannot be zero.", componentString); } else if (digits.Count < 4 || (digits[0] == '-' && digits.Count < 5)) { throw new ParseException("The long year must have at least four digits.", componentString); } } else if (digits.Count < 4 || (digits[0] == '-' && digits.Count < 5) || year < -9999 || year > 9999) { throw new ParseException("The year must have four digits.", componentString); } extendedDateTime.Year = year; } extendedDateTime.YearFlags = (YearFlags)flags; dateComponentIndex++; } else if (dateComponentIndex == 1) // We expect either a month or a season to appear second. { if (componentString[0] == '2') // The component is a season. { if (componentString.Contains('^')) // Check for season qualifier. { var seasonComponentStrings = componentString.Split('^'); extendedDateTime.SeasonQualifier = seasonComponentStrings[1]; componentString = seasonComponentStrings[0]; } if (componentString.Length != 2) { throw new ParseException("The season must have two digits (excluding the qualifier).", componentString); } else if (componentString.Any(c => !char.IsDigit(c))) { throw new ParseException("The season must be a number (excluding the qualifier).", componentString); } var seasonInteger = int.Parse(componentString); if (seasonInteger < 21 || seasonInteger > 24) { throw new ParseException("The season must be between 21 and 24.", componentString); } extendedDateTime.Season = (Season)seasonInteger; extendedDateTime.SeasonFlags = (SeasonFlags)flags; hasSeasonComponent = true; } else // The component is a month { if (componentString.Length != 2) { throw new ParseException("The month must have two digits.", componentString); } if (componentString.Any(c => !char.IsDigit(c))) { throw new ParseException("The month must be a number.", componentString); } var monthInteger = int.Parse(componentString); if (monthInteger < 1 || monthInteger > 12) { throw new ParseException("The month must be between 1 and 12.", componentString); } extendedDateTime.Month = monthInteger; extendedDateTime.Precision++; extendedDateTime.MonthFlags = (MonthFlags)flags; } dateComponentIndex++; } else if (dateComponentIndex == 2) // We expect a day. { if (hasSeasonComponent) { throw new ParseException("A season and day cannot coexist.", componentString); } if (componentString.Length != 2) { throw new ParseException("The day must have two digits.", componentString); } if (componentString.Any(c => !char.IsDigit(c))) { throw new ParseException("The day must be a number.", componentString); } var dayInteger = int.Parse(componentString); if (dayInteger < 1 || dayInteger > 31) { throw new ParseException("The day must be between 1 and 31.", componentString); } var daysInMonth = ExtendedDateTimeCalculator.DaysInMonth(extendedDateTime.Year, extendedDateTime.Month); if (dayInteger > daysInMonth) { throw new ParseException("The month " + extendedDateTime.Month + " in the year " + extendedDateTime.Year + " has only " + daysInMonth + " days.", componentString); } extendedDateTime.Day = dayInteger; extendedDateTime.Precision++; extendedDateTime.DayFlags = (DayFlags)flags; dateComponentIndex++; } else if (dateComponentIndex > 2) { throw new ParseException("The date can have at most three components.", componentString); } componentBuffer.Clear(); }
internal static ExtendedDateTimePossibilityCollection ToPossibilityCollection(UnspecifiedExtendedDateTime unspecifiedExtendedDateTime) { var extendedDateTimePossibilityCollection = new ExtendedDateTimePossibilityCollection(); var extendedDateTimeRange = new ExtendedDateTimeRange(); var startExtendedDateTime = new ExtendedDateTime(); var endExtendedDateTime = new ExtendedDateTime(); extendedDateTimeRange.Start = startExtendedDateTime; extendedDateTimeRange.End = endExtendedDateTime; extendedDateTimePossibilityCollection.Add(extendedDateTimeRange); if (unspecifiedExtendedDateTime.Year.Length != 4) // Year { throw new ConversionException("An UnspecifiedExtendedDateTime year must be four characters long."); } var yearStartBuffer = new List <char>(); var yearEndBuffer = new List <char>(); for (int i = 0; i < 4; i++) { if (unspecifiedExtendedDateTime.Year[0] == 'u') { if (i == 0) { yearStartBuffer.Add('-'); } yearStartBuffer.Add('9'); yearEndBuffer.Add('9'); } else if (unspecifiedExtendedDateTime.Year[i] == 'u') { yearStartBuffer.Add('0'); yearEndBuffer.Add('9'); } else { yearStartBuffer.Add(unspecifiedExtendedDateTime.Year[i]); yearEndBuffer.Add(unspecifiedExtendedDateTime.Year[i]); } } var yearStart = int.Parse(new string(yearStartBuffer.ToArray())); var yearEnd = int.Parse(new string(yearEndBuffer.ToArray())); if (unspecifiedExtendedDateTime.Month == null) // Month { startExtendedDateTime.Year = yearStart; endExtendedDateTime.Year = yearEnd; return(extendedDateTimePossibilityCollection); } if (unspecifiedExtendedDateTime.Month.Length != 2) { throw new ConversionException("A month must be two characters long."); } var monthStartBuffer = new List <char>(); var monthEndBuffer = new List <char>(); var isFirstMonthDigitUnspecified = false; if (unspecifiedExtendedDateTime.Month[0] == 'u') { monthStartBuffer.Add('0'); monthEndBuffer.Add('1'); isFirstMonthDigitUnspecified = true; } else { monthStartBuffer.Add(unspecifiedExtendedDateTime.Month[0]); monthEndBuffer.Add(unspecifiedExtendedDateTime.Month[0]); } if (unspecifiedExtendedDateTime.Month[1] == 'u') { if (isFirstMonthDigitUnspecified) { monthStartBuffer.Add('1'); monthEndBuffer.Add('2'); } else { var firstDigit = int.Parse(unspecifiedExtendedDateTime.Month[0].ToString()); if (firstDigit == 0) { monthStartBuffer.Add('1'); monthEndBuffer.Add('9'); } else if (firstDigit == 1) { monthStartBuffer.Add('0'); monthEndBuffer.Add('2'); } else { throw new ConversionException("A month must be between 1 and 12."); } } } else { if (isFirstMonthDigitUnspecified) { var secondDigit = int.Parse(unspecifiedExtendedDateTime.Month[1].ToString()); if (secondDigit > 2) // Month must be in the range of 01 to 09 { monthEndBuffer[0] = '0'; } } monthStartBuffer.Add(unspecifiedExtendedDateTime.Month[1]); monthEndBuffer.Add(unspecifiedExtendedDateTime.Month[1]); } var monthStart = int.Parse(new string(monthStartBuffer.ToArray())); var monthEnd = int.Parse(new string(monthEndBuffer.ToArray())); if (unspecifiedExtendedDateTime.Day == null) // Day { startExtendedDateTime.Year = yearStart; startExtendedDateTime.Month = monthStart; endExtendedDateTime.Year = yearEnd; endExtendedDateTime.Month = monthEnd; return(extendedDateTimePossibilityCollection); } if (unspecifiedExtendedDateTime.Day.Length != 2) { throw new ConversionException("A day must be two characters long."); } var dayStartBuffer = new List <char>(); var dayEndBuffer = new List <char>(); var daysInEndMonth = ExtendedDateTimeCalculator.DaysInMonth(yearEnd, monthEnd); var isFirstDayDigitUnspecified = false; if (unspecifiedExtendedDateTime.Day[0] == 'u') { dayStartBuffer.Add('0'); dayEndBuffer.Add(daysInEndMonth.ToString()[0]); isFirstDayDigitUnspecified = true; } else { dayStartBuffer.Add(unspecifiedExtendedDateTime.Day[0]); dayEndBuffer.Add(unspecifiedExtendedDateTime.Day[0]); } if (unspecifiedExtendedDateTime.Day[1] == 'u') { if (isFirstDayDigitUnspecified) { dayStartBuffer.Add('1'); dayEndBuffer.Add(daysInEndMonth.ToString()[1]); } else { var firstDigit = int.Parse(unspecifiedExtendedDateTime.Day[0].ToString()); if (firstDigit == 0) // Day is 01 to 09 { dayStartBuffer.Add('1'); dayEndBuffer.Add('9'); } else if (firstDigit == 1) // Day is 10 to 19 { dayStartBuffer.Add('0'); dayEndBuffer.Add('9'); } else if (firstDigit == 2) // Day is 20 to 28 (if end month is February in a non-leap year) or 29 { dayStartBuffer.Add('0'); if (daysInEndMonth == 28) { dayEndBuffer.Add('8'); } else { dayEndBuffer.Add('9'); } } else if (firstDigit == 3) // Day is 30 to 30 or 31 (depending on end month) { dayStartBuffer.Add('0'); if (daysInEndMonth == 30) { dayEndBuffer.Add('0'); } else { dayEndBuffer.Add('1'); } } else { throw new ConversionException("A day must be between 1 and either 28, 29, 30, or 31 depending on the month."); } } } else { if (isFirstDayDigitUnspecified) { var secondDigit = int.Parse(unspecifiedExtendedDateTime.Day[1].ToString()); if (secondDigit > daysInEndMonth.ToString()[1]) // Decrement the first digit of the end day. { dayEndBuffer[0] = (int.Parse(dayEndBuffer[0].ToString()) - 1).ToString()[0]; } } dayStartBuffer.Add(unspecifiedExtendedDateTime.Day[1]); dayEndBuffer.Add(unspecifiedExtendedDateTime.Day[1]); } var dayStart = int.Parse(new string(dayStartBuffer.ToArray())); var dayEnd = int.Parse(new string(dayEndBuffer.ToArray())); var rangeBuffer = new List <ExtendedDateTime>(); // Collects consecutive dates, which are then converted into an ExtendedDateTimeRange. extendedDateTimePossibilityCollection.Clear(); for (var year = yearStart; year <= yearEnd; year++) { for (var month = monthStart; month <= monthEnd; month++) { for (var day = dayStart; day <= dayEnd; day++) { if (day > ExtendedDateTimeCalculator.DaysInMonth(year, month)) { if (rangeBuffer.Count == 1) { extendedDateTimePossibilityCollection.Add(new ExtendedDateTime(year, month, day)); rangeBuffer.Clear(); } else if (rangeBuffer.Count > 0) { extendedDateTimePossibilityCollection.Add(new ExtendedDateTimeRange(rangeBuffer.First(), rangeBuffer.Last())); rangeBuffer.Clear(); } } else { rangeBuffer.Add(new ExtendedDateTime(year, month, day)); } if (day == dayEnd) { if (rangeBuffer.Count == 1) { extendedDateTimePossibilityCollection.Add(new ExtendedDateTime(year, month, day)); rangeBuffer.Clear(); } else if (rangeBuffer.Count > 0) { extendedDateTimePossibilityCollection.Add(new ExtendedDateTimeRange(rangeBuffer.First(), rangeBuffer.Last())); rangeBuffer.Clear(); } } } } } return(extendedDateTimePossibilityCollection); }
private static void InsertFlags(ExtendedDateTime extendedDateTime, StringBuilder stringBuilder) // Combinations generated from http://www.mathsisfun.com/combinatorics/combinations-permutations-calculator.html { var da = extendedDateTime.DayFlags.HasFlag(DayFlags.Approximate); var du = extendedDateTime.DayFlags.HasFlag(DayFlags.Uncertain); var ma = extendedDateTime.MonthFlags.HasFlag(MonthFlags.Approximate); var mu = extendedDateTime.MonthFlags.HasFlag(MonthFlags.Uncertain); var sa = extendedDateTime.SeasonFlags.HasFlag(SeasonFlags.Approximate); var su = extendedDateTime.SeasonFlags.HasFlag(SeasonFlags.Uncertain); var ya = extendedDateTime.YearFlags.HasFlag(YearFlags.Approximate); var yu = extendedDateTime.YearFlags.HasFlag(YearFlags.Uncertain); var de = string.Empty; var ds = string.Empty; var me = string.Empty; var ms = string.Empty; var se = string.Empty; var ss = string.Empty; var ye = string.Empty; var ys = string.Empty; if (yu && ya && mu && ma && du && da) { de = "?~"; } else if (yu && ya && mu && ma && du) { me = "~"; de = "?"; } else if (yu && ya && mu && ma && da) { me = "?"; de = "~"; } else if (yu && ya && mu && du && da) { ms = "("; me = ")?"; de = "?~"; } else if (yu && ya && ma && du && da) { ms = "("; me = ")~"; de = "?~"; } else if (yu && mu && ma && du && da) { ys = "("; ye = ")?"; de = "?~"; } else if (ya && mu && ma && du && da) { ys = "("; ye = ")~"; de = "?~"; } else if (yu && ya && mu && ma) { me = "?~"; } else if (yu && ya && ma && da) { ye = "?"; de = "~"; } else if (yu && mu && du && da) { ys = "("; ds = "("; de = ")~)?"; } else if (ya && mu && du && da) { ye = ")"; ms = "("; ds = "("; de = ")~)?"; } else if (yu && ya && mu && du) { ye = "~"; de = "?"; } else if (yu && ya && du && da) { de = "?~"; ms = "("; me = ")"; } else if (yu && ma && du && da) { ye = "?"; ms = "("; ds = "("; de = ")?)~"; } else if (ya && mu && du && da) { ye = "~"; ms = "("; me = ")?"; ds = "("; de = ")?~"; } else if (yu && ya && mu && da) { ye = "?"; ms = "("; me = ")?"; de = "?"; } else if (yu && mu && ma && du) { ys = "("; ms = "("; me = ")~"; de = ")?"; } else if (mu && ma && du && da) { ys = "("; ye = ")"; } else if (yu && ya && ma && du) { ye = "?"; me = "~"; ds = "("; de = ")?"; } else if (yu && mu && ma && da) { ye = "?"; ms = "("; me = "?"; de = ")~"; } else if (ya && mu && ma && da) { ys = "("; ms = "("; me = ")?"; de = ")~"; } else if (yu && ya && su && sa) { se = "?~"; } else if (yu && ya && mu) { ye = "~"; me = "?"; } else if (yu && mu && du) { de = "?"; } else if (ya && mu && ma) { ys = "("; ye = ")~"; me = "?~"; } else if (ya && du && da) { ys = "("; ye = ")~"; ds = "("; de = ")?~"; } else if (yu && ya && ma) { ye = "?"; me = "~"; } else if (yu && mu && da) { me = "?"; ds = "("; de = ")~"; } else if (ya && mu && du) { ys = "("; ye = ")~"; de = "?"; } else if (mu && ma && du) { ms = "("; me = "~"; de = ")?"; } else if (yu && ya && du) { ye = "~"; ms = "("; me = ")"; de = "?"; } else if (yu && ma && du) { ms = "("; me = ")~"; de = "?"; } else if (ya && mu && da) { ms = "("; me = ")?"; de = "~"; } else if (mu && ma && da) { ms = "("; me = "?"; de = ")~"; } else if (yu && ya && da) { ye = "?"; ms = "("; me = ")"; de = "~"; } else if (yu && ma && da) { ys = "("; ye = ")?"; de = "~"; } else if (ya && ma && du) { me = "~"; ds = "("; de = ")?"; } else if (mu && du && da) { ms = "("; me = ")?"; ds = "("; de = ")?~"; } else if (yu && mu && ma) { ye = "?"; ms = "("; me = ")?~"; } else if (yu && du && da) { ye = "?"; ds = "("; de = ")?~"; } else if (ya && ma && da) { de = "~"; } else if (ma && du && da) { ms = ")"; me = ")~"; ds = "("; de = ")?~"; } else if (yu && ya && su) { ye = "~"; se = "?"; } else if (yu && ya && sa) { ye = "?"; se = "~"; } else if (yu && su && sa) { ye = "?"; ss = "("; se = ")?~"; } else if (ya && sa && su) { ye = "~"; ss = "("; se = ")?~"; } else if (yu && ya) { ye = "?~"; } else if (yu && mu) { me = "?"; } else if (yu && ma) { ye = "?"; ms = "("; me = ")~"; } else if (yu && du) { ms = "("; me = ")"; de = "?"; } else if (yu && da) { ye = "?"; ds = "("; de = ")~"; } else if (ya && mu) { ye = "~"; ms = "("; me = ")?"; } else if (ya && ma) { me = "~"; } else if (ya && du) { ye = "~"; ds = "("; de = ")?"; } else if (ya && da) { ms = "("; me = ")"; de = "~"; } else if (mu && ma) { ms = "("; me = ")?~"; } else if (mu && du) { ys = "("; ye = ")"; de = "?"; } else if (mu && da) { ms = "("; me = ")?"; ds = "("; de = ")~"; } else if (ma && du) { ms = "("; me = ")~"; ds = "("; de = ")?"; } else if (ma && da) { ys = "("; ye = ")"; de = "~"; } else if (du && da) { ds = "("; de = ")?~"; } else if (yu && su) { se = "?"; } else if (yu && sa) { ye = "?"; ss = "("; se = ")~"; } else if (ya && su) { ye = "~"; ss = "("; se = ")?"; } else if (ya && sa) { se = "~"; } else if (su && sa) { ss = "("; se = ")?~"; } else if (yu) { ye = "?"; } else if (ya) { ye = "~"; } else if (su) { ss = "("; se = ")?"; } else if (sa) { ss = "("; se = ")~"; } else if (mu) { ms = "("; me = ")?"; } else if (ma) { ms = "("; me = ")~"; } else if (du) { ds = "("; de = ")?"; } else if (da) { ds = "("; de = ")~"; } stringBuilder .Replace("{ds}", ds) .Replace("{de}", de) .Replace("{ms}", ms) .Replace("{me}", me) .Replace("{ss}", ss) .Replace("{se}", se) .Replace("{ys}", ys) .Replace("{ye}", ye); }
internal static string Serialize(ExtendedDateTime extendedDateTime) { if (extendedDateTime.IsUnknown) { return("unknown"); } if (extendedDateTime.IsOpen) { return("open"); } var stringBuilder = new StringBuilder(); stringBuilder.Append("{ys}"); if (extendedDateTime.Year > 9999 || extendedDateTime.Year < -9999 || extendedDateTime.YearExponent.HasValue) // The year must be in long form. { stringBuilder.Append('y'); } if (extendedDateTime.YearExponent.HasValue) { stringBuilder.Append(extendedDateTime.Year); } else { stringBuilder.AppendFormat("{0:D4}", extendedDateTime.Year); } stringBuilder.Append("{ye}"); if (extendedDateTime.YearExponent.HasValue) { stringBuilder.Append('e').Append(extendedDateTime.YearExponent); } if (extendedDateTime.YearPrecision.HasValue) { stringBuilder.Append('p').Append(extendedDateTime.YearPrecision); } if (extendedDateTime.Month != 1 || extendedDateTime.Precision > ExtendedDateTimePrecision.Year) { stringBuilder.Append("-{ms}").AppendFormat("{0:D2}", extendedDateTime.Month).Append("{me}"); } if (extendedDateTime.Season != Season.Undefined) { stringBuilder.Append("-{ss}").Append((int)extendedDateTime.Season); if (extendedDateTime.SeasonQualifier != null) { stringBuilder.Append('^').Append(extendedDateTime.SeasonQualifier); } stringBuilder.Append("{se}"); } if (extendedDateTime.Day != 1 || extendedDateTime.Precision > ExtendedDateTimePrecision.Month) { stringBuilder.Append("-{ds}").AppendFormat("{0:D2}", extendedDateTime.Day); } stringBuilder.Append("{de}"); InsertFlags(extendedDateTime, stringBuilder); if (extendedDateTime.Hour != 0 || extendedDateTime.Precision > ExtendedDateTimePrecision.Day) { stringBuilder.AppendFormat("T{0:D2}", extendedDateTime.Hour); } if (extendedDateTime.Minute != 0 || extendedDateTime.Precision > ExtendedDateTimePrecision.Hour) { stringBuilder.AppendFormat(":{0:D2}", extendedDateTime.Minute); } if (extendedDateTime.Second != 0 || extendedDateTime.Precision > ExtendedDateTimePrecision.Minute) { stringBuilder.AppendFormat(":{0:D2}", extendedDateTime.Second); } if (extendedDateTime.Precision > ExtendedDateTimePrecision.Day) { if (extendedDateTime.UtcOffset.Hours == 0 && extendedDateTime.UtcOffset.Minutes == 0) { stringBuilder.Append('Z'); } else { if (extendedDateTime.UtcOffset.Hours < 0) { stringBuilder.Append('-'); } else { stringBuilder.Append('+'); } stringBuilder.AppendFormat("{0:D2}", Math.Abs(extendedDateTime.UtcOffset.Hours)); } if (extendedDateTime.UtcOffset.Minutes != 0) { stringBuilder.AppendFormat(":{0:D2}", extendedDateTime.UtcOffset.Minutes); } } return(stringBuilder.ToString()); }
protected override Size MeasureOverride(Size availableSize) { if (_hasViewChanged) { // In determining the labels to display, it must be kept in mind that the spacings between // the labels might be variable depending on the current time unit. For instance, if the // timeline is measured in months, then the labels will not be equally spaced apart because // a month is anywhere from 28 to 31 days. The solution is to produce labels one at a time // while the total accumulated width is less than the viewport width. // // 1. First we first convert the current horizontal offset into a TimeSpan, and add // that to the timeline's start time to get the time of the viewport's left edge. This // time may be more precise than the timeline's current unit of measurement. For // example, the viewport left time may be defined down to the second, but the timeline's // unit is an hour. Therefore, we must round up to the nearest unit. // // 2. The difference between the rounded date and the precise date is the time between // the start of the viewport and the initial label. Converting this TimeSpan into pixels // will yield the x position of the initial label relative to the viewport's left edge. // We can store this value along with the label number for use during the arranging process. // // 3. Now we can create a label, add it to the panel, and measure it. // // 4. Next we increment the rounded date by whatever is set as the timeline's unit. // // 5. Repeat steps 2 to 4 until the rounded date is equal to or exceeds the date of the // viewport's right edge. Children.Clear(); if (_labelOffsets == null) { _labelOffsets = new Dictionary<int, double>(); } else { _labelOffsets.Clear(); } ExtendedDateTime viewportLeftTime = Dates.Earliest() + Ruler.ToTimeSpan(_horizontalOffset); ExtendedDateTime viewportRightTime = Dates.Earliest() + Ruler.ToTimeSpan(_horizontalOffset + availableSize.Width); ExtendedDateTime labelTime = null; int labelIndex = 0; switch (Resolution) { case TimeResolution.Century: labelTime = new ExtendedDateTime(viewportLeftTime.Year - viewportLeftTime.Year % 100); break; case TimeResolution.Decade: labelTime = new ExtendedDateTime(viewportLeftTime.Year - viewportLeftTime.Year % 10); break; case TimeResolution.Year: labelTime = viewportLeftTime.ToRoundedPrecision(ExtendedDateTimePrecision.Year); break; case TimeResolution.Month: labelTime = viewportLeftTime.ToRoundedPrecision(ExtendedDateTimePrecision.Month, false); break; case TimeResolution.Day: labelTime = viewportLeftTime.ToRoundedPrecision(ExtendedDateTimePrecision.Day, false); break; case TimeResolution.Hour: labelTime = viewportLeftTime.ToRoundedPrecision(ExtendedDateTimePrecision.Hour, false); break; case TimeResolution.Minute: labelTime = viewportLeftTime.ToRoundedPrecision(ExtendedDateTimePrecision.Minute, false); break; case TimeResolution.Second: labelTime = viewportLeftTime.ToRoundedPrecision(ExtendedDateTimePrecision.Second, false); break; default: break; } while (labelTime < viewportRightTime) { var label = new TextBlock(); label.Text = labelTime.ToString(); label.FontFamily = FontFamily; label.FontSize = FontSize; label.Foreground = Foreground; switch (Resolution) { case TimeResolution.Century: _labelOffsets.Add(labelIndex, Ruler.ToPixels(viewportLeftTime, labelTime) + LabelOffset); labelTime = labelTime.AddYears(100); break; case TimeResolution.Decade: _labelOffsets.Add(labelIndex, Ruler.ToPixels(viewportLeftTime, labelTime) + LabelOffset); labelTime = labelTime.AddYears(10); break; case TimeResolution.Year: _labelOffsets.Add(labelIndex, Ruler.ToPixels(viewportLeftTime, labelTime) + LabelOffset); labelTime = labelTime.AddYears(1); break; case TimeResolution.Month: _labelOffsets.Add(labelIndex, Ruler.ToPixels(viewportLeftTime, labelTime) + LabelOffset); labelTime = labelTime.AddMonths(1); break; case TimeResolution.Day: _labelOffsets.Add(labelIndex, Ruler.ToPixels(viewportLeftTime, labelTime) + LabelOffset); labelTime = labelTime + TimeSpan.FromDays(1); break; case TimeResolution.Hour: _labelOffsets.Add(labelIndex, Ruler.ToPixels(viewportLeftTime, labelTime) + LabelOffset); labelTime = labelTime + TimeSpan.FromHours(1); break; case TimeResolution.Minute: _labelOffsets.Add(labelIndex, Ruler.ToPixels(viewportLeftTime, labelTime) + LabelOffset); labelTime = labelTime + TimeSpan.FromMinutes(1); break; case TimeResolution.Second: _labelOffsets.Add(labelIndex, Ruler.ToPixels(viewportLeftTime, labelTime) + LabelOffset); labelTime = labelTime + TimeSpan.FromSeconds(1); break; default: break; } Children.Add(label); label.Measure(availableSize); labelIndex++; } _hasViewChanged = false; return availableSize; } foreach (UIElement child in Children) { child.Measure(availableSize); } return availableSize; }
internal static ExtendedDateTime ToRoundedPrecision(ExtendedDateTime e, ExtendedDateTimePrecision p, bool roundUp = false) { var year = e.Year; var month = e.Month; var day = e.Day; var hour = e.Hour; var minute = e.Minute; var second = e.Second; if (p < ExtendedDateTimePrecision.Second) { second = 0; } if (p < ExtendedDateTimePrecision.Minute) { minute = 0; } if (p < ExtendedDateTimePrecision.Hour) { hour = 0; } if (p < ExtendedDateTimePrecision.Day) { day = 1; } if (p < ExtendedDateTimePrecision.Month) { month = 1; } if (roundUp) { switch (p) { case ExtendedDateTimePrecision.Year: if (e.Month > 1) { year++; } break; case ExtendedDateTimePrecision.Month: if (e.Day > 1) { month++; } break; case ExtendedDateTimePrecision.Day: if (e.Hour > 0) { day++; } break; case ExtendedDateTimePrecision.Hour: if (e.Minute > 0) { hour++; } break; case ExtendedDateTimePrecision.Minute: if (e.Second > 0) { minute++; } break; case ExtendedDateTimePrecision.Second: break; } if (second > 59) { second = 0; minute++; } if (minute > 59) { minute = 0; hour++; } if (hour > 23) { hour = 0; day++; } if (day > DaysInMonth(year, month == 13 ? 1 : month)) { day = 1; month++; } if (month > 12) { month = 1; year++; } } var extendedDateTime = new ExtendedDateTime(year, month, day, hour, minute, second, e.UtcOffset.Hours, e.UtcOffset.Minutes); extendedDateTime.YearFlags = e.YearFlags; extendedDateTime.MonthFlags = e.MonthFlags; extendedDateTime.DayFlags = e.DayFlags; extendedDateTime.Precision = p; return(extendedDateTime); }
private static void CommitTimeComponent(ref int timeComponentIndex, bool timeZonePart, List <char> componentBuffer, ref ExtendedDateTime extendedDateTime) { if (componentBuffer.Count == 0) { return; } var componentString = new string(componentBuffer.ToArray()); if (timeComponentIndex == 0 && !timeZonePart) // We expect hours to appear first. { if (componentString.Any(c => !char.IsDigit(c))) { throw new ParseException("The hour must be a number.", componentString); } else if (componentString.Length != 2 && !(componentString.Length == 3 && componentString.StartsWith("-"))) { throw new ParseException("The hour must be two digits long.", componentString); } extendedDateTime.Hour = int.Parse(componentString); extendedDateTime.Precision++; timeComponentIndex++; } else if (timeComponentIndex == 1 && !timeZonePart) // We expect minutes to appear second. { if (componentString.Any(c => !char.IsDigit(c))) { throw new ParseException("The minute must be a number.", componentString); } else if (componentString.Length != 2) { throw new ParseException("The minute must be two digits long.", componentString); } extendedDateTime.Minute = int.Parse(componentString); extendedDateTime.Precision++; timeComponentIndex++; } else if (timeComponentIndex == 2 && !timeZonePart) // We expect seconds to appear third. { if (componentString.Any(c => !char.IsDigit(c))) { throw new ParseException("The second must be a number.", componentString); } else if (componentString.Length != 2) { throw new ParseException("The second must be two digits long.", componentString); } extendedDateTime.Second = int.Parse(componentString); extendedDateTime.Precision++; timeComponentIndex++; } else if (timeZonePart) { if (componentString.StartsWith("Z")) { extendedDateTime.UtcOffset = TimeSpan.Zero; } else if (componentString.StartsWith("+") || componentString.StartsWith("-")) // It must be a non-UTC time zone offset. { var timeZoneOffsetComponentStrings = componentString.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries); if (timeZoneOffsetComponentStrings.Length == 0) { throw new ParseException("The time zone offset must have at least two digits.", componentString); } var hourOffsetString = timeZoneOffsetComponentStrings[0]; // Time zone hours offset. if (hourOffsetString.Any(c => !char.IsDigit(c) && c != '+' && c != '-')) { throw new ParseException("The time zone hour offset must be a number.", hourOffsetString); } if (hourOffsetString.StartsWith("+") || hourOffsetString.StartsWith("-")) { if (hourOffsetString.Length != 3) { throw new ParseException("The time zone hour offset must have exactly two digits.", hourOffsetString); } } else if (hourOffsetString.Length != 2) { throw new ParseException("The time zone hour offset must have exactly two digits.", hourOffsetString); } extendedDateTime.UtcOffset = TimeSpan.FromHours(double.Parse(hourOffsetString)); if (timeZoneOffsetComponentStrings.Length == 2) // Optional time zone minutes offset. { var minuteOffsetString = timeZoneOffsetComponentStrings[1]; if (minuteOffsetString.Any(c => !char.IsDigit(c))) { throw new ParseException("The time zone minute offset must be a number.", minuteOffsetString); } else if (minuteOffsetString.Length != 2) { throw new ParseException("The time zone minute offset must have exactly two digits.", minuteOffsetString); } extendedDateTime.UtcOffset += TimeSpan.FromMinutes(double.Parse(minuteOffsetString)); } if (timeZoneOffsetComponentStrings.Length > 2) { throw new ParseException("The time zone offset can have at most two components: hours and minutes.", componentString); } } } else { throw new ParseException("The time can have at most three components excluding the time zone.", componentString); } componentBuffer.Clear(); }
internal static ExtendedDateTime Parse(string extendedDateTimeString, ExtendedDateTime extendedDateTime = null) { if (string.IsNullOrWhiteSpace(extendedDateTimeString)) { throw new ArgumentNullException("extendedDateTimeString"); } if (extendedDateTime == null) { extendedDateTime = new ExtendedDateTime(); } InsertArtificialScopes(ref extendedDateTimeString); // e.g. 1995-11?-12~ => {{1995-11}?-12}~ var componentBuffer = new List <char>(); var isDatePart = true; var isTimeZonePart = false; var isSeasonQualifierPart = false; var currentDateComponent = 0; var currentTimeComponent = 0; var hasSeasonComponent = false; if (string.IsNullOrEmpty(extendedDateTimeString)) { throw new ParseException("The input string cannot be empty.", extendedDateTimeString); } for (int i = 0; i < extendedDateTimeString.Length; i++) { var character = extendedDateTimeString[i]; if (isDatePart) // Parsing date portion of extended date time. { if (character == '(' || character == '{') // Scope increment for natural and artificial scopes. { CommitDateComponent(ref currentDateComponent, ref hasSeasonComponent, GetScopeFlags(i - 1, extendedDateTimeString), componentBuffer, ref extendedDateTime); } else if (character == ')' || character == '}') // Scope decrement for natural and artificial scopes. { CommitDateComponent(ref currentDateComponent, ref hasSeasonComponent, GetScopeFlags(i - 1, extendedDateTimeString), componentBuffer, ref extendedDateTime); if (i + 1 < extendedDateTimeString.Length && GetFlag(extendedDateTimeString[i + 1]) != 0) { i++; // Skip past first flag if exists. if (i + 1 < extendedDateTimeString.Length && GetFlag(extendedDateTimeString[i + 1]) != 0) { i++; // Skip past second flag if exists. } } } else if (char.IsDigit(character) || character == 'y' || character == 'e' || character == 'p') { componentBuffer.Add(character); } else if (character == '^') // Add season qualifier indicator to component buffer. { componentBuffer.Add(character); isSeasonQualifierPart = true; } else if (character == '-') { if (i == 0 || (i > 0 && extendedDateTimeString[i - 1] == 'y')) // Hyphen is a negative sign. { componentBuffer.Add(character); } else // Hyphen is a component separator. { CommitDateComponent(ref currentDateComponent, ref hasSeasonComponent, GetScopeFlags(i - 1, extendedDateTimeString), componentBuffer, ref extendedDateTime); } } else if (character == 'T') { CommitDateComponent(ref currentDateComponent, ref hasSeasonComponent, GetScopeFlags(i - 1, extendedDateTimeString), componentBuffer, ref extendedDateTime); isDatePart = false; } else if (isSeasonQualifierPart) { if (char.IsWhiteSpace(character)) { throw new ParseException("Season qualifiers cannot contain whitespace.", new string(componentBuffer.ToArray())); } componentBuffer.Add(character); } else { throw new ParseException("The character \'" + character + "\' could not be recognized.", new string(componentBuffer.ToArray())); } } else // Parsing time portion of extended date time. { if (char.IsDigit(character) || (character == ':' && isTimeZonePart)) // Add digit to component buffer. { componentBuffer.Add(character); } else if (character == ':' && !isTimeZonePart) { CommitTimeComponent(ref currentTimeComponent, isTimeZonePart, componentBuffer, ref extendedDateTime); } else if (character == 'Z' || character == '+' || character == '-') // Time zone component { CommitTimeComponent(ref currentTimeComponent, isTimeZonePart, componentBuffer, ref extendedDateTime); componentBuffer.Add(character); isTimeZonePart = true; } else { throw new ParseException("The character \'" + character + "\' could not be recognized.", new string(componentBuffer.ToArray())); } } } if (isDatePart) { CommitDateComponent(ref currentDateComponent, ref hasSeasonComponent, GetScopeFlags(extendedDateTimeString.Length - 1, extendedDateTimeString), componentBuffer, ref extendedDateTime); } else { CommitTimeComponent(ref currentTimeComponent, isTimeZonePart, componentBuffer, ref extendedDateTime); } return(extendedDateTime); }
protected override Size MeasureOverride(Size availableSize) { if (_hasViewChanged) { // In determining the number of guidelines to display, it must be kept in mind that // the spacings between the lines might be variable depending on the // current time unit. For instance, if the timeline is measured in months, then the // lines will not be equally spaced apart because a month is anywhere from 28 to 31 days. // The solution is to produce lines one at a time while the total accumulated width is // less than the viewport width. // // 1. First we first convert the current horizontal offset into a TimeSpan, and add // that to the timeline's start time to get the time of the viewport's left edge. This // time may be more precise than the timeline's current unit of measurement. For // example, the viewport left time may be defined down to the second, but the timeline's // unit is an hour. Therefore, we must round up to the nearest unit. // // 2. The difference between the rounded date and the precise date is the time between // the start of the viewport and the initial line. Converting this TimeSpan into pixels // will yield the x position of the initial line relative to the viewport's left edge. // We can store this value along with the line number for use during the arranging process. // // 3. Now we can create a guideline, add it to the panel, and measure it. // // 4. Next we increment the rounded date by whatever is set as the timeline's unit. // // 5. Repeat steps 2 to 4 until the rounded date is equal to or exceeds the date of the // viewport's right edge. Children.Clear(); if (_lineOffsets == null) { _lineOffsets = new Dictionary<int, double>(); } else { _lineOffsets.Clear(); } ExtendedDateTime viewportLeftTime = Dates.Earliest() + Ruler.ToTimeSpan(_horizontalOffset); ExtendedDateTime viewportRightTime = Dates.Earliest() + Ruler.ToTimeSpan(_horizontalOffset + availableSize.Width); ExtendedDateTime guidelineTime = null; int guidelineIndex = 0; switch (Resolution) { case TimeResolution.Century: guidelineTime = new ExtendedDateTime(viewportLeftTime.Year - viewportLeftTime.Year % 100 + 100); break; case TimeResolution.Decade: guidelineTime = new ExtendedDateTime(viewportLeftTime.Year - viewportLeftTime.Year % 10 + 10); break; case TimeResolution.Year: guidelineTime = viewportLeftTime.ToRoundedPrecision(ExtendedDateTimePrecision.Year, true); break; case TimeResolution.Month: guidelineTime = viewportLeftTime.ToRoundedPrecision(ExtendedDateTimePrecision.Month, true); break; case TimeResolution.Day: guidelineTime = viewportLeftTime.ToRoundedPrecision(ExtendedDateTimePrecision.Day, true); break; case TimeResolution.Hour: guidelineTime = viewportLeftTime.ToRoundedPrecision(ExtendedDateTimePrecision.Hour, true); break; case TimeResolution.Minute: guidelineTime = viewportLeftTime.ToRoundedPrecision(ExtendedDateTimePrecision.Minute, true); break; case TimeResolution.Second: guidelineTime = viewportLeftTime.ToRoundedPrecision(ExtendedDateTimePrecision.Second, true); break; default: break; } while (guidelineTime < viewportRightTime) { var guideline = new Line(); guideline.Y2 = availableSize.Height; guideline.StrokeThickness = 1; guideline.UseLayoutRounding = true; guideline.SnapsToDevicePixels = true; switch (Resolution) { case TimeResolution.Century: _lineOffsets.Add(guidelineIndex, Ruler.ToPixels(viewportLeftTime, guidelineTime)); guideline.Stroke = guidelineTime.Year % (100 * MajorFrequency) == 0 ? MajorBrush : MinorBrush; guidelineTime = guidelineTime.AddYears(100); break; case TimeResolution.Decade: _lineOffsets.Add(guidelineIndex, Ruler.ToPixels(viewportLeftTime, guidelineTime)); guideline.Stroke = guidelineTime.Year % (10 * MajorFrequency) == 0 ? MajorBrush : MinorBrush; guidelineTime = guidelineTime.AddYears(10); break; case TimeResolution.Year: _lineOffsets.Add(guidelineIndex, Ruler.ToPixels(viewportLeftTime, guidelineTime)); guideline.Stroke = guidelineTime.Year % MajorFrequency == 0 ? MajorBrush : MinorBrush; guidelineTime = guidelineTime.AddYears(1); break; case TimeResolution.Month: _lineOffsets.Add(guidelineIndex, Ruler.ToPixels(viewportLeftTime, guidelineTime)); guideline.Stroke = guidelineTime.Month % MajorFrequency == 0 ? MajorBrush : MinorBrush; try { guidelineTime = guidelineTime.AddMonths(1); } catch (Exception) { var month = guidelineTime.Month + 1; var year = guidelineTime.Year; if (month > 12) { month -= 12; year++; } var day = ExtendedDateTimeCalculator.DaysInMonth(year, month); guidelineTime = new ExtendedDateTime(year, month, day, guidelineTime.Hour, guidelineTime.Minute, guidelineTime.Second, guidelineTime.UtcOffset.Hours, guidelineTime.UtcOffset.Minutes); } break; case TimeResolution.Day: _lineOffsets.Add(guidelineIndex, Ruler.ToPixels(viewportLeftTime, guidelineTime)); guideline.Stroke = guidelineTime.Day % MajorFrequency == 0 ? MajorBrush : MinorBrush; guidelineTime = guidelineTime + TimeSpan.FromDays(1); break; case TimeResolution.Hour: _lineOffsets.Add(guidelineIndex, Ruler.ToPixels(viewportLeftTime, guidelineTime)); guideline.Stroke = guidelineTime.Hour % MajorFrequency == 0 ? MajorBrush : MinorBrush; guidelineTime = guidelineTime + TimeSpan.FromHours(1); break; case TimeResolution.Minute: _lineOffsets.Add(guidelineIndex, Ruler.ToPixels(viewportLeftTime, guidelineTime)); guideline.Stroke = guidelineTime.Minute % MajorFrequency == 0 ? MajorBrush : MinorBrush; guidelineTime = guidelineTime + TimeSpan.FromMinutes(1); break; case TimeResolution.Second: _lineOffsets.Add(guidelineIndex, Ruler.ToPixels(viewportLeftTime, guidelineTime)); guideline.Stroke = guidelineTime.Second % MajorFrequency == 0 ? MajorBrush : MinorBrush; guidelineTime = guidelineTime + TimeSpan.FromSeconds(1); break; default: break; } Children.Add(guideline); guideline.Measure(availableSize); guidelineIndex++; } _hasViewChanged = false; return availableSize; } foreach (UIElement child in Children) { child.Measure(availableSize); } return availableSize; }