Co jeśli chcesz obliczyć względny czas od teraz do przyszłości?
Jhonny D. Cano-Lewica-
2
moment.js to bardzo fajna biblioteka parsująca daty. Możesz rozważyć użycie tej strony (po stronie serwera lub po stronie klienta), w zależności od potrzeb. po prostu fyi, bo nikt o tym nie wspominał
Nienawidzę takich stałych z pasją. Czy to komuś wygląda źle? Thread.Sleep(1 * MINUTE)? Ponieważ jest to błąd 1000 razy.
Roman Starkov
31
const int SECOND = 1;Dziwne, sekunda to jedna sekunda.
poważnedev
62
Ten typ kodu jest prawie niemożliwy do zlokalizowania. Jeśli Twoja aplikacja musi pozostać tylko w języku angielskim, to dobrze. Ale jeśli przejdziesz do innych języków, będziesz nienawidził siebie za robienie takiej logiki. Tylko żebyście wszyscy wiedzieli ...
Nik Reiman
73
Myślę, że gdyby zmieniono nazwy stałych, aby dokładnie opisać zawartą w nich wartość, łatwiej byłoby to zrozumieć. SecondsPerMinute = 60; MinutyPogodziny = 60; SecondsPerHour = MinutyPerHour * SecondsPerHour; itp. Samo wywołanie go MINUTA = 60 nie pozwala czytelnikowi określić, jaka jest wartość.
slolife
14
Dlaczego nikt (oprócz Joe) nie przejmuje się niewłaściwą wartością „Wczoraj” lub „dni temu” ??? Wczoraj nie chodzi o obliczenia godzinowe, ale codzienne. Tak, to zły kod, przynajmniej w dwóch częstych przypadkach.
Jeff, ponieważ Stack Overflow intensywnie korzysta z jQuery, polecam wtyczkę jquery.timeago .
Korzyści:
Unikaj znaczników czasu z „1 minuty temu”, mimo że strona została otwarta 10 minut temu; timeago odświeża się automatycznie.
Możesz w pełni wykorzystać buforowanie stron i / lub fragmentów w swoich aplikacjach internetowych, ponieważ znaczniki czasu nie są obliczane na serwerze.
Możesz używać mikroformatów jak fajne dzieci.
Po prostu dołącz go do znaczników czasu w DOM gotowych:
Seb, Jeśli masz wyłączoną obsługę Javascript, wyświetlony zostanie łańcuch, który pierwotnie umieściłeś między znacznikami abbr. Zazwyczaj jest to tylko data lub godzina w dowolnym formacie. Timeago degraduje się z gracją. To nie staje się dużo prostsze.
Hej, dzięki Rob. W porządku Jest to ledwo zauważalne, zwłaszcza gdy podczas przejścia zmienia się tylko jedna liczba, chociaż strony SO mają wiele znaczników czasu. Wydawało mi się jednak, że doceniłby przynajmniej zalety buforowania stron, nawet jeśli zdecyduje się unikać automatycznych aktualizacji. Jestem pewien, że Jeff mógł również wyrazić opinię na temat ulepszenia wtyczki. Pocieszam się, wiedząc, że korzystają z niego witryny takie jak arstechnica.com .
Ryan McGeary,
19
@Rob Fonseca-Ensor - teraz też sprawia, że płaczę. Jak aktualizuje się raz na minutę, aby wyświetlać dokładne informacje, w jakikolwiek sposób związane z miganiem tekstu co sekundę?
Daniel Earwicker
25
Pytanie dotyczy C #, nie widzę znaczenia wtyczki jQuery.
„<48 * 60 * 60s” to dość niekonwencjonalna definicja „wczoraj”. Jeśli w środę jest 9 rano, to czy naprawdę myślisz o 9:01 w poniedziałek jako „wczoraj”? Myślałem, że algorytm na wczoraj lub „n dni temu” powinien rozważyć przed / po północy.
Joe
139
Kompilatory są zwykle bardzo dobre w obliczaniu stałych wyrażeń, takich jak 24 * 60 * 60, więc możesz bezpośrednio ich używać zamiast obliczać je jako 86400 i umieszczać oryginalne wyrażenie w komentarzach
zvolkov
11
@bzlm Myślę, że zrobiłem projekt, nad którym pracowałem. Moją motywacją było ostrzeżenie innych, że w tym przykładzie kodu pominięto tygodnie. Jeśli chodzi o to, jak to zrobić, wydawało mi się to całkiem proste.
jray
9
Myślę, że dobrym sposobem na ulepszenie algorytmu jest wyświetlenie 2 jednostek, takich jak „2 miesiące 21 dni temu”, „1 godzina 40 minut temu” dla zwiększenia dokładności.
Evgeny Levin
5
@ Jeffy, przeoczyłeś obliczenia dotyczące roku przestępnego i powiązanych kontroli
Saboor Awan,
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"";}
Wolę tę wersję ze względu na zwięzłość i możliwość dodawania nowych punktów zaznaczenia. Można by to zawrzeć w Latest()rozszerzeniu Timespan zamiast tego długiego 1 linijki, ale ze względu na zwięzłość przy wysyłaniu, tak się stanie.
To naprawia godzinę temu, 1 godzinę temu, zapewniając godzinę do upływu 2 godzin
Używam tej funkcji, mam wiele problemów, na przykład, jeśli kpisz sobie z „theDate = DateTime.Now.AddMinutes (-40);” Dostaję „40 godzin temu”, ale przy odpowiedzi kodu refactormycode Michaela zwraca poprawność „40 minut temu”?
GONeale,
myślę, że brakuje Ci zera, spróbuj: dawno od = (DateTime.Now.Ticks - theDate.Ticks) / 10000000;
robnardo
8
Hmm, chociaż ten kod może działać, niepoprawne i niepoprawne jest założenie, że kolejność kluczy w Słowniku będzie w określonej kolejności. Słownik używa Object.GetHashCode (), która nie zwraca długiej, ale int !. Jeśli chcesz je posortować, powinieneś użyć SortedList <long, string>. Co jest złego w ocenie progów w zbiorze if / else if /.../ else? Otrzymujesz taką samą liczbę porównań. FYI hash na długo.MaxValue okazuje się być taki sam jak int.MinValue!
CodeMonkeyKing
OP zapomniał t.Dzień> 30? t.Days / 30:
Lars Holm Jensen
Aby rozwiązać problem wspomniany przez @CodeMonkeyKing, możesz użyć SortedDictionaryzamiast zwykłego Dictionary: użycie jest takie samo, ale zapewnia sortowanie kluczy. Ale nawet wtedy algorytm ma wady, ponieważ RelativeDate(DateTime.Now.AddMonths(-3).AddDays(-3))zwraca „95 miesięcy temu” , niezależnie od używanego typu słownika, co jest niepoprawne (powinno zwrócić „3 miesiące temu” lub „4 miesiące temu” w zależności od tego, który próg „ ponownie używam) - nawet jeśli -3 nie tworzy daty w poprzednim roku (przetestowałem to w grudniu, więc w tym przypadku nie powinno się to zdarzyć).
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;}
to jest bardzo fajne IMO :) Można to również refaktoryzować jako metodę rozszerzenia? czy słownik może stać się statyczny, więc jest tworzony tylko raz i do którego można się później odwoływać?
Prawdopodobnie zechcesz wyciągnąć ten słownik do pola, aby zmniejszyć tworzenie instancji i rezygnację z GC. Musisz się zmienić Func<string>na Func<double>.
Drew Noakes,
49
Oto implementacja, którą dodałem jako metodę rozszerzenia do klasy DateTime, która obsługuje zarówno przyszłe, jak i przeszłe daty i zapewnia opcję przybliżenia, która pozwala określić poziom szczegółowości, którego szukasz („3 godziny temu” w porównaniu do „3 godzin, 23 minuty, 12 sekund temu ”):
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();}
przyjazna uwaga: na .net 4.5 lub nowszym nie instaluj pełnego Humanizera ... zainstaluj tylko Humanizer.Core część tego .. ponieważ inne pakiety językowe nie są obsługiwane w tej wersji
Ahmad
Tak przydatne! Ta odpowiedź musi być znacznie wyższa na tej liście. Gdybym miał 100 głosów, oddałbym to temu. Najwyraźniej (pochodzący z JS-land) poszukiwanie tego pakietu nie było łatwe.
kumarharsh
29
@jeff
IMHO twoje wydaje się trochę długie. Wydaje się jednak, że jest nieco bardziej solidny dzięki obsłudze „wczoraj” i „lat”. Jednak z mojego doświadczenia wynika, że osoba ta najprawdopodobniej wyświetli treść w ciągu pierwszych 30 dni. Potem są tylko naprawdę hardcorowi ludzie. Dlatego zwykle decyduję się na to, aby było to krótkie i proste.
Jest to metoda, której obecnie używam na jednej z moich stron internetowych. Zwraca tylko względny dzień, godzinę i godzinę. A potem użytkownik musi uderzyć „temu” w wynik.
Kilka lat spóźniłem się na przyjęcie, ale musiałem to zrobić zarówno dla dat przeszłych, jak i przyszłych, więc połączyłem w to Jeffa i Vincenta . To ternarytastyczna ekstrawagancja! :)
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";}}}
Biorąc pod uwagę, że świat i jej mąż wydają się publikować próbki kodu, oto, co napisałem jakiś czas temu, w oparciu o kilka tych odpowiedzi.
Szczególnie potrzebowałem, aby ten kod był zlokalizowany. Mam więc dwie klasy - Grammarktóre określają lokalizowalne warunki i FuzzyDateExtensionsktóre zawierają wiele metod rozszerzenia. Nie musiałem zajmować się przyszłymi czasami danych, więc nie próbuję obsłużyć ich przy pomocy tego kodu.
Część XMLdoc zostawiłem w źródle, ale usunąłem większość (tam, gdzie byłyby oczywiste) ze względu na zwięzłość. Nie uwzględniłem tu również każdego członka klasy:
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){...}}
Jedną z najważniejszych rzeczy, które chcieliśmy osiągnąć, jak również lokalizacja, było to, że „dziś” będzie jedynie oznaczać „ten dzień kalendarzowy”, więc IsToday, IsThisMonth, IsThisYearmetody wyglądać następująco:
Myślałem, że dam temu szansę za pomocą zajęć i polimorfizmu. Miałem poprzednią iterację, w której wykorzystano podklasę, która skończyła się zbyt dużym obciążeniem. Zmieniłem na bardziej elastyczny model obiektu delegowanego / własności publicznej, który jest znacznie lepszy. Mój kod jest bardzo nieznacznie dokładniejszy, chciałbym wymyślić lepszy sposób na wygenerowanie „miesięcy temu”, który nie wydawał się zbyt zawyżony.
Myślę, że nadal trzymałbym się kaskady Jeffa jeśli-to, ponieważ jest mniej kodu i jest prostszy (zdecydowanie łatwiej jest upewnić się, że zadziała zgodnie z oczekiwaniami).
Dla poniższego kodu PrintRelativeTime.GetRelativeTimeMessage (TimeSpan temu) zwraca komunikat o względnym czasie (np. „Wczoraj”).
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: Łatwość odczytu i modyfikacji w porównaniu do instrukcji switch lub stosu instrukcji if / else. Słownik jest statyczny, co oznacza, że nie trzeba tworzyć obiektów Func <,> za każdym razem, gdy chcemy użyć ToRelativeDate; jest tworzony tylko raz, w porównaniu do tego, który podłączyłem w mojej odpowiedzi.
Chris Charabaruk
Widzę. Właśnie myślałem, ponieważ dokumentacja Dictionarymówi, że „Kolejność zwracania elementów jest niezdefiniowana” ( msdn.microsoft.com/en-us/library/xfhwa508.aspx ) być może nie jest to najlepsza struktura danych do użycia gdy nie zależy Ci na czasach wyszukiwania, a nie na utrzymywaniu porządku.
StriplingWarrior
StriplingWarrior: Wierzę, że LINQ bierze to pod uwagę, gdy używa się go z Dictionarys. Jeśli nadal czujesz się z tym niekomfortowo, możesz go użyć SortedDictionary, ale z mojego własnego doświadczenia wynika, że jest to niepotrzebne.
Chris Charabaruk
12
Jeśli znasz strefę czasową widza, łatwiej będzie użyć dni kalendarzowych w skali dnia. Nie znam bibliotek .NET, więc niestety nie wiem, jak byś to zrobił w C #.
W witrynach konsumenckich możesz też być nieco bardziej machający ręką. „Mniej niż minutę temu” lub „przed chwilą” może być wystarczająco dobre.
Pytanie ma oznaczenie C # . Dlaczego ten kod Java ? IMHO stosuje tylko kod C #
Kiquenet
9
@Jeff
var ts =newTimeSpan(DateTime.UtcNow.Ticks- dt.Ticks);
Mimo to odejmowanie DateTimezwraca TimeSpan.
Więc możesz po prostu zrobić
(DateTime.UtcNow- dt).TotalSeconds
Jestem również zaskoczony, widząc, że stałe są mnożone ręcznie, a następnie dodawane są komentarze z mnożeniem. Czy to była jakaś błędna optymalizacja?
Oto algorytm nakładania stosów algorytmów, ale przepisany bardziej zwięźle w pseudokodzie „zgiń” z poprawką (nie „godzinę temu”). Funkcja zajmuje (dodatnią) liczbę sekund temu i zwraca przyjazny dla człowieka ciąg, taki jak „3 godziny temu” lub „wczoraj”.
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;}
Lub użyj wtyczki jQuery z rozszerzeniem Razor od Timeago.
Można zmniejszyć obciążenie po stronie serwera, wykonując tę logikę po stronie klienta. Zobacz źródło na niektórych stronach Digg w celach informacyjnych. Mają serwer, który emituje wartość czasu epoki, która jest przetwarzana przez Javascript. W ten sposób nie musisz zarządzać strefą czasową użytkownika końcowego. Nowy kod po stronie serwera mógłby wyglądać następująco:
Dostałem to z jednego z blogów Billa Gatesa. Muszę to znaleźć w historii przeglądarki i dam ci link.
Kod JavaScript, aby zrobić to samo (zgodnie z żądaniem):
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';}}
Myślę, że istnieje już wiele odpowiedzi związanych z tym postem, ale można użyć tego, który jest łatwy w użyciu, podobnie jak wtyczka, a także łatwy do odczytania dla programistów. Wyślij konkretną datę i uzyskaj jej wartość w postaci ciągu:
Odpowiedzi:
Jeff, twój kod jest fajny, ale może być jaśniejszy dzięki stałym (jak zasugerowano w Code Complete).
źródło
Thread.Sleep(1 * MINUTE)
? Ponieważ jest to błąd 1000 razy.const int SECOND = 1;
Dziwne, sekunda to jedna sekunda.wtyczka jquery.timeago
Jeff, ponieważ Stack Overflow intensywnie korzysta z jQuery, polecam wtyczkę jquery.timeago .
Korzyści:
Po prostu dołącz go do znaczników czasu w DOM gotowych:
Spowoduje to obrócenie wszystkich
abbr
elementów z klasą czasu i znacznikiem czasu ISO 8601 w tytule:w coś takiego:
co daje: 4 miesiące temu. W miarę upływu czasu znaczniki czasu będą automatycznie aktualizowane.
Oświadczenie: Napisałem tę wtyczkę, więc jestem stronniczy.
źródło
Oto jak to robię
Propozycje? Komentarze? Sposoby ulepszenia tego algorytmu?
źródło
Wolę tę wersję ze względu na zwięzłość i możliwość dodawania nowych punktów zaznaczenia. Można by to zawrzeć w
Latest()
rozszerzeniu Timespan zamiast tego długiego 1 linijki, ale ze względu na zwięzłość przy wysyłaniu, tak się stanie. To naprawia godzinę temu, 1 godzinę temu, zapewniając godzinę do upływu 2 godzinźródło
SortedDictionary
zamiast zwykłegoDictionary
: użycie jest takie samo, ale zapewnia sortowanie kluczy. Ale nawet wtedy algorytm ma wady, ponieważRelativeDate(DateTime.Now.AddMonths(-3).AddDays(-3))
zwraca „95 miesięcy temu” , niezależnie od używanego typu słownika, co jest niepoprawne (powinno zwrócić „3 miesiące temu” lub „4 miesiące temu” w zależności od tego, który próg „ ponownie używam) - nawet jeśli -3 nie tworzy daty w poprzednim roku (przetestowałem to w grudniu, więc w tym przypadku nie powinno się to zdarzyć).Oto przepis z Jeffs Script dla PHP:
źródło
http://refactormycode.com/codes/493-twitter-esque-relative-dates
Wersja C # 6:
źródło
Func<string>
naFunc<double>
.Oto implementacja, którą dodałem jako metodę rozszerzenia do klasy DateTime, która obsługuje zarówno przyszłe, jak i przeszłe daty i zapewnia opcję przybliżenia, która pozwala określić poziom szczegółowości, którego szukasz („3 godziny temu” w porównaniu do „3 godzin, 23 minuty, 12 sekund temu ”):
źródło
Poleciłbym to również po stronie klienta. Mniej pracy dla serwera.
Oto wersja, której używam (od Zach Leatherman)
źródło
Istnieje także pakiet o nazwie Humanizr w Nuget, który faktycznie działa naprawdę dobrze i jest w .NET Foundation.
Scott Hanselman napisał o tym na swoim blogu
źródło
@jeff
IMHO twoje wydaje się trochę długie. Wydaje się jednak, że jest nieco bardziej solidny dzięki obsłudze „wczoraj” i „lat”. Jednak z mojego doświadczenia wynika, że osoba ta najprawdopodobniej wyświetli treść w ciągu pierwszych 30 dni. Potem są tylko naprawdę hardcorowi ludzie. Dlatego zwykle decyduję się na to, aby było to krótkie i proste.
Jest to metoda, której obecnie używam na jednej z moich stron internetowych. Zwraca tylko względny dzień, godzinę i godzinę. A potem użytkownik musi uderzyć „temu” w wynik.
źródło
Kilka lat spóźniłem się na przyjęcie, ale musiałem to zrobić zarówno dla dat przeszłych, jak i przyszłych, więc połączyłem w to Jeffa i Vincenta . To ternarytastyczna ekstrawagancja! :)
źródło
Czy jest łatwy sposób to zrobić w Javie?
java.util.Date
Klasa wydaje się raczej ograniczona.Oto moje szybkie i brudne rozwiązanie Java:
źródło
Wersja iPhone Objective-C
źródło
Biorąc pod uwagę, że świat i jej mąż wydają się publikować próbki kodu, oto, co napisałem jakiś czas temu, w oparciu o kilka tych odpowiedzi.
Szczególnie potrzebowałem, aby ten kod był zlokalizowany. Mam więc dwie klasy -
Grammar
które określają lokalizowalne warunki iFuzzyDateExtensions
które zawierają wiele metod rozszerzenia. Nie musiałem zajmować się przyszłymi czasami danych, więc nie próbuję obsłużyć ich przy pomocy tego kodu.Część XMLdoc zostawiłem w źródle, ale usunąłem większość (tam, gdzie byłyby oczywiste) ze względu na zwięzłość. Nie uwzględniłem tu również każdego członka klasy:
FuzzyDateString
Klasa zawiera:Jedną z najważniejszych rzeczy, które chcieliśmy osiągnąć, jak również lokalizacja, było to, że „dziś” będzie jedynie oznaczać „ten dzień kalendarzowy”, więc
IsToday
,IsThisMonth
,IsThisYear
metody wyglądać następująco:i metody zaokrąglania są takie (uwzględniłem
RoundedMonths
, ponieważ jest to nieco inne):Mam nadzieję, że ludzie uznają to za przydatne i / lub interesujące: o)
źródło
W PHP robię to w ten sposób:
źródło
używając Fluent DateTime
źródło
Myślałem, że dam temu szansę za pomocą zajęć i polimorfizmu. Miałem poprzednią iterację, w której wykorzystano podklasę, która skończyła się zbyt dużym obciążeniem. Zmieniłem na bardziej elastyczny model obiektu delegowanego / własności publicznej, który jest znacznie lepszy. Mój kod jest bardzo nieznacznie dokładniejszy, chciałbym wymyślić lepszy sposób na wygenerowanie „miesięcy temu”, który nie wydawał się zbyt zawyżony.
Myślę, że nadal trzymałbym się kaskady Jeffa jeśli-to, ponieważ jest mniej kodu i jest prostszy (zdecydowanie łatwiej jest upewnić się, że zadziała zgodnie z oczekiwaniami).
Dla poniższego kodu PrintRelativeTime.GetRelativeTimeMessage (TimeSpan temu) zwraca komunikat o względnym czasie (np. „Wczoraj”).
źródło
To samo co inna odpowiedź na to pytanie, ale jako metoda rozszerzenia ze słownikiem statycznym.
źródło
Dictionary
mówi, że „Kolejność zwracania elementów jest niezdefiniowana” ( msdn.microsoft.com/en-us/library/xfhwa508.aspx ) być może nie jest to najlepsza struktura danych do użycia gdy nie zależy Ci na czasach wyszukiwania, a nie na utrzymywaniu porządku.Dictionary
s. Jeśli nadal czujesz się z tym niekomfortowo, możesz go użyćSortedDictionary
, ale z mojego własnego doświadczenia wynika, że jest to niepotrzebne.Jeśli znasz strefę czasową widza, łatwiej będzie użyć dni kalendarzowych w skali dnia. Nie znam bibliotek .NET, więc niestety nie wiem, jak byś to zrobił w C #.
W witrynach konsumenckich możesz też być nieco bardziej machający ręką. „Mniej niż minutę temu” lub „przed chwilą” może być wystarczająco dobre.
źródło
możesz spróbować. Myślę, że będzie działać poprawnie.
źródło
Java dla użycia gwt po stronie klienta:
źródło
@Jeff
Mimo to odejmowanie
DateTime
zwracaTimeSpan
.Więc możesz po prostu zrobić
Jestem również zaskoczony, widząc, że stałe są mnożone ręcznie, a następnie dodawane są komentarze z mnożeniem. Czy to była jakaś błędna optymalizacja?
źródło
Oto algorytm nakładania stosów algorytmów, ale przepisany bardziej zwięźle w pseudokodzie „zgiń” z poprawką (nie „godzinę temu”). Funkcja zajmuje (dodatnią) liczbę sekund temu i zwraca przyjazny dla człowieka ciąg, taki jak „3 godziny temu” lub „wczoraj”.
źródło
Możesz użyć rozszerzenia TimeAgo, z którego wygląda następująco:
Lub użyj wtyczki jQuery z rozszerzeniem Razor od Timeago.
źródło
Można zmniejszyć obciążenie po stronie serwera, wykonując tę logikę po stronie klienta. Zobacz źródło na niektórych stronach Digg w celach informacyjnych. Mają serwer, który emituje wartość czasu epoki, która jest przetwarzana przez Javascript. W ten sposób nie musisz zarządzać strefą czasową użytkownika końcowego. Nowy kod po stronie serwera mógłby wyglądać następująco:
Możesz nawet dodać tam blok NOSCRIPT i po prostu wykonać ToString ().
źródło
Dostałem to z jednego z blogów Billa Gatesa. Muszę to znaleźć w historii przeglądarki i dam ci link.
Kod JavaScript, aby zrobić to samo (zgodnie z żądaniem):
Zasadniczo pracujesz w sekundach ...
źródło
Myślę, że istnieje już wiele odpowiedzi związanych z tym postem, ale można użyć tego, który jest łatwy w użyciu, podobnie jak wtyczka, a także łatwy do odczytania dla programistów. Wyślij konkretną datę i uzyskaj jej wartość w postaci ciągu:
źródło
źródło
Jeśli chcesz mieć dane wyjściowe
"2 days, 4 hours and 12 minutes ago"
, potrzebujesz przedziału czasu:Następnie możesz uzyskać dostęp do wartości, które lubisz:
itp...
źródło
Podałbym do tego kilka przydatnych metod rozszerzeń i uczyniłbym kod bardziej czytelnym. Po pierwsze, kilka metod rozszerzenia dla
Int32
.Potem jeden za
DateTime
.Teraz możesz zrobić coś takiego:
źródło