Voici en fait la façon la plus précise de le faire, car la définition de «1 mois» change en fonction du mois, et aucune des autres réponses n'en tient compte! Si vous voulez plus d'informations sur le problème qui n'est pas intégré au framework, vous pouvez lire cet article: Un objet Real Timespan avec .Years & .Months (cependant, la lecture de cet article n'est pas nécessaire pour comprendre et utiliser la fonction ci-dessous, cela fonctionne à 100%, sans les inexactitudes inhérentes à l'approximation que les autres aiment utiliser - et n'hésitez pas à remplacer la fonction .ReverseIt par la fonction .Reverse intégrée que vous pouvez avoir sur votre framework (c'est juste ici pour l'exhaustivité).
Veuillez noter que vous pouvez obtenir n'importe quel nombre de dates / heures de précision, secondes et minutes, ou secondes, minutes et jours, n'importe où jusqu'à des années (qui contiendraient 6 parties / segments). Si vous spécifiez les deux premiers et qu'il date de plus d'un an, il renverra "Il y a 1 an et 3 mois" et ne renverra pas le reste, car vous avez demandé deux segments. s'il n'a que quelques heures, il ne retournera que "il y a 2 heures et 1 minute". Bien sûr, les mêmes règles s'appliquent si vous spécifiez 1, 2, 3, 4, 5 ou 6 segments (maximum à 6 car les secondes, minutes, heures, jours, mois, années ne font que 6 types). Il corrigera également les problèmes de grammaire tels que "minutes" vs "minute" selon qu'il s'agit d'une minute ou plus, même pour tous les types, et la "chaîne" générée sera toujours grammaticalement correcte.
Voici quelques exemples d'utilisation: bAllowSegments identifie le nombre de segments à afficher ... c'est-à-dire: si 3, alors la chaîne de retour serait (à titre d'exemple) ... "3 years, 2 months and 13 days"
(n'inclut pas les heures, les minutes et les secondes comme les 3 premières heures catégories sont renvoyées), si toutefois la date était une date plus récente, comme quelque chose d'il y a quelques jours, la spécification des mêmes segments (3) reviendra à la "4 days, 1 hour and 13 minutes ago"
place, donc il prend tout en compte!
si bAllowSegments vaut 2, il retournera "3 years and 2 months"
et si 6 (valeur maximale) retournera "3 years, 2 months, 13 days, 13 hours, 29 minutes and 9 seconds"
, mais rappelez-vous que ce sera NEVER RETURN
quelque chose comme ça "0 years, 0 months, 0 days, 3 hours, 2 minutes and 13 seconds ago"
car il comprend qu'il n'y a pas de données de date dans les 3 premiers segments et les ignore, même si vous spécifiez 6 segments , alors ne vous inquiétez pas :). Bien sûr, s'il y a un segment avec 0 dedans, il en tiendra compte lors de la formation de la chaîne, et s'affichera comme "3 days and 4 seconds ago"
et en ignorant la partie "0 heures"! Profitez-en et commentez si vous le souhaitez.
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
Bien sûr, vous aurez besoin d'une fonction "ReplaceLast", qui prend une chaîne source, et un argument spécifiant ce qui doit être remplacé, et un autre argument spécifiant par quoi vous voulez le remplacer, et il ne remplace que la dernière occurrence de cette chaîne ... j'ai inclus mon un si vous n'en avez pas ou ne voulez pas l'implémenter, alors le voici, il fonctionnera "tel quel" sans aucune modification nécessaire. Je sais que la fonction reverseit n'est plus nécessaire (existe dans .net) mais les fonctions ReplaceLast et ReverseIt sont reportées des jours pre-.net, alors veuillez excuser à quel point elle peut sembler datée (fonctionne toujours à 100%, utilise em depuis plus de dix ans, peut garantir qu'ils sont sans bogue) ... :). à votre santé.
<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