usingSystem.Text;/// <summary>/// Compares a supplied date to the current date and generates a friendly English /// comparison ("5 days ago", "5 days from now")/// </summary>/// <param name="date">The date to convert</param>/// <param name="approximate">When off, calculate timespan down to the second./// When on, approximate to the largest round unit of time.</param>/// <returns></returns>publicstaticstringToRelativeDateString(thisDateTimevalue,bool approximate){StringBuilder sb =newStringBuilder();string suffix =(value> DateTime.Now)?" from now":" ago";TimeSpan timeSpan =newTimeSpan(Math.Abs(DateTime.Now.Subtract(value).Ticks));if(timeSpan.Days >0){sb.AppendFormat("{0} {1}", timeSpan.Days,(timeSpan.Days >1)?"days":"day");if(approximate)return sb.ToString()+ suffix;}if(timeSpan.Hours >0){sb.AppendFormat("{0}{1} {2}",(sb.Length >0)?", ":string.Empty,timeSpan.Hours,(timeSpan.Hours >1)?"hours":"hour");if(approximate)return sb.ToString()+ suffix;}if(timeSpan.Minutes >0){sb.AppendFormat("{0}{1} {2}",(sb.Length >0)?", ":string.Empty, timeSpan.Minutes,(timeSpan.Minutes >1)?"minutes":"minute");if(approximate)return sb.ToString()+ suffix;}if(timeSpan.Seconds >0){sb.AppendFormat("{0}{1} {2}",(sb.Length >0)?", ":string.Empty, timeSpan.Seconds,(timeSpan.Seconds >1)?"seconds":"second");if(approximate)return sb.ToString()+ suffix;}if(sb.Length ==0)return"right now";sb.Append(suffix);return sb.ToString();}
staticreadonlySortedList<double, Func<TimeSpan,string>> offsets =newSortedList<double, Func<TimeSpan,string>>{{0.75, _ =>"less than a minute"},{1.5, _ =>"about a minute"},{45, x =>$"{x.TotalMinutes:F0} minutes"},{90, x =>"about an hour"},{1440, x =>$"about {x.TotalHours:F0} hours"},{2880, x =>"a day"},{43200, x =>$"{x.TotalDays:F0} days"},{86400, x =>"about a month"},{525600, x =>$"{x.TotalDays /30:F0} months"},{1051200, x =>"about a year"},{double.MaxValue, x =>$"{x.TotalDays /365:F0} years"}};publicstaticstringToRelativeDate(thisDateTime input){TimeSpan x = DateTime.Now - input;string Suffix = x.TotalMinutes >0?" ago":" from now";x =newTimeSpan(Math.Abs(x.Ticks));return offsets.First(n => x.TotalMinutes < n.Key).Value(x)+ Suffix;}