/// <summary> /// Calculates a DateTimeInterval from two DateTime values and rounds it to a resolution of /// not more than 2 levels. /// </summary> /// <param name="start">Start time of the interval.</param> /// <param name="end">End time of the interval.</param> /// <returns></returns> private static DateTimeInterval GetRoundedInterval(DateTime start, DateTime end) { // Round difference to seconds, update end time const long ticksPerSecond = 10000000; long newEnd = (long)Math.Round((double)(end.Ticks - start.Ticks) / ticksPerSecond) * ticksPerSecond; end = new DateTime(start.Ticks + newEnd); DateTimeInterval interval = new DateTimeInterval(start, end); // Snap to grid // Only snap a unit downwards if there is a greater unit. if ((interval.Years != 0 || interval.Months != 0 || interval.Days != 0 || interval.Hours != 0 || interval.Minutes != 0) && interval.Seconds <= 3) { interval.Seconds = 0; } else if (interval.Seconds >= 57) { interval.Minutes++; interval.Seconds = 0; } if ((interval.Years != 0 || interval.Months != 0 || interval.Days != 0 || interval.Hours != 0) && interval.Minutes <= 3) { interval.Minutes = 0; } else if (interval.Minutes >= 57) { interval.Hours++; interval.Minutes = 0; } if ((interval.Years != 0 || interval.Months != 0 || interval.Days != 0) && interval.Hours <= 1) { interval.Hours = 0; } else if (interval.Hours >= 23) { interval.Days++; interval.Hours = 0; } if ((interval.Years != 0 || interval.Months != 0) && interval.Days <= 2) { interval.Days = 0; } else if (interval.Days >= 28) { interval.Months++; interval.Days = 0; } // (Months are not snapped) // Decrease resolution over time, also ensure that only one smaller unit is set if (interval.Years >= 5) { if (interval.Months >= 7) interval.Years++; interval.Months = 0; } if (interval.Years >= 1) { interval.Days = 0; interval.Hours = 0; interval.Minutes = 0; interval.Seconds = 0; } if (interval.Months >= 5) { if (interval.Days >= 16) interval.Months++; interval.Days = 0; } if (interval.Months >= 1) { interval.Hours = 0; interval.Minutes = 0; interval.Seconds = 0; } if (interval.Days >= 5) { if (interval.Hours >= 12) interval.Days++; interval.Hours = 0; } if (interval.Days >= 1) { interval.Minutes = 0; interval.Seconds = 0; } if (interval.Hours >= 5) { if (interval.Minutes >= 30) interval.Hours++; interval.Minutes = 0; } if (interval.Hours >= 1) { interval.Seconds = 0; } if (interval.Minutes >= 5) { if (interval.Seconds >= 30) interval.Minutes++; interval.Seconds = 0; } return interval; }
/// <summary> /// Formats a DateTimeInterval value with the specified set of text keys for a specific /// grammatical situation. /// </summary> /// <param name="interval">Interval data.</param> /// <param name="keys">Text keys for years, months, days, hours, minutes and seconds.</param> /// <param name="singleSpecial">Specifies whether single-unit values are more verbose ("a day" instead of "1 day").</param> /// <returns></returns> private static string FormatTimeInterval(DateTimeInterval interval, string[] keys, bool singleSpecial) { // First count the number of levels we will have int levelCount = 0; if (interval.Years > 0) levelCount++; if (interval.Months > 0) levelCount++; if (interval.Days > 0) levelCount++; if (interval.Hours > 0) levelCount++; if (interval.Minutes > 0) levelCount++; if (interval.Seconds > 0) levelCount++; // If it's one, first try to use the wording for a single level if (levelCount == 1) { string suffix = singleSpecial ? ".single" : ""; if (interval.Years > 0) { string text = GetText(keys[0] + suffix, interval.Years); if (text != null) return ResolveData(text, keys[0] + suffix, interval.Years, (Dictionary<string, string>)null); } else if (interval.Months > 0) { string text = GetText(keys[1] + suffix, interval.Months); if (text != null) return ResolveData(text, keys[1] + suffix, interval.Months, (Dictionary<string, string>)null); } else if (interval.Days > 0) { string text = GetText(keys[2] + suffix, interval.Days); if (text != null) return ResolveData(text, keys[2] + suffix, interval.Days, (Dictionary<string, string>)null); } else if (interval.Hours > 0) { string text = GetText(keys[3] + suffix, interval.Hours); if (text != null) return ResolveData(text, keys[3] + suffix, interval.Hours, (Dictionary<string, string>)null); } else if (interval.Minutes > 0) { string text = GetText(keys[4] + suffix, interval.Minutes); if (text != null) return ResolveData(text, keys[4] + suffix, interval.Minutes, (Dictionary<string, string>)null); } else if (interval.Seconds > 0) { string text = GetText(keys[5] + suffix, interval.Seconds); if (text != null) return ResolveData(text, keys[5] + suffix, interval.Seconds, (Dictionary<string, string>)null); } } // Now use the regular texts for every other case List<string> levels = new List<string>(); if (interval.Years > 0 && levels.Count < 2) levels.Add(ResolveData(GetText(keys[0], interval.Years), keys[0], interval.Years, (Dictionary<string, string>)null)); if (interval.Months > 0 && levels.Count < 2) levels.Add(ResolveData(GetText(keys[1], interval.Months), keys[1], interval.Months, (Dictionary<string, string>)null)); if (interval.Days > 0 && levels.Count < 2) levels.Add(ResolveData(GetText(keys[2], interval.Days), keys[2], interval.Days, (Dictionary<string, string>)null)); if (interval.Hours > 0 && levels.Count < 2) levels.Add(ResolveData(GetText(keys[3], interval.Hours), keys[3], interval.Hours, (Dictionary<string, string>)null)); if (interval.Minutes > 0 && levels.Count < 2) levels.Add(ResolveData(GetText(keys[4], interval.Minutes), keys[4], interval.Minutes, (Dictionary<string, string>)null)); if (interval.Seconds > 0 && levels.Count < 2) levels.Add(ResolveData(GetText(keys[5], interval.Seconds), keys[5], interval.Seconds, (Dictionary<string, string>)null)); return string.Join(GetText(SystemKeys.TimeRelativeSeparator, " "), levels.ToArray()); }