Как найти високосный год в VBA?
-
02-07-2019 - |
Вопрос
Какова хорошая реализация функции IsLeapYear в VBA?
Редактировать: Я запустил if-then и реализацию DateSerial с итерациями, заключенными в таймер, и DateSerial был быстрее в среднем на 1-2 мс (5 прогонов по 300 итераций, при этом также работала 1 средняя формула таблицы ячеек).
Решение
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 Чипа Пирсона.
Другие советы
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
Если учитывается эффективность, а ожидаемый год является случайным, то, возможно, лучше сначала рассмотреть наиболее частый случай:
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
В качестве варианта решения Чипа Пирсона вы также можете попробовать
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(Year As Varient) As Boolean
IsLeapYear = IsDate("29-Feb-" & Year)
End Function
Хотя я почти уверен, что использование IsDate в функции, вероятно, медленнее, чем пара if и elseif.
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
Я вижу много замечательных концепций, которые указывают на дополнительное понимание и использование функций даты, которые потрясающе учиться у ...С точки зрения эффективности кода..рассмотрите машинный код, необходимый для выполнения функции
Вместо сложных функций даты используются только довольно быстрые целочисленные функции.
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 раз быстрее
Я вижу здесь две группы ответов
- Математическая интерпретация определения високосного года
- Используйте функции даты и времени Excel для определения 29 февраля (они делятся на два лагеря:те, которые создают дату в виде строки, и те, которые этого не делают)
Я провел тесты времени для всех опубликованных ответов и обнаружил Математика методы о в 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
Версии Date/Time, которые создают дату в виде строки, были исключены, поскольку они снова намного медленнее.
Испытание заключалось в том, чтобы получить IsLeapYear
за годы 100..9999, повторено 1000 раз
Полученные результаты
- Математическая версия:640 мс
- Версия даты/времени:3360 мс
Тестовый код был
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
Вот еще один простой вариант.
Leap_Day_Check = Day(DateValue("01/03/" & Required_Year) - 1)
Если Leap_Day_Check = 28, то год не високосный, если 29, то високосный.
VBA знает, какая дата в году будет перед 1 марта, поэтому установит для нас это 28 или 29 февраля.