Algoritmo de fecha difusa

Estoy buscando un algoritmo de fecha difusa. Empecé a escribir uno y me di cuenta de la tarea tediosa que es. Degeneró rápidamente en un gran número de códigos horribles para lidiar con casos especiales como la diferencia entre “ayer”, “la semana pasada” y “el último mes”, todos los cuales (en algunos casos) se refieren al mismo día pero son individualmente correctos basado en la fecha de hoy.

Estoy seguro de que debe haber un formateador de fecha difusa de código abierto, pero no puedo encontrarlo. Idealmente, me gustaría algo utilizando NSDate (OSX / iPhone) y sus formateadores, pero ese no es el aspecto difícil. ¿Alguien sabe de un formateador de fecha difusa tomando un período de tiempo relativo a ahora y devolviendo una cadena como (pero no limitada a):

  • hace unos momentos
  • en los últimos cinco minutos
  • el día de hoy
  • esta mañana
  • anoche
  • la semana pasada
  • el miércoles pasado
  • principios del mes pasado
  • junio del año pasado
  • un par de años atras

En un mundo ideal, me gustaría que la cadena sea lo más rica posible (es decir, devolver variantes aleatorias en “Hace apenas un momento”, como “justo ahora”).

Aclaración. Estoy buscando algo más sutil que los buckts y strings básicos. Quiero algo que sepa “ayer” y “el último miércoles” pueden referirse al mismo período pero solo uno es correcto cuando hoy es jueves.

Esta pregunta debería ayudarte a comenzar. Tiene el código que este sitio usa para calcular su tiempo relativo. Puede que no tenga los rangos específicos que desea, pero son lo suficientemente fáciles de agregar una vez que lo configuró.

Hay una propiedad en NSDateFormatter – “doesRelativeDateFormatting”. Aparece solo en 10.6 / iOS4.0 y posterior, pero formateará una fecha en una fecha relativa en la configuración regional correcta.

De la documentación de Apple :

Si un formateador de fecha utiliza el formato de fecha relativo, cuando sea posible, reemplaza el componente de fecha de su salida con una frase, como “hoy” o “mañana”, que indica una fecha relativa. Las frases disponibles dependen de la configuración regional para el formateador de fecha; mientras que, para fechas futuras, el inglés solo puede permitir “mañana”, los franceses pueden permitir “pasado pasado mañana”, como se ilustra en el siguiente ejemplo.

Código

El siguiente es un código que imprimirá un buen número de cadenas relativas para una configuración regional determinada.

NSLocale *locale = [NSLocale currentLocale]; // NSLocale *locale = [[[NSLocale alloc] initWithLocaleIdentifier:@"fr_FR"] autorelease]; NSDateFormatter *relativeDateFormatter = [[NSDateFormatter alloc] init]; [relativeDateFormatter setTimeStyle:NSDateFormatterNoStyle]; [relativeDateFormatter setDateStyle:NSDateFormatterMediumStyle]; [relativeDateFormatter setDoesRelativeDateFormatting:YES]; [relativeDateFormatter setLocale:locale]; NSDateFormatter *normalDateFormatter = [[NSDateFormatter alloc] init]; [normalDateFormatter setTimeStyle:NSDateFormatterNoStyle]; [normalDateFormatter setDateStyle:NSDateFormatterMediumStyle]; [normalDateFormatter setDoesRelativeDateFormatting:NO]; [normalDateFormatter setLocale:locale]; NSString * lastUniqueString = nil; for ( NSTimeInterval timeInterval = -60*60*24*400; timeInterval < 60*60*24*400; timeInterval += 60.0*60.0*24.0 ) { NSDate * date = [NSDate dateWithTimeIntervalSinceNow:timeInterval]; NSString * relativeFormattedString = [relativeDateFormatter stringForObjectValue:date]; NSString * formattedString = [normalDateFormatter stringForObjectValue:date]; if ( [relativeFormattedString isEqualToString:lastUniqueString] || [relativeFormattedString isEqualToString:formattedString] ) continue; NSLog( @"%@", relativeFormattedString ); lastUniqueString = relativeFormattedString; } 

