Какова хорошая реализация функции IsLeapYear в VBA?
Редактировать:I запускал if-then и реализацию DateSerial с итерациями, заключенными в таймер, а DateSerial был быстрее в среднем на 1-2 мс (5 прогонов по 300 итераций, с одной формулой рабочего листа средней ячейки).
@ Джин, хороший вопрос, это было несколько лет назад. Я постараюсь вспомнить, когда я вернусь к работе на следующей неделе, чтобы провести еще несколько тестов, это было бы особенно хорошо, поскольку с тех пор пришло больше ответов.

Public Function isLeapYear(Yr As Integer) As Boolean
' returns FALSE if not Leap Year, TRUE if Leap Year
isLeapYear = (Month(DateSerial(Yr, 2, 29)) = 2)
End Function
Изначально я получил эту функцию с замечательного сайта Excel Чипа Пирсона.
креативное решение! Интересно, как он работает по сравнению с другими опубликованными.
При этом не учитываются все правила високосного года.
На самом деле, если вы изучите то, что они делают, это всегда сработает. Они проверяют, есть ли в феврале 29 дней, и это делает его високосным. По сути, он закладывает правила високосного года для Microsoft. У чипа много хороших решений.
public function isLeapYear (yr as integer) as boolean
isLeapYear = false
if (mod(yr,400)) = 0 then isLeapYear = true
elseif (mod(yr,100)) = 0 then isLeapYear = false
elseif (mod(yr,4)) = 0 then isLeapYear = true
end function
Википедия, чтобы узнать больше ... http://en.wikipedia.org/wiki/Leap_year
этот может быть даже более эффективным. Мне нравится, что он специально берет определение високосного года и превращает его в ответ.
переменная isLeap не используется
Если делиться на 4 без остатка, это еще не значит, что год високосный! 2100 год - не високосный. Разделение теста на 400 должно предшествовать делению на 4.
Если эффективность важна, а ожидаемый год является случайным, тогда может быть немного лучше сначала рассмотреть наиболее частый случай:
public function isLeapYear (yr as integer) as boolean
if (mod(yr,4)) <> 0 then isLeapYear = false
elseif (mod(yr,400)) = 0 then isLeapYear = true
elseif (mod(yr,100)) = 0 then isLeapYear = false
else isLeapYear = true
end function
Если целью является эффективность, вы можете избавиться от isLeapYear = false, поскольку логические значения по умолчанию равны false :)
Я нашел этот забавный на CodeToad:
Public Function IsLeapYear(Year As Varient) As Boolean
IsLeapYear = IsDate("29-Feb-" & Year)
End Function
Хотя я почти уверен, что использование IsDate в функции, вероятно, медленнее, чем несколько if, elseif.
В качестве варианта решения Чипа Пирсона вы также можете попробовать
Public Function isLeapYear(Yr As Integer) As Boolean
' returns FALSE if not Leap Year, TRUE if Leap Year
isLeapYear = (DAY(DateSerial(Yr, 3, 0)) = 29)
End Function
вы хотите использовать функцию ДЕНЬ вместо функции МЕСЯЦ?
Public Function ISLeapYear(Y As Integer) AS Boolean
' Uses a 2 or 4 digit year
'To determine whether a year is a leap year, follow these steps:
'1 If the year is evenly divisible by 4, go to step 2. Otherwise, go to step 5.
'2 If the year is evenly divisible by 100, go to step 3. Otherwise, go to step 4.
'3 If the year is evenly divisible by 400, go to step 4. Otherwise, go to step 5.
'4 The year is a leap year (it has 366 days).
'5 The year is not a leap year (it has 365 days).
If Y Mod 4 = 0 Then ' This is Step 1 either goto step 2 else step 5
If Y Mod 100 = 0 Then ' This is Step 2 either goto step 3 else step 4
If Y Mod 400 = 0 Then ' This is Step 3 either goto step 4 else step 5
ISLeapYear = True ' This is Step 4 from step 3
Exit Function
Else: ISLeapYear = False ' This is Step 5 from step 3
Exit Function
End If
Else: ISLeapYear = True ' This is Step 4 from Step 2
Exit Function
End If
Else: ISLeapYear = False ' This is Step 5 from Step 1
End If
End Function
Public Function isLeapYear(Optional intYear As Variant) As Boolean
If IsMissing(intYear) Then
intYear = Year(Date)
End If
If intYear Mod 400 = 0 Then
isLeapYear = True
ElseIf intYear Mod 4 = 0 And intYear Mod 100 <> 0 Then
isLeapYear = True
End If
End Function
Вот еще один простой вариант.
Leap_Day_Check = Day(DateValue("01/03/" & Required_Year) - 1)
Если Leap_Day_Check = 28, то это не високосный год, если 29, то год.
VBA знает, какая дата до 1 марта в году, и поэтому установит для нас это 28 или 29 февраля.
Я вижу много отличных концепций, которые указывают на дополнительное понимание и использование функций даты, у которых потрясающе учиться ... С точки зрения эффективности кода .. рассмотрите машинный код, необходимый для выполнения функции
а не сложные функции даты используйте только довольно быстрые целочисленные функции BASIC был построен на GOTO Я подозреваю, что что-то вроде ниже быстрее
Function IsYLeapYear(Y%) As Boolean
If Y Mod 4 <> 0 Then GoTo NoLY ' get rid of 75% of them
If Y Mod 400 <> 0 And Y Mod 100 = 0 Then GoTo NoLY
IsYLeapYear = True
ТОЛЬКО:
End Function
Поздний ответ на вопрос о производительности.
TL / DR: версии Математика примерно В 5 раз быстрее
Я вижу здесь две группы ответов
Я провел тесты времени для всех опубликованных ответов, и обнаружил, что методы Математика относятся к В 5 раз быстрее, чем методы даты / времени.
Затем я провел некоторую оптимизацию методов и придумал (хотите верьте, хотите нет, Integer в данном случае немного быстрее, чем Long, не знаю почему).
Function IsLeapYear1(Y As Integer) As Boolean
If Y Mod 4 Then Exit Function
If Y Mod 100 Then
ElseIf Y Mod 400 Then Exit Function
End If
IsLeapYear1 = True
End Function
Для сравнения подошел (очень мало отличий от опубликованной версии)
Public Function IsLeapYear2(yr As Integer) As Boolean
IsLeapYear2 = Month(DateSerial(yr, 2, 29)) = 2
End Function
Версии даты и времени, которые строят дату в виде строки, были сброшены со счетов, поскольку они снова намного медленнее.
Тест должен был получить IsLeapYear на 100..9999 лет, повторенный 1000 раз.
Полученные результаты
Тестовый код был
Sub Test()
Dim n As Long, i As Integer, j As Long
Dim d As Long
Dim t1 As Single, t2 As Single
Dim b As Boolean
n = 1000
Debug.Print "============================ = "
t1 = Timer()
For j = 1 To n
For i = 100 To 9999
b = IsYLeapYear1(i)
Next i, j
t2 = Timer()
Debug.Print 1, (t2 - t1) * 1000
t1 = Timer()
For j = 1 To n
For i = 100 To 9999
b = IsLeapYear2(i)
Next i, j
t2 = Timer()
Debug.Print 2, (t2 - t1) * 1000
End Sub
1-2 мс из скольких мс? то есть каков выигрыш в эффективности относительный? Просто любопытно!