Что делать, если вы хотите рассчитать относительное время с настоящего времени до будущего?
Джонни Д. Кано-Лефтовар -
2
moment.js - очень хорошая библиотека для разбора дат. Вы можете использовать ее (на стороне сервера или на стороне клиента), в зависимости от ваших потребностей. просто к сведению, потому что никто не упомянул об этом здесь
Я ненавижу такие константы со страстью. Это кому-нибудь кажется неправильным? Thread.Sleep(1 * MINUTE)? Потому что это неправильно в 1000 раз.
Роман Старков
31
const int SECOND = 1;Так странно, что секунда - это одна секунда.
Серьезный
62
Этот тип кода практически невозможно локализовать. Если ваше приложение должно оставаться только на английском языке, тогда хорошо. Но если вы перейдете на другие языки, вы возненавидите себя за то, что делаете логику подобным образом. Просто, чтобы вы знали ...
Ник Рейман
73
Я думаю, что если бы константы были переименованы, чтобы точно описать значение, которое в них заложено, было бы легче понять. Итак, SecondsPerMinute = 60; MinutesPerHour = 60; SecondsPerHour = MinutesPerHour * SecondsPerHour; и т. д. Просто назвать его MINUTE = 60 не позволяет читателю определить значение.
Slolife
14
Почему никто (кроме Джо) не заботится о неправильном значении «вчера» или «несколько дней назад» ??? Вчера это не часовой расчет, а ежедневный расчет. Так что да, это неправильный код, по крайней мере, в двух частых случаях.
Джефф, потому что Stack Overflow широко использует jQuery, я рекомендую плагин jquery.timeago .
Льготы:
Избегайте отметок времени от «1 минута назад», даже если страница была открыта 10 минут назад; Timeago обновляется автоматически.
Вы можете в полной мере использовать кэширование страниц и / или фрагментов в своих веб-приложениях, поскольку временные метки не рассчитываются на сервере.
Вы можете использовать микроформаты, как крутые дети.
Просто прикрепите его к своим временным меткам на DOM:
Seb, Если у вас отключен Javascript, то отображается строка, которую вы изначально поместили между тегами abbr. Как правило, это просто дата или время в любом формате, который вы пожелаете. Timeago грациозно ухудшается. Это не становится намного проще.
Хех, спасибо, Роб. Это нормально. Это едва заметно, особенно когда во время перехода изменяется только одно число, хотя на страницах SO много временных отметок. Я бы подумал, что он по крайней мере оценил бы преимущества кэширования страниц, даже если бы он решил избегать автообновлений. Я уверен, что Джефф мог бы оставить отзыв, чтобы улучшить плагин. Я беру утешение, зная, что такие сайты, как arstechnica.com, используют его.
Райан Макгири
19
@Rob Fonseca-Ensor - теперь я тоже плачу. Как происходит обновление раз в минуту, чтобы показывать точную информацию, каким-либо образом связанную с миганием текста раз в секунду?
Даниэль Эрвикер
25
Вопрос о C #, я не вижу, насколько уместен плагин jQuery.
«<48 * 60 * 60-е годы» - довольно необычное определение для «вчера». Если в среду 9 часов утра, неужели вы думаете о 9:01 утра в понедельник как о «вчера»? Я бы подумал, что алгоритм для вчерашнего дня или "n дней назад" следует рассмотреть до / после полуночи.
Джо
139
Компиляторы, как правило, довольно хороши в предварительном вычислении константных выражений, например, 24 * 60 * 60, поэтому вы можете напрямую использовать их вместо того, чтобы вычислять их как 86400 и помещать исходное выражение в комментарии
zvolkov
11
@bzlm Я думаю, что сделал для проекта, над которым я работал. Моя мотивация заключалась в том, чтобы предупредить других о том, что в этом примере кода были пропущены недели. Что касается того, как это сделать, мне это показалось довольно простым.
2010 года
9
Я думаю, что хорошим способом улучшить алгоритм является отображение 2 единиц, таких как «2 месяца 21 день назад», «1 час 40 минут назад» для повышения точности.
Евгений Левин
5
@ Джеффи, ты пропустил расчет для високосного года и связанных с ним проверок
Сабур Аван
92
publicstaticstringRelativeDate(DateTime theDate){Dictionary<long,string> thresholds =newDictionary<long,string>();int minute =60;int hour =60* minute;int day =24* hour;
thresholds.Add(60,"{0} seconds ago");
thresholds.Add(minute *2,"a minute ago");
thresholds.Add(45* minute,"{0} minutes ago");
thresholds.Add(120* minute,"an hour ago");
thresholds.Add(day,"{0} hours ago");
thresholds.Add(day *2,"yesterday");
thresholds.Add(day *30,"{0} days ago");
thresholds.Add(day *365,"{0} months ago");
thresholds.Add(long.MaxValue,"{0} years ago");long since =(DateTime.Now.Ticks- theDate.Ticks)/10000000;foreach(long threshold in thresholds.Keys){if(since < threshold){TimeSpan t =newTimeSpan((DateTime.Now.Ticks- theDate.Ticks));returnstring.Format(thresholds[threshold],(t.Days>365? t.Days/365:(t.Days>0? t.Days:(t.Hours>0? t.Hours:(t.Minutes>0? t.Minutes:(t.Seconds>0? t.Seconds:0))))).ToString());}}return"";}
Я предпочитаю эту версию за ее краткость и возможность добавлять новые точки галочки. Это может быть инкапсулировано с Latest()расширением Timespan вместо этого длинного 1 вкладыша, но для краткости публикации это подойдет.
Это исправляет час назад, 1 час назад, предоставляя час до 2 часов прошло
Я получаю все виды проблем с использованием этой функции, например, если вы издеваетесь над 'theDate = DateTime.Now.AddMinutes (-40);' Я получаю «40 часов назад», но с ответом рефакторикода Майкла, он возвращается правильно в «40 минут назад»?
GONeale
я думаю, что вы пропускаете ноль, попробуйте: давно с = (DateTime.Now.Ticks - theDate.Ticks) / 10000000;
Робнардо
8
Хм, хотя этот код может работать, он неверен и недопустим, если предположить, что порядок ключей в Словаре будет в определенном порядке. Словарь использует Object.GetHashCode (), который возвращает не long, а int !. Если вы хотите, чтобы они были отсортированы, вам следует использовать SortedList <long, string>. Что не так с пороговыми значениями, оцениваемыми в наборе if / else if /.../ else? Вы получаете такое же количество сравнений. К сведению, хеш-код на долгое время. MaxValue оказывается таким же, как int.MinValue!
CodeMonkeyKing
ОП забыл т. Дни> 30? t.Days / 30:
Ларс Холм Дженсен
Чтобы исправить проблему, упомянутую @CodeMonkeyKing, вы можете использовать SortedDictionaryвместо простого Dictionary: использование такое же, но оно гарантирует, что ключи отсортированы. Но даже в этом случае алгоритм имеет недостатки, поскольку RelativeDate(DateTime.Now.AddMonths(-3).AddDays(-3))возвращает «95 месяцев назад» , независимо от того, какой тип словаря вы используете, что является неправильным (он должен возвращать «3 месяца назад» или «4 месяца назад» в зависимости от того, какой порог вы используете » повторное использование) - даже если -3 не создает дату в прошлом году (я проверял это в декабре, поэтому в этом случае это не должно происходить).
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;stringSuffix= x.TotalMinutes>0?" ago":" from now";
x =newTimeSpan(Math.Abs(x.Ticks));return offsets.First(n => x.TotalMinutes< n.Key).Value(x)+Suffix;}
это очень хорошая IMO :) Это также может быть рефакторинг как метод расширения? может ли словарь стать статичным, поэтому он создается только один раз и на него ссылаются с тех пор и после?
Возможно, вы захотите вытащить этот словарь в поле, чтобы уменьшить количество экземпляров и GC-отток. Вы должны измениться Func<string>на Func<double>.
Дрю Ноакс
49
Вот реализация, которую я добавил в качестве метода расширения для класса DateTime, который обрабатывает как будущие, так и прошедшие даты и предоставляет параметр аппроксимации, который позволяет вам указать уровень детализации, который вы ищете («3 часа назад» против «3 часа, 23 минуты 12 секунд назад "):
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();}
дружеское примечание: на .net 4.5 или выше не устанавливайте полный Humanizer ... устанавливайте только Humanizer.Core его часть .. потому что другие языковые пакеты не поддерживаются в этой версии
Ahmad
Так полезно! Этот ответ должен быть намного выше в этом списке. Если бы у меня было 100 голосов, я бы отдал это за это. Судя по всему (из JS-land), поиск этого пакета был нелегким.
kumarharsh
29
@Джефф
ИМХО твое кажется немного длинным. Однако он кажется немного более устойчивым с поддержкой «вчера» и «лет». Но по моему опыту, когда это используется, человек, скорее всего, будет просматривать контент в первые 30 дней. Это только действительно жестокие люди, которые приходят после этого. Вот почему я обычно предпочитаю быть кратким и простым.
Это метод, который я сейчас использую на одном из моих сайтов. Это только возвращает относительный день, час, время. И тогда пользователь должен ударить по «назад» в выводе.
Пару лет опоздал на вечеринку, но у меня было требование сделать это как для прошлых, так и для будущих свиданий, поэтому я объединил Джеффа и Винсента в это. Это тернаритическая феерия! :)
publicstaticclassDateTimeHelper{privateconstint SECOND =1;privateconstint MINUTE =60* SECOND;privateconstint HOUR =60* MINUTE;privateconstint DAY =24* HOUR;privateconstint MONTH =30* DAY;/// <summary>/// Returns a friendly version of the provided DateTime, relative to now. E.g.: "2 days ago", or "in 6 months"./// </summary>/// <param name="dateTime">The DateTime to compare to Now</param>/// <returns>A friendly string</returns>publicstaticstringGetFriendlyRelativeTime(DateTime dateTime){if(DateTime.UtcNow.Ticks== dateTime.Ticks){return"Right now!";}bool isFuture =(DateTime.UtcNow.Ticks< dateTime.Ticks);var ts =DateTime.UtcNow.Ticks< dateTime.Ticks?newTimeSpan(dateTime.Ticks-DateTime.UtcNow.Ticks):newTimeSpan(DateTime.UtcNow.Ticks- dateTime.Ticks);double delta = ts.TotalSeconds;if(delta <1* MINUTE){return isFuture ?"in "+(ts.Seconds==1?"one second": ts.Seconds+" seconds"): ts.Seconds==1?"one second ago": ts.Seconds+" seconds ago";}if(delta <2* MINUTE){return isFuture ?"in a minute":"a minute ago";}if(delta <45* MINUTE){return isFuture ?"in "+ ts.Minutes+" minutes": ts.Minutes+" minutes ago";}if(delta <90* MINUTE){return isFuture ?"in an hour":"an hour ago";}if(delta <24* HOUR){return isFuture ?"in "+ ts.Hours+" hours": ts.Hours+" hours ago";}if(delta <48* HOUR){return isFuture ?"tomorrow":"yesterday";}if(delta <30* DAY){return isFuture ?"in "+ ts.Days+" days": ts.Days+" days ago";}if(delta <12* MONTH){int months =Convert.ToInt32(Math.Floor((double)ts.Days/30));return isFuture ?"in "+(months <=1?"one month": months +" months"): months <=1?"one month ago": months +" months ago";}else{int years =Convert.ToInt32(Math.Floor((double)ts.Days/365));return isFuture ?"in "+(years <=1?"one year": years +" years"): years <=1?"one year ago": years +" years ago";}}}
Учитывая, что мир и ее муж, похоже, публикуют примеры кода, вот что я написал некоторое время назад, основываясь на нескольких из этих ответов.
У меня была особая необходимость, чтобы этот код был локализуемым. Итак, у меня есть два класса - Grammar, которые определяют локализуемые термины, и FuzzyDateExtensionsкоторые содержат несколько методов расширения. Мне не нужно было иметь дело с будущими датами, поэтому я не пытался справиться с ними с помощью этого кода.
Я оставил некоторые XML-документы в источнике, но ради краткости удалил большинство (где они были бы очевидны). Я также не включил каждого ученика здесь:
publicclassGrammar{/// <summary> Gets or sets the term for "just now". </summary>publicstringJustNow{get;set;}/// <summary> Gets or sets the term for "X minutes ago". </summary>/// <remarks>/// This is a <see cref="String.Format"/> pattern, where <c>{0}</c>/// is the number of minutes./// </remarks>publicstringMinutesAgo{get;set;}publicstringOneHourAgo{get;set;}publicstringHoursAgo{get;set;}publicstringYesterday{get;set;}publicstringDaysAgo{get;set;}publicstringLastMonth{get;set;}publicstringMonthsAgo{get;set;}publicstringLastYear{get;set;}publicstringYearsAgo{get;set;}/// <summary> Gets or sets the term for "ages ago". </summary>publicstringAgesAgo{get;set;}/// <summary>/// Gets or sets the threshold beyond which the fuzzy date should be/// considered "ages ago"./// </summary>publicTimeSpanAgesAgoThreshold{get;set;}/// <summary>/// Initialises a new <see cref="Grammar"/> instance with the/// specified properties./// </summary>privatevoidInitialise(string justNow,string minutesAgo,string oneHourAgo,string hoursAgo,string yesterday,string daysAgo,string lastMonth,string monthsAgo,string lastYear,string yearsAgo,string agesAgo,TimeSpan agesAgoThreshold){...}}
Одна из ключевых вещей , которые я хотел достичь, а также локализации, в том , что «сегодня» означал бы только «этот календарный день», так что IsToday, IsThisMonth, IsThisYearметода выглядеть следующим образом :
Я думал, что смогу сделать это, используя классы и полиморфизм. У меня была предыдущая итерация, в которой использовалось подклассификация, в результате чего было слишком много накладных расходов. Я переключился на более гибкую объектную модель делегата / публичного свойства, которая значительно лучше. Мой код немного более точен, я бы хотел придумать лучший способ генерировать «месяцы назад», который не выглядел бы слишком перегруженным.
Я думаю, что я все еще буду придерживаться каскада Джеффа if-then, потому что он меньше кода и проще (определенно легче гарантировать, что он будет работать так, как ожидалось).
Для приведенного ниже кода PrintRelativeTime.GetRelativeTimeMessage (TimeSpan ago) возвращает сообщение об относительном времени (например, «вчера»).
publicclassRelativeTimeRange:IComparable{publicTimeSpanUpperBound{get;set;}publicdelegatestringRelativeTimeTextDelegate(TimeSpan timeDelta);publicRelativeTimeTextDelegateMessageCreator{get;set;}publicintCompareTo(object obj){if(!(obj isRelativeTimeRange)){return1;}// note that this sorts in reverse order to the way you'd expect, // this saves having to reverse a list laterreturn(obj asRelativeTimeRange).UpperBound.CompareTo(UpperBound);}}publicclassPrintRelativeTime{privatestaticList<RelativeTimeRange> timeRanges;staticPrintRelativeTime(){
timeRanges =newList<RelativeTimeRange>{newRelativeTimeRange{UpperBound=TimeSpan.FromSeconds(1),MessageCreator=(delta)=>{return"one second ago";}},newRelativeTimeRange{UpperBound=TimeSpan.FromSeconds(60),MessageCreator=(delta)=>{return delta.Seconds+" seconds ago";}},newRelativeTimeRange{UpperBound=TimeSpan.FromMinutes(2),MessageCreator=(delta)=>{return"one minute ago";}},newRelativeTimeRange{UpperBound=TimeSpan.FromMinutes(60),MessageCreator=(delta)=>{return delta.Minutes+" minutes ago";}},newRelativeTimeRange{UpperBound=TimeSpan.FromHours(2),MessageCreator=(delta)=>{return"one hour ago";}},newRelativeTimeRange{UpperBound=TimeSpan.FromHours(24),MessageCreator=(delta)=>{return delta.Hours+" hours ago";}},newRelativeTimeRange{UpperBound=TimeSpan.FromDays(2),MessageCreator=(delta)=>{return"yesterday";}},newRelativeTimeRange{UpperBound=DateTime.Now.Subtract(DateTime.Now.AddMonths(-1)),MessageCreator=(delta)=>{return delta.Days+" days ago";}},newRelativeTimeRange{UpperBound=DateTime.Now.Subtract(DateTime.Now.AddMonths(-2)),MessageCreator=(delta)=>{return"one month ago";}},newRelativeTimeRange{UpperBound=DateTime.Now.Subtract(DateTime.Now.AddYears(-1)),MessageCreator=(delta)=>{return(int)Math.Floor(delta.TotalDays/30)+" months ago";}},newRelativeTimeRange{UpperBound=DateTime.Now.Subtract(DateTime.Now.AddYears(-2)),MessageCreator=(delta)=>{return"one year ago";}},newRelativeTimeRange{UpperBound=TimeSpan.MaxValue,MessageCreator=(delta)=>{return(int)Math.Floor(delta.TotalDays/365.24D)+" years ago";}}};
timeRanges.Sort();}publicstaticstringGetRelativeTimeMessage(TimeSpan ago){RelativeTimeRange postRelativeDateRange = timeRanges[0];foreach(var timeRange in timeRanges){if(ago.CompareTo(timeRange.UpperBound)<=0){
postRelativeDateRange = timeRange;}}return postRelativeDateRange.MessageCreator(ago);}}
StriplingWarrior: Простота чтения и изменения по сравнению с оператором switch или стеком операторов if / else. Статический словарь означает, что его и объекты Func <,> не нужно создавать каждый раз, когда мы хотим использовать ToRelativeDate; он создан только один раз, по сравнению с тем, который я связал в своем ответе.
Крис Чарабарук
Понимаю. Я просто думал, поскольку в документации Dictionaryговорится, что «порядок, в котором возвращаются элементы, не определен» ( msdn.microsoft.com/en-us/library/xfhwa508.aspx ), возможно, это не лучшая структура данных для использования когда вы не заботитесь о времени поиска, а о том, чтобы все оставалось в порядке.
StriplingWarrior
StriplingWarrior: Я считаю, что LINQ учитывает это при использовании с Dictionarys. Если вам все еще неудобно с этим, вы можете использовать SortedDictionary, но мой собственный опыт показывает, что в этом нет необходимости.
Крис Чарабарук
12
Когда вы знаете часовой пояс зрителя, может быть понятнее использовать календарные дни в масштабе дня. Я не знаком с библиотеками .NET, поэтому, к сожалению, я не знаю, как вы это сделаете в C #.
На потребительских сайтах вы также можете помахать рукой меньше минуты. «Менее минуты назад» или «только сейчас» может быть достаточно хорошо.
Вот алгоритм, который использует stackoverflow, но переписанный более кратко в псевдокоде perlish с исправлением ошибки (без «одного часа назад»). Функция занимает (положительное) число секунд назад и возвращает удобную для человека строку, например «3 часа назад» или «вчера».
publicstaticstringTimeAgo(thisDateTime dateTime){string result =string.Empty;var timeSpan =DateTime.Now.Subtract(dateTime);if(timeSpan <=TimeSpan.FromSeconds(60)){
result =string.Format("{0} seconds ago", timeSpan.Seconds);}elseif(timeSpan <=TimeSpan.FromMinutes(60)){
result = timeSpan.Minutes>1?String.Format("about {0} minutes ago", timeSpan.Minutes):"about a minute ago";}elseif(timeSpan <=TimeSpan.FromHours(24)){
result = timeSpan.Hours>1?String.Format("about {0} hours ago", timeSpan.Hours):"about an hour ago";}elseif(timeSpan <=TimeSpan.FromDays(30)){
result = timeSpan.Days>1?String.Format("about {0} days ago", timeSpan.Days):"yesterday";}elseif(timeSpan <=TimeSpan.FromDays(365)){
result = timeSpan.Days>30?String.Format("about {0} months ago", timeSpan.Days/30):"about a month ago";}else{
result = timeSpan.Days>365?String.Format("about {0} years ago", timeSpan.Days/365):"about a year ago";}return result;}
Или используйте плагин jQuery с расширением Razor от Timeago.
Вы можете уменьшить нагрузку на стороне сервера, выполнив эту логику на стороне клиента. Посмотреть источник на некоторых страницах Digg для справки. У них сервер выдает значение времени эпохи, которое обрабатывается Javascript. Таким образом, вам не нужно управлять часовым поясом конечного пользователя. Новый код на стороне сервера будет выглядеть примерно так:
Это я получил из одного из блогов Билла Гейтса. Мне нужно найти его в истории моего браузера, и я дам вам ссылку.
Код Javascript для того же (по запросу):
function posted(t){var now =newDate();var diff = parseInt((now.getTime()-Date.parse(t))/1000);if(diff <60){return'less than a minute ago';}elseif(diff <120){return'about a minute ago';}elseif(diff <(2700)){return(parseInt(diff /60)).toString()+' minutes ago';}elseif(diff <(5400)){return'about an hour ago';}elseif(diff <(86400)){return'about '+(parseInt(diff /3600)).toString()+' hours ago';}elseif(diff <(172800)){return'1 day ago';}else{return(parseInt(diff /86400)).toString()+' days ago';}}
Я думаю, что уже есть ряд ответов, связанных с этим постом, но можно использовать его, который прост в использовании, как плагин, а также легко читаем для программистов. Отправьте свою конкретную дату и получите ее значение в виде строки:
Ответы:
Джефф, твой код хорош, но может быть понятнее с константами (как предложено в Code Complete).
источник
Thread.Sleep(1 * MINUTE)
? Потому что это неправильно в 1000 раз.const int SECOND = 1;
Так странно, что секунда - это одна секунда.плагин jquery.timeago
Джефф, потому что Stack Overflow широко использует jQuery, я рекомендую плагин jquery.timeago .
Льготы:
Просто прикрепите его к своим временным меткам на DOM:
Это превратит все
abbr
элементы с классом времени и отметкой времени ISO 8601 в заголовке:в нечто вроде этого:
который дает: 4 месяца назад. По прошествии времени метки времени будут автоматически обновляться.
Отказ от ответственности: я написал этот плагин, поэтому я пристрастен.
источник
Вот как я это делаю
Предложения? Комментарии? Как улучшить этот алгоритм?
источник
Я предпочитаю эту версию за ее краткость и возможность добавлять новые точки галочки. Это может быть инкапсулировано с
Latest()
расширением Timespan вместо этого длинного 1 вкладыша, но для краткости публикации это подойдет. Это исправляет час назад, 1 час назад, предоставляя час до 2 часов прошлоисточник
SortedDictionary
вместо простогоDictionary
: использование такое же, но оно гарантирует, что ключи отсортированы. Но даже в этом случае алгоритм имеет недостатки, посколькуRelativeDate(DateTime.Now.AddMonths(-3).AddDays(-3))
возвращает «95 месяцев назад» , независимо от того, какой тип словаря вы используете, что является неправильным (он должен возвращать «3 месяца назад» или «4 месяца назад» в зависимости от того, какой порог вы используете » повторное использование) - даже если -3 не создает дату в прошлом году (я проверял это в декабре, поэтому в этом случае это не должно происходить).Вот переписать с Jeffs Script для PHP:
источник
http://refactormycode.com/codes/493-twitter-esque-relative-dates
Версия C # 6:
источник
Func<string>
наFunc<double>
.Вот реализация, которую я добавил в качестве метода расширения для класса DateTime, который обрабатывает как будущие, так и прошедшие даты и предоставляет параметр аппроксимации, который позволяет вам указать уровень детализации, который вы ищете («3 часа назад» против «3 часа, 23 минуты 12 секунд назад "):
источник
Я бы порекомендовал вычислить это на стороне клиента тоже. Меньше работы для сервера.
Ниже приводится версия, которую я использую (от Зак Кожаного)
источник
Есть также пакет под названием Humanizr для Nuget, и он действительно очень хорошо работает и находится в .NET Foundation.
Скотт Хансельман написал в своем блоге
источник
@Джефф
ИМХО твое кажется немного длинным. Однако он кажется немного более устойчивым с поддержкой «вчера» и «лет». Но по моему опыту, когда это используется, человек, скорее всего, будет просматривать контент в первые 30 дней. Это только действительно жестокие люди, которые приходят после этого. Вот почему я обычно предпочитаю быть кратким и простым.
Это метод, который я сейчас использую на одном из моих сайтов. Это только возвращает относительный день, час, время. И тогда пользователь должен ударить по «назад» в выводе.
источник
Пару лет опоздал на вечеринку, но у меня было требование сделать это как для прошлых, так и для будущих свиданий, поэтому я объединил Джеффа и Винсента в это. Это тернаритическая феерия! :)
источник
Есть ли простой способ сделать это на Java?
java.util.Date
Класс кажется довольно ограниченным.Вот мое быстрое и грязное решение Java:
источник
Версия для iPhone Objective-C
источник
Учитывая, что мир и ее муж, похоже, публикуют примеры кода, вот что я написал некоторое время назад, основываясь на нескольких из этих ответов.
У меня была особая необходимость, чтобы этот код был локализуемым. Итак, у меня есть два класса -
Grammar
, которые определяют локализуемые термины, иFuzzyDateExtensions
которые содержат несколько методов расширения. Мне не нужно было иметь дело с будущими датами, поэтому я не пытался справиться с ними с помощью этого кода.Я оставил некоторые XML-документы в источнике, но ради краткости удалил большинство (где они были бы очевидны). Я также не включил каждого ученика здесь:
FuzzyDateString
Класс содержит:Одна из ключевых вещей , которые я хотел достичь, а также локализации, в том , что «сегодня» означал бы только «этот календарный день», так что
IsToday
,IsThisMonth
,IsThisYear
метода выглядеть следующим образом :и методы округления такие (я включил
RoundedMonths
, так как это немного отличается):Я надеюсь, что люди найдут это полезным и / или интересным: о)
источник
В PHP я делаю это так:
источник
используя Fluent DateTime
источник
Я думал, что смогу сделать это, используя классы и полиморфизм. У меня была предыдущая итерация, в которой использовалось подклассификация, в результате чего было слишком много накладных расходов. Я переключился на более гибкую объектную модель делегата / публичного свойства, которая значительно лучше. Мой код немного более точен, я бы хотел придумать лучший способ генерировать «месяцы назад», который не выглядел бы слишком перегруженным.
Я думаю, что я все еще буду придерживаться каскада Джеффа if-then, потому что он меньше кода и проще (определенно легче гарантировать, что он будет работать так, как ожидалось).
Для приведенного ниже кода PrintRelativeTime.GetRelativeTimeMessage (TimeSpan ago) возвращает сообщение об относительном времени (например, «вчера»).
источник
Так же, как другой ответ на этот вопрос, но как метод расширения со статическим словарем.
источник
Dictionary
говорится, что «порядок, в котором возвращаются элементы, не определен» ( msdn.microsoft.com/en-us/library/xfhwa508.aspx ), возможно, это не лучшая структура данных для использования когда вы не заботитесь о времени поиска, а о том, чтобы все оставалось в порядке.Dictionary
s. Если вам все еще неудобно с этим, вы можете использоватьSortedDictionary
, но мой собственный опыт показывает, что в этом нет необходимости.Когда вы знаете часовой пояс зрителя, может быть понятнее использовать календарные дни в масштабе дня. Я не знаком с библиотеками .NET, поэтому, к сожалению, я не знаю, как вы это сделаете в C #.
На потребительских сайтах вы также можете помахать рукой меньше минуты. «Менее минуты назад» или «только сейчас» может быть достаточно хорошо.
источник
Вы можете попробовать это. Я думаю, что это будет работать правильно.
источник
Java для использования gwt на стороне клиента:
источник
@Джефф
Делать вычитание на
DateTime
возвращает вTimeSpan
любом случае.Так что вы можете просто сделать
Я также удивлен, увидев, как константы умножаются вручную, а затем добавляются комментарии с умножениями. Это была некоторая ошибочная оптимизация?
источник
Вот алгоритм, который использует stackoverflow, но переписанный более кратко в псевдокоде perlish с исправлением ошибки (без «одного часа назад»). Функция занимает (положительное) число секунд назад и возвращает удобную для человека строку, например «3 часа назад» или «вчера».
источник
Вы можете использовать расширение TimeAgo, из которого выглядит следующее:
Или используйте плагин jQuery с расширением Razor от Timeago.
источник
Вы можете уменьшить нагрузку на стороне сервера, выполнив эту логику на стороне клиента. Посмотреть источник на некоторых страницах Digg для справки. У них сервер выдает значение времени эпохи, которое обрабатывается Javascript. Таким образом, вам не нужно управлять часовым поясом конечного пользователя. Новый код на стороне сервера будет выглядеть примерно так:
Вы даже можете добавить туда блок NOSCRIPT и просто выполнить ToString ().
источник
Это я получил из одного из блогов Билла Гейтса. Мне нужно найти его в истории моего браузера, и я дам вам ссылку.
Код Javascript для того же (по запросу):
По сути, вы работаете в секундах ...
источник
Я думаю, что уже есть ряд ответов, связанных с этим постом, но можно использовать его, который прост в использовании, как плагин, а также легко читаем для программистов. Отправьте свою конкретную дату и получите ее значение в виде строки:
источник
источник
Если вы хотите иметь вывод, например
"2 days, 4 hours and 12 minutes ago"
, вам нужен временной интервал:Затем вы можете получить доступ к значениям, которые вам нравятся:
так далее...
источник
Я бы предоставил несколько удобных методов расширения для этого и сделал бы код более читабельным. Во-первых, пара методов расширения для
Int32
.Тогда один для
DateTime
.Теперь вы можете сделать что-то вроде ниже:
источник