Вот основной ответ с кодом, обратите внимание, что вы можете получить любое число с точностью до даты / времени, секунд и минут или секунд, минут и дней, где угодно, вплоть до лет (которые будут содержать 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», которая принимает исходную строку, аргумент, указывающий, что нужно заменить, и другой аргумент, указывающий, чем вы хотите заменить его, и он заменяет только последний случай. этой строки ... я включил свой, если у вас его нет или вы не хотите его реализовывать, так что вот, он будет работать "как есть", без изменений. Я знаю, что функция reverseit больше не нужна (существует в .net), но функции ReplaceLast и ReverseIt перенесены с дней до.net, поэтому, пожалуйста, извините, как это может выглядеть устаревшим (все еще работает на 100%, используя Их уже более десяти лет, можно гарантировать, что они не содержат ошибок) ... :). Кроме того, если вы используете VB6, вы можете использовать StrReverse (оборачивая его вокруг строки, расширенной с помощью метода расширения .ReverseIt) вместо использования функции ReverseIt () (предоставляется в качестве метода расширения). Таким образом, вместо выполнения sReplacable.ReverseIt, вы должны выполнить StrReverse (sReplacable), так как StrReverse () является встроенной функцией VB6 (и делает то же самое, переворачивает заданную строку и больше ничего не делает). Если вы используете StrReverse () вместо моей общей функции ReverseIt, не стесняйтесь удалять функцию / расширение ReverseIt. Функция StrReverse () должна быть доступна в .NET, если вы импортируете устаревшую библиотеку ms-visualbasic-dll. Не имеет значения в любом случае, я написал ReverseIt () еще до того, как узнал, что функция StrReverse () существовала, и использовал ее с тех пор по привычке (нет реальной причины использовать мой в отличие от встроенной универсальной функции). StrReverse) - на самом деле, я уверен, что StrReverse (или аналогичная, более новая, специфичная для .NET версия функции обращения строк) будет написана для большей эффективности :). веселит.
<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