Di seguito è riportato il modo più accurato in cui puoi farlo, poiché la definizione di "1 mese" cambia a seconda del mese in cui si trova e nessuna delle altre risposte ne tiene conto! Se desideri ulteriori informazioni sul problema che non è integrato nel framework, puoi leggere questo post: Un vero oggetto a tempo con .Years & .Months (tuttavia, leggere quel post non è necessario per comprendere e utilizzare la funzione di seguito, funziona al 100%, senza le imprecisioni intrinseche dell'approssimazione che altri amano usare - e sentiti libero di sostituire la funzione .ReverseIt con la funzione .Reverse integrata che potresti avere sul tuo framework (è qui solo per completezza).
Si noti che è possibile ottenere un numero qualsiasi di precisione di date / orari, secondi e minuti o secondi, minuti e giorni, ovunque fino a anni (che conterrebbe 6 parti / segmenti). Se specifichi i primi due e ha più di un anno, restituirà "1 anno e 3 mesi fa" e non restituirà il resto perché hai richiesto due segmenti. se ha solo poche ore, restituirà solo "2 ore e 1 minuto fa". Naturalmente, si applicano le stesse regole se si specificano 1, 2, 3, 4, 5 o 6 segmets (massimo a 6 perché secondi, minuti, ore, giorni, mesi, anni ne fanno solo 6 tipi). Correggerà anche problemi di grammatica come "minuti" vs "minuto" a seconda che sia di 1 minuto o più, lo stesso per tutti i tipi e la "stringa" generata sarà sempre grammaticalmente corretta.
Ecco alcuni esempi di utilizzo: bAllowSegments identifica il numero di segmenti da mostrare ... ovvero: se 3, la stringa di ritorno sarebbe (ad esempio) ... "3 years, 2 months and 13 days"
(non includerà ore, minuti e secondi come i primi 3 tempi vengono restituite le categorie), tuttavia, se la data era una data più recente, come qualcosa di qualche giorno fa, specificando invece gli stessi segmenti (3) verrà restituita "4 days, 1 hour and 13 minutes ago"
, quindi tiene conto di tutto!
se bAllowSegments è 2 restituisce "3 years and 2 months"
e se restituisce 6 (valore massimo) "3 years, 2 months, 13 days, 13 hours, 29 minutes and 9 seconds"
, ma si ricorda che farà NEVER RETURN
qualcosa del genere "0 years, 0 months, 0 days, 3 hours, 2 minutes and 13 seconds ago"
in quanto comprende che non ci sono dati relativi alla data nei primi 3 segmenti e li ignora, anche se si specificano 6 segmenti , quindi non preoccuparti :). Ovviamente, se c'è un segmento con 0, ne terrà conto quando si forma la stringa e verrà visualizzato come "3 days and 4 seconds ago"
e ignorando la parte "0 ore"! Divertiti e per favore commenta se vuoi.
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
Ovviamente, avrai bisogno di una funzione "ReplaceLast", che accetta una stringa di origine e un argomento che specifica ciò che deve essere sostituito, e un altro argomento che specifica con cosa vuoi sostituirlo, e sostituisce solo l'ultima occorrenza di quella stringa ... ho incluso il mio se non ne hai uno o non vuoi implementarlo, quindi eccolo, funzionerà "così com'è" senza modifiche necessarie. So che la funzione di inversione non è più necessaria (esiste in .net) ma le funzioni ReplaceLast e ReverseIt sono riportate dai giorni pre.net, quindi scusa la data in cui potrebbe apparire (funziona ancora al 100%, usando em per oltre dieci anni, possono garantire che sono privi di bug) ... :). Saluti.
<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