Pregunta

¿Qué es una buena implementación de una función IsLeapYear en VBA?

Editar: Ejecuté if-then y DateSerial con iteraciones envueltas en un temporizador, y DateSerial fue más rápido en promedio de 1 a 2 ms (5 ejecuciones de 300 iteraciones, con 1 fórmula de hoja de cálculo de celda promedio también funciona).

¿Fue útil?

Solución

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  

Originalmente obtuve esta función del excelente sitio de Excel de Chip Pearson.

sitio de Pearson

Otros consejos

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

Wikipedia para más ... http://en.wikipedia.org/wiki/Leap_year

Si la eficiencia es una consideración y el año esperado es aleatorio, entonces podría ser un poco mejor hacer el caso más frecuente primero:

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

Como una variación de la solución Chip Pearson, también puedes probar

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

Encontré este divertido en CodeToad :

Public Function IsLeapYear(Year As Varient) As Boolean
  IsLeapYear = IsDate("29-Feb-" & Year)
End Function 

Aunque estoy bastante seguro de que el uso de IsDate en una función es probablemente más lento que un par de if, elseifs.

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

Veo muchos grandes conceptos que indican una comprensión extra y el uso de funciones de fecha que son excelentes para aprender de ... En términos de eficiencia de código.  considere el código de máquina necesario para que una función se ejecute

en lugar de funciones de fecha complejas usar solo funciones enteras bastante rápidas BASIC fue construido en GOTO Sospecho que algo como abajo es más rápido

  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

NoLY:

 End Function

Respuesta tardía para abordar la pregunta de rendimiento.

TL / DR: las versiones de Matemáticas son 5 veces más rápidas


Veo dos grupos de respuestas aquí

  1. Interpretación matemática de la definición del año bisiesto
  2. Utilice las funciones de Fecha / Hora de Excel para detectar el 29 de febrero (se dividen en dos campos: los que construyen una fecha como una cadena y los que no)

Hice pruebas de tiempo en todas las respuestas publicadas, y descubrí que los métodos Matemáticas son 5 veces más rápidos que los métodos de Fecha / Hora.


Luego hice una optimización de los métodos y se me ocurrió (créanlo o no Integer es ligeramente más rápido que Long en este caso, no sé por qué. )

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

Para comparar, aparecí (muy poca diferencia con la versión publicada)

Public Function IsLeapYear2(yr As Integer) As Boolean
    IsLeapYear2 = Month(DateSerial(yr, 2, 29)) = 2
End Function

Las versiones de Fecha / Hora que construyen una fecha como una cadena se descontaron porque nuevamente son mucho más lentas.

La prueba fue obtener IsLeapYear para los años 100..9999, repetidas 1000 veces

Resultados

  • Versión matemática: 640ms
  • Versión de fecha / hora: 3360ms

El código de prueba era

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

Aquí hay otra opción simple.

Leap_Day_Check = Day(DateValue("01/03/" & Required_Year) - 1)

Si Leap_Day_Check = 28 entonces no es un año bisiesto, si es 29 es.

VBA sabe lo que es la fecha anterior al 1 de marzo en un año, por lo que estableceremos que sea el 28 o el 29 de febrero para nosotros.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top