Notas:

  • No se requiere una configuración regional
  • No hay tantas sustituciones para el inglés. En el momento de escribir, hay: "Ayer, Hoy, Mañana". Apple puede incluir más en el futuro.
  • Es divertido cambiar la configuración regional y ver qué está disponible en otros idiomas (el francés tiene algunos más que el inglés, por ejemplo)
  • Si está en iOS, es posible que desee suscribirse a UIApplicationSignificantTimeChangeNotification

Constructor de interfaz

Puede establecer la propiedad "doesRelativeDateFormatting" en el Interface Builder:

  • Seleccione su NSDateFormatter y elija la pestaña "Inspector de identidad" de la Paleta del inspector (la última [comando-6]).
  • En la subsección denominada "Atributos de tiempo de ejecución definidos por el usuario", puede agregar su propio valor para una clave en el objeto seleccionado (en este caso, su instancia de NSDateFormatter). Agregue "doesRelativeDateFormatting", elija un tipo "booleano" y asegúrese de que esté marcado.
  • Recuerde : puede parecer que no funcionó para nada, pero puede ser porque solo hay unos pocos valores sustituidos para su configuración regional. Pruebe al menos una fecha para Ayer, Hoy y Mañana antes de decidir si no está configurado correctamente.

Es posible que desee consultar la función de distance_of_time_in_words de Rail en date_helper.rb , que he pegado a continuación.

 # File vendor/rails/actionpack/lib/action_view/helpers/date_helper.rb, line 59 def distance_of_time_in_words(from_time, to_time = 0, include_seconds = false, options = {}) from_time = from_time.to_time if from_time.respond_to?(:to_time) to_time = to_time.to_time if to_time.respond_to?(:to_time) distance_in_minutes = (((to_time - from_time).abs)/60).round distance_in_seconds = ((to_time - from_time).abs).round I18n.with_options :locale => options[:locale], :scope => 'datetime.distance_in_words''datetime.distance_in_words' do |locale| case distance_in_minutes when 0..1 return distance_in_minutes == 0 ? locale.t(:less_than_x_minutes, :count => 1) : locale.t(:x_minutes, :count => distance_in_minutes) unless include_seconds case distance_in_seconds when 0..4 then locale.t :less_than_x_seconds, :count => 5 when 5..9 then locale.t :less_than_x_seconds, :count => 10 when 10..19 then locale.t :less_than_x_seconds, :count => 20 when 20..39 then locale.t :half_a_minute when 40..59 then locale.t :less_than_x_minutes, :count => 1 else locale.t :x_minutes, :count => 1 end when 2..44 then locale.t :x_minutes, :count => distance_in_minutes when 45..89 then locale.t :about_x_hours, :count => 1 when 90..1439 then locale.t :about_x_hours, :count => (distance_in_minutes.to_f / 60.0).round when 1440..2879 then locale.t :x_days, :count => 1 when 2880..43199 then locale.t :x_days, :count => (distance_in_minutes / 1440).round when 43200..86399 then locale.t :about_x_months, :count => 1 when 86400..525599 then locale.t :x_months, :count => (distance_in_minutes / 43200).round when 525600..1051199 then locale.t :about_x_years, :count => 1 else locale.t :over_x_years, :count => (distance_in_minutes / 525600).round end end end 

Entonces, aquí está la categoría que escribí en NSDate para aquellos que todavía están interesados. El problema es uno de esos que se vuelve un poco quijotesco. Básicamente es una gran statement de cambio (aunque lo implementé en una serie de if () en cascada para mantenerlo más legible).

Para cada período de tiempo, entonces selecciono de un conjunto aleatorio de formas de decir la hora.

