Нижче насправді найточніший спосіб це зробити, оскільки визначення поняття "1 місяць" змінюється залежно від місяця, а інші відповіді беруть це до уваги! Якщо ви хочете отримати більше інформації про проблему, яка не вбудована в рамки, ви можете прочитати цю публікацію: Реальний об’єкт часу з .Years & .Months (втім, читати цей пост не потрібно розуміти і використовувати функцію нижче, вона працює на 100%, без притаманних неточностей наближення, які інші люблять використовувати - і сміливо заміняйте функцію .ReverseIt на вбудовану функцію.
Зверніть увагу, що ви можете отримувати будь-яку кількість точності дат / разів, секунд і хвилин або секунд, хвилин і днів, де завгодно до років (що міститиме 6 частин / сегментів). Якщо ви вкажете два перших і йому більше року, він повернеться "1 рік і 3 місяці тому", а решту не поверне, тому що ви просили два сегменти. якщо йому всього кілька годин, то він повернеться лише "2 години та 1 хвилину тому". Звичайно, ті ж правила застосовуються, якщо ви вказали 1, 2, 3, 4, 5 або 6 сегментів (максимум в 6, оскільки секунди, хвилини, години, дні, місяці, роки складають лише 6 типів). Він також виправить проблеми граммерів, такі як "хвилини" проти "хвилини", залежно від того, якщо це 1 хвилина або більше, однакова для всіх типів, і "рядок", що генерується, завжди буде граматично правильним.
Ось деякі приклади використання: bAllowSegments визначає , скільки сегментів , щоб показати ... тобто: якщо 3, то повертати рядок буде ( в якості прикладу) ... "3 years, 2 months and 13 days"
(не включатиме в себе години, хвилини і секунди , якраз топ 3 категорія повертається), якщо, однак, дата була більш новою датою, наприклад, кілька днів тому із зазначенням тих самих сегментів (3) повернеться"4 days, 1 hour and 13 minutes ago"
замість цього, тож воно враховує все!
якщо bAllowSegments 2, він повернеться, "3 years and 2 months"
і якщо 6 (максимальне значення) повернеться "3 years, 2 months, 13 days, 13 hours, 29 minutes and 9 seconds"
, але, слід пам'ятати, що він буде NEVER RETURN
щось подібне, "0 years, 0 months, 0 days, 3 hours, 2 minutes and 13 seconds ago"
як він розуміє, що немає даних про дату у верхніх 3 сегментах і ігнорує їх, навіть якщо ви вказали 6 сегментів , тому не хвилюйтесь :). Звичайно, якщо є сегмент з 0 в ньому, він буде враховувати це при формуванні рядка, і відображатиметься як "3 days and 4 seconds ago"
і ігнорування частини "0 годин"! Насолоджуйтесь і будь ласка, коментуйте, якщо хочете.
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
Звичайно, вам знадобиться функція "ReplaceLast", яка бере рядок джерела, та аргумент, що вказує, що потрібно замінити, та інший аргумент із зазначенням того, на що ви хочете його замінити, і він замінює лише останню появу цього рядка ... я включив свій, якщо у вас його немає або не хочете його реалізовувати, тож ось він працює, "як є", не потребуючи жодних змін. Я знаю, що функція зворотного перегляду більше не потрібна (існує в .net), але функція ReplaceLast і ReverseIt переносяться з попередніх днів, тому, будь ласка, вибачте, як це може виглядати (все ще працює 100% тхо, використовую Їм вже більше десяти років, можу гарантувати, що вони позбавлені помилок) ... :). ура.
<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