Abaixo, na verdade, é a maneira mais precisa de fazê-lo, uma vez que a definição de "1 mês" muda dependendo do mês em que é, e nenhuma das outras respostas leva isso em consideração! Se você deseja obter mais informações sobre o problema que não está incorporado na estrutura, pode ler esta postagem: Um objeto de período de tempo real com anos e meses (no entanto, não é necessário ler essa publicação para entender e usar a função abaixo, funciona 100%, sem as imprecisões inerentes à aproximação que outras pessoas adoram usar - e sinta-se à vontade para substituir a função .ReverseIt pela função .Reverse incorporada que você pode ter em sua estrutura (está aqui para ser completo).
Observe que você pode obter qualquer número de precisão de datas / horas, segundos e minutos ou segundos, minutos e dias, em qualquer lugar até anos (que conteria 6 partes / segmentos). Se você especificar os dois primeiros e tiver mais de um ano, ele retornará "1 ano e 3 meses atrás" e não retornará o restante porque você solicitou dois segmentos. se tiver apenas algumas horas, retornará apenas "2 horas e 1 minuto atrás". Obviamente, as mesmas regras se aplicam se você especificar 1, 2, 3, 4, 5 ou 6 segmets (no máximo 6, pois segundos, minutos, horas, dias, meses, anos e anos produzem apenas 6 tipos). Ele também corrige problemas gramaticais como "minutos" vs "minuto", dependendo de se levar 1 minuto ou mais, o mesmo para todos os tipos, e a "sequência" gerada sempre estará gramaticalmente correta.
Aqui estão alguns exemplos para uso: bAllowSegments identifica quantos segmentos serão exibidos ... ou seja: se 3, a string de retorno seria (como exemplo) ... "3 years, 2 months and 13 days"
(não incluirá horas, minutos e segundos como os três primeiros) categorias são retornadas); se, no entanto, a data era uma data mais recente, como algo há alguns dias atrás, especificar os mesmos segmentos (3) retornará "4 days, 1 hour and 13 minutes ago"
, então isso leva tudo em consideração!
se bAllowSegments for 2, ele retornará "3 years and 2 months"
e se 6 (valor máximo) retornará "3 years, 2 months, 13 days, 13 hours, 29 minutes and 9 seconds"
, mas lembre-se de que será NEVER RETURN
algo assim "0 years, 0 months, 0 days, 3 hours, 2 minutes and 13 seconds ago"
, pois entende que não há dados de data nos 3 principais segmentos e os ignora, mesmo se você especificar 6 segmentos , então não se preocupe :). Obviamente, se houver um segmento com 0, isso será levado em consideração ao formar a string e será exibido como "3 days and 4 seconds ago"
e ignorando a parte "0 horas"! Aproveite e comente se quiser.
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
Obviamente, você precisará de uma função "ReplaceLast", que aceita uma string de origem e um argumento que especifique o que precisa ser substituído, e outro argumento que especifique com o que você deseja substituí-lo, e substitui apenas a última ocorrência dessa string ... incluí o meu, se você não tiver um ou não quiser implementá-lo, então aqui está, ele funcionará "como está" sem nenhuma modificação necessária. Eu sei que a função reverseit não é mais necessária (existe em .net), mas a função ReplaceLast e ReverseIt são transferidas dos dias anteriores à net, portanto, desculpe o quão datada ela pode parecer (ainda funciona 100%, usando por mais de dez anos, pode garantir que eles estão livres de erros) ... :). Felicidades.
<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