Con todo, esto deleitó a algunos de nuestros usuarios, pero no estoy seguro de si valió la pena el esfuerzo.

 NSTimeInterval const kTenSeconds = (10.0f ); NSTimeInterval const kOneMinute = (60.0f); NSTimeInterval const kFiveMinutes = (5.0f*60.0f); NSTimeInterval const kFifteenMinutes = (15.0f*60.0f) ; NSTimeInterval const kHalfAnHour = (30.0f*60.0f) ; NSTimeInterval const kOneHour = 3600.0f; // (60.0f * 60.0f); NSTimeInterval const kHalfADay = (3600.0f * 12.0f); NSTimeInterval const kOneDay = (3600.0f * 24.0f); NSTimeInterval const kOneWeek = (3600.0f * 24.0f * 7.0f); @implementation NSDate (Fuzzy) -(NSString*)fuzzyStringRelativeToNow; { static NSArray* secondsStrings; static NSArray* minuteStrings; static NSArray* fiveMinuteStrings; static NSArray* halfHourStrings; static NSArray* earlyMonthStrings; NSTimeInterval timeFromNow = [self timeIntervalSinceNow]; if((timeFromNow < 0)) // In the past { timeFromNow = - timeFromNow; if ( (timeFromNow < kTenSeconds)) { if(!secondsStrings) { secondsStrings = [[NSArray arrayWithObjects:@"just now", //@"a few seconds ago", //@"right this instant", @"moments ago", nil] retain]; } unsigned int index = random() % ([secondsStrings count] - 1); return [secondsStrings objectAtIndex:index]; } if ( (timeFromNow < kOneMinute)) { if(!minuteStrings) { minuteStrings = [[NSArray arrayWithObjects:@"just now", @"very recently", @"in the last minute", nil] retain]; } unsigned int index = random() % ([minuteStrings count] - 1); return [minuteStrings objectAtIndex:index]; } if (timeFromNow < kFiveMinutes) { if(!fiveMinuteStrings) { fiveMinuteStrings = [[NSArray arrayWithObjects:@"just now", @"very recently", //@"in the last minute", @"a few minutes ago", //@"in the last five minutes", nil] retain]; } unsigned int index = random() % ([fiveMinuteStrings count] - 1); return [fiveMinuteStrings objectAtIndex:index]; } if (timeFromNow < kFifteenMinutes) { return @"in the last 15 minutes"; } if (timeFromNow < kHalfAnHour) { if(!halfHourStrings) { halfHourStrings = [[NSArray arrayWithObjects:@"in the last half hour", //@"in the last half an hour", @"in the last 30 minutes", //@"about half an hour ago", @"fairly recently", nil] retain]; } unsigned int index = random() % ([halfHourStrings count] - 1); return [halfHourStrings objectAtIndex:index]; } if (timeFromNow < kOneHour) { return @"in the last hour"; } if ((timeFromNow < (kOneHour + kFiveMinutes)) && (timeFromNow > (kOneHour - kFiveMinutes))) { return @"about an hour ago"; } if((timeFromNow < ((kOneHour*2.0f) + kFiveMinutes ))&& (timeFromNow > ((kOneHour*2.0f) - kFiveMinutes))) { return @"a couple of hours ago"; } // Now we're over an hour, we need to calculate a few specific dates to compare against NSDate *today = [NSDate date]; NSCalendar *gregorian = [[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] autorelease]; NSUInteger unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit; NSDateComponents* todayComponents = [gregorian components:unitFlags fromDate:today]; todayComponents.hour = 12; NSDate* noonToday = [gregorian dateFromComponents:todayComponents]; NSTimeInterval timeSinceNoonToday = [self timeIntervalSinceDate:noonToday]; if (timeSinceNoonToday > 0) // sometime since noon { if (timeSinceNoonToday > kOneHour * 9) // ie after 9pm today return @"earlier tonight"; if (timeSinceNoonToday > kOneHour * 7) // ie after 7pm today return @"earlier this evening"; if (timeSinceNoonToday < kOneHour * 1) // between noon and 1pm return @"early this afternoon"; return @"this afternoon"; } NSTimeInterval timeSinceMidnight = kHalfADay -timeSinceNoonToday; // Note sign is reversed. if ((timeSinceNoonToday < 0) & (timeSinceNoonToday > -kHalfADay)) // between midnight and noon today { if (timeSinceMidnight < kFiveMinutes) return @"around midnight"; if (timeSinceMidnight < kOneHour * 2) // up to 2am return @"very early this morning"; if (timeSinceMidnight < kOneHour * 5) // up to 5am return @"early this morning"; else if (timeSinceMidnight < kOneHour * 11) return @"late this morning"; else return @"this morning"; } // NSTimeInterval timeSinceNoonYesterday = timeSinceNoonToday - kOneDay; // timeSinceMidnight = -timeSinceMidnight; if (timeSinceMidnight < kOneHour * 24) // not the day before... { if (timeSinceMidnight < kFiveMinutes) return @"around midnight"; if (timeSinceMidnight < kFifteenMinutes) return @"just before midnight"; if (timeSinceMidnight < kOneHour * 2) // after 10pm return @"late last night"; if (timeSinceMidnight < kOneHour * 5) // After 7 return @"yesterday evening"; else if (timeSinceMidnight < kOneHour * 7) return @"yesterday evening"; // after 5pm else if (timeSinceMidnight < kOneHour * 7) return @"yesterday evening"; // after 5pm else if (timeSinceMidnight < kOneHour * 10) return @"yesterday afternoon"; // after 5pm else if (timeSinceMidnight < kOneHour * 12) return @"early yesterday afternoon"; // before 1pm else if (timeSinceMidnight < kOneHour * 13) return @"late yesterday morning"; // after 11m else if (timeSinceMidnight < kOneHour * 17) return @"yesterday morning"; else return @"early yesterday morning"; } NSDateFormatter* formatter = [[[NSDateFormatter alloc] init] autorelease]; int integerSeconds = timeSinceMidnight; int integerDay = kOneDay; int secondsIntoDay = integerSeconds % integerDay; NSString* formatString = @"last %@"; if (timeFromNow < kOneWeek) { if (secondsIntoDay < kFifteenMinutes) formatString = @"around midnight on %@"; //else if (secondsIntoDay < kFifteenMinutes) // formatString = @"just before midnight on %@"; else if (secondsIntoDay < kOneHour * 2) // after 10pm formatString = @"late on %@ night"; else if (secondsIntoDay < kOneHour * 5) // After 7 formatString = @"on %@ evening"; else if (secondsIntoDay < kOneHour * 10) formatString = @"on %@ afternoon"; // after 5pm else if (secondsIntoDay < kOneHour * 12) formatString = @"early on %@ afternoon"; // before 1pm else if (secondsIntoDay < kOneHour * 13) formatString = @"late on %@ morning"; // after 11am else if (secondsIntoDay < kOneHour * 17) formatString = @"on %@ morning"; else if (secondsIntoDay < kOneHour * 24) // not the day before... formatString = @"early on %@ morning"; [formatter setDateFormat:@"EEEE"]; /// EEEE is long format of day of the week see: http://unicode.org/reports/tr35/tr35-6.html#Date_Format_Patterns return [NSString stringWithFormat:formatString, [formatter stringFromDate: self]]; } //formatString = @"on %@ the week before last"; /*if (secondsIntoDay < kOneHour * 2) // after 10pm formatString = @"early on %@ the week before last"; else if (timeSinceMidnight > kOneHour * 13) formatString = @"late on %@ the week before last"; // after 11m*/ //if (timeFromNow < kOneWeek * 2) //{ // [formatter setDateFormat:@"EEE"]; /// EEE is short format of day of the week see: http://unicode.org/reports/tr35/tr35-6.html#Date_Format_Patterns // return [NSString stringWithFormat:formatString, [formatter stringFromDate: self]]; //} if (timeFromNow < kOneWeek * 2) { return @"the week before last"; } NSDateComponents* myComponents = [gregorian components:unitFlags fromDate:self]; int monthsAgo = myComponents.month - todayComponents.month; int yearsAgo = myComponents.year - todayComponents.year; if (yearsAgo == 0) { if (monthsAgo == 0) { if(myComponents.day > 22) return @"late this month"; if(myComponents.day < 7) { if(!earlyMonthStrings) { earlyMonthStrings = [[NSArray arrayWithObjects:@"earlier this month", //@"at the beginning of the month", @"early this month", nil] retain]; } unsigned int index = random() % ([earlyMonthStrings count] - 1); return [earlyMonthStrings objectAtIndex:index]; } return @"earlier this month"; } if (monthsAgo == 1) { if(myComponents.day > 22) return @"late last month"; if(myComponents.day < 7) return @"early last month"; return @"last month"; } formatString = @"in %@ this year"; /*if(myComponents.day > 22) formatString = @"late in %@ this year"; if(myComponents.day < 7) formatString = @"early in %@ this year";*/ [formatter setDateFormat:@"MMMM"]; /// MMM is longformat of month see: http://unicode.org/reports/tr35/tr35-6.html#Date_Format_Patterns return [NSString stringWithFormat:formatString, [formatter stringFromDate: self]]; } if (yearsAgo == 1) { formatString = @"in %@ last year"; /*if(myComponents.day > 22) formatString = @"late in %@ last year"; if(myComponents.day < 7) formatString = @"late in %@ last year";*/ [formatter setDateFormat:@"MMM"]; /// MMM is longformat of month see: http://unicode.org/reports/tr35/tr35-6.html#Date_Format_Patterns return [NSString stringWithFormat:formatString, [formatter stringFromDate: self]]; } // int daysAgo = integerSeconds / integerDay; // Nothing yet... [formatter setDateStyle:kCFDateFormatterMediumStyle]; //[formatter setTimeStyle:kCFDateFormatterShortStyle]; return [NSString stringWithFormat:@"on %@",[formatter stringFromDate: self]]; } else if(timeFromNow > 0) // The future { AICLog(kErrorLogEntry, @"FuzzyDates: Time marked as in the future: referenced date is %@, local time is %@", self, [NSDate date]); return @"moments ago"; } else return @"right now"; // this seems unlikely. return [self description]; // should never get here. } 

