A continuación se muestra la forma más precisa de hacerlo, ya que la definición de "1 mes" cambia según el mes que sea, ¡y ninguna de las otras respuestas tiene esto en cuenta! Si desea obtener más información sobre el problema que no está integrado en el marco, puede leer esta publicación: Un objeto de intervalo de tiempo real con .Años y meses (sin embargo, leer esa publicación no es necesario para comprender y usar la función a continuación, funciona al 100%, sin las inexactitudes inherentes de la aproximación que a otros les encanta usar, y siéntase libre de reemplazar la función .ReverseIt con la función incorporada .Reverse que puede tener en su marco (está aquí para completar).
Tenga en cuenta que puede obtener cualquier número de fechas / horas de precisión, segundos y minutos, o segundos, minutos y días, en cualquier lugar hasta años (que contendría 6 partes / segmentos). Si especifica los dos primeros y tiene más de un año, devolverá "hace 1 año y 3 meses" y no devolverá el resto porque ha solicitado dos segmentos. si solo tiene unas pocas horas, solo devolverá "hace 2 horas y 1 minuto". Por supuesto, se aplican las mismas reglas si especifica 1, 2, 3, 4, 5 o 6 segmentos (máximo a 6 porque segundos, minutos, horas, días, meses, años solo hacen 6 tipos). También corregirá problemas de gramática como "minutos" vs "minuto" dependiendo de si es 1 minuto o más, lo mismo para todos los tipos, y la "cadena" generada siempre será gramaticalmente correcta.
Estos son algunos ejemplos de uso: bAllowSegments identifica cuántos segmentos mostrar ... es decir: si es 3, entonces la cadena de retorno sería (como ejemplo) ... "3 years, 2 months and 13 days"
(no incluirá horas, minutos y segundos como los 3 primeros tiempos se devuelven las categorías), si, sin embargo, la fecha era una fecha más reciente, como algo hace unos días, especificando que los mismos segmentos (3) volverán en su "4 days, 1 hour and 13 minutes ago"
lugar, ¡así que tiene todo en cuenta!
si bAllowSegments es 2, volvería "3 years and 2 months"
y si volvería 6 (valor máximo) "3 years, 2 months, 13 days, 13 hours, 29 minutes and 9 seconds"
, pero recuerde que será NEVER RETURN
algo así, "0 years, 0 months, 0 days, 3 hours, 2 minutes and 13 seconds ago"
ya que entiende que no hay datos de fecha en los 3 segmentos principales y los ignora, incluso si especifica 6 segmentos , así que no te preocupes :). Por supuesto, si hay un segmento con 0, lo tendrá en cuenta al formar la cadena, y se mostrará "3 days and 4 seconds ago"
e ignorará la parte "0 horas". Disfruta y comenta si quieres.
Public Function RealTimeUntilNow(ByVal dt As DateTime, Optional ByVal bAllowSegments As Byte = 2) As String
' bAllowSegments identifies how many segments to show... ie: if 3, then return string would be (as an example)...
' "3 years, 2 months and 13 days" the top 3 time categories are returned, if bAllowSegments is 2 it would return
' "3 years and 2 months" and if 6 (maximum value) would return "3 years, 2 months, 13 days, 13 hours, 29 minutes and 9 seconds"
Dim rYears, rMonths, rDays, rHours, rMinutes, rSeconds As Int16
Dim dtNow = DateTime.Now
Dim daysInBaseMonth = Date.DaysInMonth(dt.Year, dt.Month)
rYears = dtNow.Year - dt.Year
rMonths = dtNow.Month - dt.Month
If rMonths < 0 Then rMonths += 12 : rYears -= 1 ' add 1 year to months, and remove 1 year from years.
rDays = dtNow.Day - dt.Day
If rDays < 0 Then rDays += daysInBaseMonth : rMonths -= 1
rHours = dtNow.Hour - dt.Hour
If rHours < 0 Then rHours += 24 : rDays -= 1
rMinutes = dtNow.Minute - dt.Minute
If rMinutes < 0 Then rMinutes += 60 : rHours -= 1
rSeconds = dtNow.Second - dt.Second
If rSeconds < 0 Then rSeconds += 60 : rMinutes -= 1
' this is the display functionality
Dim sb As StringBuilder = New StringBuilder()
Dim iSegmentsAdded As Int16 = 0
If rYears > 0 Then sb.Append(rYears) : sb.Append(" year" & If(rYears <> 1, "s", "") & ", ") : iSegmentsAdded += 1
If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn
If rMonths > 0 Then sb.AppendFormat(rMonths) : sb.Append(" month" & If(rMonths <> 1, "s", "") & ", ") : iSegmentsAdded += 1
If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn
If rDays > 0 Then sb.Append(rDays) : sb.Append(" day" & If(rDays <> 1, "s", "") & ", ") : iSegmentsAdded += 1
If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn
If rHours > 0 Then sb.Append(rHours) : sb.Append(" hour" & If(rHours <> 1, "s", "") & ", ") : iSegmentsAdded += 1
If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn
If rMinutes > 0 Then sb.Append(rMinutes) : sb.Append(" minute" & If(rMinutes <> 1, "s", "") & ", ") : iSegmentsAdded += 1
If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn
If rSeconds > 0 Then sb.Append(rSeconds) : sb.Append(" second" & If(rSeconds <> 1, "s", "") & "") : iSegmentsAdded += 1
parseAndReturn:
' if the string is entirely empty, that means it was just posted so its less than a second ago, and an empty string getting passed will cause an error
' so we construct our own meaningful string which will still fit into the "Posted * ago " syntax...
If sb.ToString = "" Then sb.Append("less than 1 second")
Return ReplaceLast(sb.ToString.TrimEnd(" ", ",").ToString, ",", " and")
End Function
Por supuesto, necesitará una función "Reemplazar por última vez", que toma una cadena de origen y un argumento que especifica lo que necesita ser reemplazado, y otro argumento que especifica con qué desea reemplazarlo, y solo reemplaza la última aparición de esa cadena ... He incluido el mío si no tiene uno o no desea implementarlo, por lo que aquí está, funcionará "tal cual" sin necesidad de modificaciones. Sé que la función reverseit ya no es necesaria (existe en .net) pero la función ReplaceLast y ReverseIt se transfieren de los días anteriores a.net, así que disculpe qué tan anticuada puede verse (todavía funciona al 100% aunque em durante más de diez años, puedo garantizar que están libres de errores) ... :). salud.
<Extension()> _
Public Function ReplaceLast(ByVal sReplacable As String, ByVal sReplaceWhat As String, ByVal sReplaceWith As String) As String
' let empty string arguments run, incase we dont know if we are sending and empty string or not.
sReplacable = sReplacable.ReverseIt
sReplacable = Replace(sReplacable, sReplaceWhat.ReverseIt, sReplaceWith.ReverseIt, , 1) ' only does first item on reversed version!
Return sReplacable.ReverseIt.ToString
End Function
<Extension()> _
Public Function ReverseIt(ByVal strS As String, Optional ByVal n As Integer = -1) As String
Dim strTempX As String = "", intI As Integer
If n > strS.Length Or n = -1 Then n = strS.Length
For intI = n To 1 Step -1
strTempX = strTempX + Mid(strS, intI, 1)
Next intI
ReverseIt = strTempX + Right(strS, Len(strS) - n)
End Function