Poniżej znajduje się najdokładniejszy sposób, w jaki możesz to zrobić, ponieważ definicja „1 miesiąca” zmienia się w zależności od tego, który to miesiąc, a inne odpowiedzi nie uwzględniają tego! Jeśli chcesz uzyskać więcej informacji na temat problemu, który nie jest wbudowany w framework, możesz przeczytać ten post: A Real Timespan Object With .Years & .Months (jednak przeczytanie tego posta nie jest konieczne, aby zrozumieć i używać poniższej funkcji, działa w 100%, bez nieodłącznych niedokładności przybliżeń, których inni lubią używać - i nie krępuj się, aby zastąpić funkcję .ReverseIt wbudowaną funkcją .Reverse, którą możesz mieć w swoim frameworku (jest tutaj tylko dla kompletności).
Należy pamiętać, że można uzyskać dowolną liczbę dokładności dat / godzin, sekund i minut lub sekund, minut i dni, w dowolnym miejscu do lat (co zawierałoby 6 części / segmentów). Jeśli określisz dwie górne i ma ponad rok, zwróci wartość „1 rok i 3 miesiące temu” i nie zwróci pozostałych, ponieważ zażądałeś dwóch segmentów. jeśli ma tylko kilka godzin, zwróci tylko „2 godziny i 1 minutę temu”. Oczywiście te same zasady obowiązują, jeśli określisz 1, 2, 3, 4, 5 lub 6 segmentów (maksimum to 6, ponieważ sekundy, minuty, godziny, dni, miesiące, lata tworzą tylko 6 typów). Poprawi również problemy gramatyczne, takie jak „minuty” w porównaniu z „minutami”, w zależności od tego, czy jest to 1 minuta czy więcej, tak samo dla wszystkich typów, a wygenerowany „ciąg znaków” będzie zawsze poprawny gramatycznie.
Oto kilka przykładów użycia: bAllowSegments określa, ile segmentów ma być pokazanych ... tj .: jeśli 3, to zwracany ciąg będzie (jako przykład) ... "3 years, 2 months and 13 days"
(nie będzie zawierał godzin, minut i sekund jako 3 pierwszych godzin kategorie są zwracane), jeśli jednak data była nowszą datą, na przykład kilka dni temu, określenie tych samych segmentów (3) zostanie zwrócone "4 days, 1 hour and 13 minutes ago"
, więc bierze wszystko pod uwagę!
jeśli bAllowSegments jest równe 2, zwróci, "3 years and 2 months"
a jeśli 6 (wartość maksymalna) zwróci "3 years, 2 months, 13 days, 13 hours, 29 minutes and 9 seconds"
, ale pamiętaj , że będzie to NEVER RETURN
coś takiego, "0 years, 0 months, 0 days, 3 hours, 2 minutes and 13 seconds ago"
ponieważ rozumie, że nie ma danych daty w 3 pierwszych segmentach i je ignoruje, nawet jeśli określisz 6 segmentów więc nie martw się :). Oczywiście, jeśli istnieje segment zawierający 0, weźmie to pod uwagę podczas tworzenia ciągu i wyświetli się jako "3 days and 4 seconds ago"
i ignorując część „0 godzin”! Ciesz się i komentuj, jeśli chcesz.
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
Oczywiście będziesz potrzebować funkcji "ReplaceLast", która pobiera ciąg źródłowy i argument określający, co ma zostać zastąpione, oraz inny argument określający, czym chcesz go zastąpić, i zastępuje tylko ostatnie wystąpienie tego ciągu ... dołączyłem mój, jeśli go nie masz lub nie chcesz go wdrażać, więc oto on, będzie działał „tak jak jest” bez konieczności modyfikacji. Wiem, że funkcja reverseit nie jest już potrzebna (istnieje w .net), ale funkcja ReplaceLast i ReverseIt zostały przeniesione z dni pre-.net, więc proszę wybaczyć, jak może wyglądać datowanie (nadal działa w 100%, używałem od ponad dziesięciu lat, gwarantuje, że są wolne od błędów) ... :). Twoje zdrowie.
<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