lo siento, tomó tanto tiempo publicar esto …

Esto se basa en el código de los hilos de fecha y hora Pretty and Humane. Agregué el manejo para “el último lunes, a las 5 p.m.”, porque me gusta más que hace x días. Esto maneja el pasado y el futuro hasta siglos. Me entusiasma el aspecto de la internacionalización, así que esto necesita mucho más trabajo eventualmente. Los cálculos están en la zona horaria local.

 public static class DateTimePretty { private const int SECOND = 1; private const int MINUTE = 60 * SECOND; private const int HOUR = 60 * MINUTE; private const int DAY = 24 * HOUR; private const int WEEK = 7 * DAY; private const int MONTH = 30 * DAY; private const int YEAR = 365; const string now = "just now"; const string secondsFuture = "in {0} seconds", secondsPast = "{0} seconds ago"; const string minuteFuture = "in about a minute", minutePast = "about a minute ago"; const string minutesFuture = "in about {0} minutes", minutesPast = "about {0} minutes ago"; const string hourFuture = "in about an hour", hourPast = "about an hour ago"; const string hoursFuture = "in about {0} hours", hoursPast = "about {0} hours ago"; const string tomorrow = "tomorrow, {0}", yesterday = "yesterday, {0}"; const string nextDay = "{0}", nextWeekDay = "next {0}", lastDay = "last {0}"; //const string daysFuture = "in about {0} days", daysPast = "about {0} days ago"; const string weekFuture = "in about a week", weekPast = "about a week ago"; const string weeksFuture = "in about {0} weeks", weeksPast = "about {0} weeks ago"; const string monthFuture = "in about a month", monthPast = "about a month ago"; const string monthsFuture = "in about {0} months", monthsPast = "about {0} months ago"; const string yearFuture = "in about a year", yearPast = "about a year ago"; const string yearsFuture = "in about {0} years", yearsPast = "about {0} years ago"; const string centuryFuture = "in about a century", centuryPast = "about a century ago"; const string centuriesFuture = "in about {0} centuries", centuriesPast = "about {0} centuries ago"; ///  /// Returns a pretty version of the provided DateTime: "42 years ago", or "in 9 months". ///  /// DateTime in local time format, not Utc /// A pretty string public static string GetPrettyDate(DateTime dateTime) { DateTime dateTimeNow = DateTime.Now; bool isFuture = (dateTimeNow.Ticks < dateTime.Ticks); var ts = isFuture ? new TimeSpan(dateTime.Ticks - dateTimeNow.Ticks) : new TimeSpan(dateTimeNow.Ticks - dateTime.Ticks); double delta = ts.TotalSeconds; if (delta < 10) return now; if (delta < 1 * MINUTE) return isFuture ? string.Format(secondsFuture, ts.Seconds) : string.Format(secondsPast, ts.Seconds); if (delta < 2 * MINUTE) return isFuture ? minuteFuture : minutePast; if (delta < 45 * MINUTE) return isFuture ? string.Format(minutesFuture, ts.Minutes) : string.Format(minutesPast, ts.Minutes); if (delta < 2 * HOUR) return isFuture ? hourFuture : hourPast; if (delta < 7 * DAY) { string shortTime = DateTimeFormatInfo.CurrentInfo.ShortTimePattern; string shortWeekdayTime = "dddd, " + shortTime; int dtDay = (int) dateTime.DayOfWeek; int nowDay = (int) dateTimeNow.DayOfWeek; if (isFuture) { if (dtDay == nowDay) { if (delta < DAY) return string.Format(hoursFuture, ts.Hours); else return string.Format(nextWeekDay, dateTime.ToString(shortWeekdayTime)); } else if (dtDay - nowDay == 1 || dtDay - nowDay == -6) return string.Format(tomorrow, dateTime.ToString(shortTime)); else return string.Format(nextDay, dateTime.ToString(shortWeekdayTime)); } else { if (dtDay == nowDay) { if (delta < DAY) return string.Format(hoursPast, ts.Hours); else return string.Format(lastDay, dateTime.ToString(shortWeekdayTime)); } else if (nowDay - dtDay == 1 || nowDay - dtDay == -6) return string.Format(yesterday, dateTime.ToString(shortTime)); else return string.Format(lastDay, dateTime.ToString(shortWeekdayTime)); } } //if (delta < 7 * DAY) // return isFuture ? string.Format(daysFuture, ts.Days) : string.Format(daysPast, ts.Days); if (delta < 4 * WEEK) { int weeks = Convert.ToInt32(Math.Floor((double) ts.Days / 30)); if (weeks <= 1) return isFuture ? weekFuture : weekPast; else return isFuture ? string.Format(weeksFuture, weeks) : string.Format(weeksPast, weeks); } if (delta < 12 * MONTH) { int months = Convert.ToInt32(Math.Floor((double) ts.Days / 30)); if (months <= 1) return isFuture ? monthFuture : monthPast; else return isFuture ? string.Format(monthsFuture, months) : string.Format(monthsPast, months); } // Switch to days to avoid overflow delta = ts.TotalDays; if (delta < 100 * YEAR) { int years = Convert.ToInt32(Math.Floor((double) ts.TotalDays / 365.25)); if (years <= 1) return isFuture ? yearFuture : yearPast; else return isFuture ? string.Format(yearsFuture, years) : string.Format(yearsPast, years); } else { int centuries = Convert.ToInt32(Math.Floor((double) ts.TotalDays / 365.2425)); if (centuries <= 1) return isFuture ? centuryFuture : centuryPast; else return isFuture ? string.Format(centuriesFuture, centuries) : string.Format(centuriesPast, centuries); } } } 

No estoy seguro de por qué dices que sería una horrible práctica de encoding. Cada una de las cadenas de retorno son en realidad un subconjunto del conjunto principal, por lo que puede hacerlo de forma bastante elegante en una cadena if / elseif.

 if timestamp < 5sec "A moment ago" elseif timestamp < 5min "Few minutes ago" elseif timestamp < 12hr && timestamp < noon "Today Morning" ... elseif timestamp < 1week "Few days ago" elseif timestamp < 1month "Few weeks ago" elseif timestamp < 6month "Few Months ago" ... else "Really really long time ago" 

En mi experiencia, estos tipos de generadores de fecha no son “borrosos” en absoluto. De hecho, son solo un montón de declaraciones basadas en bandas de tiempo. Por ejemplo, cualquier momento de menos de 30 segundos es “hace un momento”, 360 a 390 días es “hace apenas un año”, etc. Algunos de ellos usarán la fecha objective para calcular los nombres especiales (junio, miércoles, etc.). Perdón por lanzar una ilusión que tenías.

no hace falta decirlo (pero lo diré de todos modos) no use un bucle where que disminuya 365 días por año incluso en los años bisiestos de 366 días (o se encontrará en las filas de los desarrolladores de Zune)

aquí está la versión de CA #:

http://tiredblogger.wordpress.com/2008/08/21/creating-twitter-esque-relative-dates-in-c/

Sé que express tiempos como este se ha vuelto bastante popular últimamente, pero por favor considere la opción de cambiar las fechas relativas ‘difusas’ y las fechas absolutas normales.

Por ejemplo, es útil saber que un comentario fue hecho hace 5 minutos, pero es menos útil decirme que el comentario A fue hace 4 horas y el comentario B fue hace 9 horas cuando son las 11 AM y preferiría saber que el comentario A fue escrito cuando alguien se despertó esta mañana y el comentario B fue escrito por alguien quedándose hasta tarde (suponiendo que sé que están en mi zona horaria).

– EDITAR: al mirar más de cerca tu pregunta, pareces haber evitado esto hasta cierto punto al referirte a la hora del día en lugar de “X ago”, pero, por otro lado, puedes estar dando una falsa impresión si los usuarios están en otro momento. zona, ya que su “esta mañana” puede ser en el medio de la noche para el usuario correspondiente.

Puede ser genial boost los tiempos con la hora del día relativa dependiendo de la zona horaria del otro usuario, pero eso supone que los usuarios están dispuestos a suministrarlo y que es correcto.

No estaba contento con la solución en la otra pregunta. Así que hice mi propio uso de la clase de fecha y hora. IMO, es más limpio En mis pruebas funcionó como yo quería. Espero que esto ayude a alguien.

 DateTime now = DateTime.Now; long nowticks = now.Ticks; long thenticks = dt.Ticks; long diff = nowticks - thenticks; DateTime n = new DateTime(diff); if (n.Year > 1) { return n.Year.ToString() + " years ago"; } else if (n.Month > 1) { return n.Month.ToString() + " months ago"; } else if (n.Day > 1) { return n.Day.ToString() + " days ago"; } else if (n.Hour > 1) { return n.Hour.ToString() + " hours ago"; } else if (n.Minute > 1) { return n.Minute.ToString() + " minutes ago"; } else { return n.Second.ToString() + " seconds ago"; } 

Esto casi siempre se hace usando una statement de cambio gigante y es trivial de implementar.

Tenga en cuenta lo siguiente:

  • Siempre prueba primero el lapso de tiempo más pequeño
  • No olvides mantener tus cadenas localizables.

Puede encontrar útil la fuente de timeago . La descripción del complemento es “un complemento de jQuery que hace que sea más fácil admitir la actualización automática de las marcas de tiempo difusas (por ejemplo,” hace 4 minutos “o” hace aproximadamente 1 día “).

Básicamente es un puerto de JavaScript de la función distance_of_time_in_words de Rail metido en un plugin jQuery.

Mi compañía tiene esta biblioteca .NET que hace algo de lo que usted desea, ya que realiza un análisis de fecha y hora muy flexible (incluidos algunos formatos relativos), pero solo genera salidas no relativas.

Consulte Chrono para ver un analizador heurístico de fechas de Javascript.

Chrono es compatible con la mayoría de los formatos de fecha y hora, como:

 Today, Tomorrow, Yesterday, Last Friday, etc 17 August 2013 - 19 August 2013 This Friday from 13:00 - 16.00 5 days ago Sat Aug 17 2013 18:40:39 GMT+0900 (JST) 2014-11-30T08:15:30-05:30 

https://github.com/wanasit/chrono