Función del número de días bisiestos en un período

Antecedentes

Como saben, "la pereza es el motor del progreso". En mi trabajo, una vez enfrenté un problema cuando era necesario elaborar una tabla para calcular los intereses de un contrato de préstamo, donde el número real de días en un año debería haber sido para la base. El inconveniente era que no había que olvidarse de los años bisiestos y separar los días que pertenecen al año bisiesto y los días de los años no bisiestos. Se escribió una fórmula simple, pero luego descubrí que calcular los años bisiestos no es tan simple.





Descripción del problema

Quería mejorar la fórmula. En Internet, encontré muchos textos de programas en los que se calculaba el número de años y días bisiestos o no bisiestos en un período. Pero, lamentablemente, no me satisfizo el hecho de que la velocidad de estas funciones dependiera de la cantidad de años en el período. Y quería que la función funcionara con la misma rapidez, sin importar cuántos años en el período. Pero durante el desarrollo, tuve que limitar el período permitido de la función.





Razón 1

La mayoría de los países viven de acuerdo con el calendario gregoriano, cuyas reglas de los años bisiestos fueron determinadas en 1582 por el Papa Gregorio XIII :





1. Un año cuyo número es divisible por 400 es un año bisiesto;





2. El resto de los años, cuyo número es un múltiplo de 100, son años no bisiestos (por ejemplo, los años 1700, 1800, 1900, 2100, 2200,2300)





3.       , 4, - .





2900, 3200, 4000, 01.01.2900.





2

Excel VBA (Visual Basic for Applications). , MS Office, .





Excel , 1900 1904. 1900. , 1 01 1900 , 2 – 2 .





VBA CDate(expression), Date . 1, Date 31 1899 . 60 CDate 28.02.1900, 29.02.1900 (, , 1900 ). , 01 1900 .





Excel, Microsoft , . 01 1900 .





, () , .





, 4, 4 () 1 . 1- 1 4, 2- 5 8 .





1 4 (, 2021 506- , 1)





3 :





Leap ^ {total} _ {days} = B ^ {desde la fecha de inicio} _ {días hasta el final del cuarteto} + B ^ {en el medio.  cuartetos} _ {días} + B ^ {desde el comienzo del último trimestre} _ {hasta el día del final}

:





:





En ^ {desde la fecha de inicio} _ {hasta el final del cuarteto} = Fecha_ {fin} - Fecha_ {inicio}

, , :





En ^ {desde la fecha de inicio} _ {hasta el final del cuarteto} = Fecha_ {final} - 31 de diciembre (Año_ {fecha final} -1)

, , :





En ^ {desde la fecha de inicio} _ {hasta el final del cuarteto} = 31 de diciembre Año_ {fecha de inicio} - Fecha_ {inicio}

, , , 1- 366 ( 1 3, ).





VBA:





Private Function first_quartet_leap_year_days(ByVal d_begin As Date, ByVal d_end As Date) As Long

    Dim result As Long
    result = 0
    
    Dim year_diff As Long
    Dim quartet_index_diff As Long
    
    year_diff = year(d_end) - year(d_begin)
    quartet_index_diff = quartet_index(year(d_end)) - quartet_index(year(d_begin))
    
    If year_diff = 0 And is_year_leap(d_begin) Then
        result = DateDiff("d", d_begin, d_end)
        first_quartet_leap_year_days = result
        Exit Function
    End If
    
    If quartet_index_diff = 0 Then
    
        If is_year_leap(d_begin) Then
            result = DateDiff("d", d_begin, CDate(DateSerial(year(d_begin), 12, 31)))
            first_quartet_leap_year_days = result
            Exit Function
        
        End If
        
        If is_year_leap(d_end) Then
            result = DateDiff("d", CDate(DateSerial(year(d_end) - 1, 12, 31)), d_end)
            first_quartet_leap_year_days = result
            Exit Function
        End If
        
    Else
    
        If is_year_leap(d_begin) Then
            result = DateDiff("d", d_begin, CDate(DateSerial(year(d_begin), 12, 31)))
            first_quartet_leap_year_days = result
            Exit Function
        Else
        
            If Not is_quartet_noleap(quartet_index(year(d_begin))) Then
                result = 366
                first_quartet_leap_year_days = result
                Exit Function
            End If
            
        End If
        
    End If

    first_quartet_leap_year_days = result
    
End Function
      
      







>0, 3- " ".





, , :





En ^ {desde el comienzo del cuarteto} _ {hasta la fecha de finalización} = Fecha_ {fin} - 31 de diciembre (Año_ {fecha de finalización} -1)
Private Function last_quartet_leap_year_days(ByVal d_begin As Date, ByVal d_end As Date) As Long
    
    Dim result As Long
    result = 0
     
    Dim quartet_index_diff As Long
       
    quartet_index_diff = quartet_index(year(d_end)) - quartet_index(year(d_begin))
    
    If quartet_index_diff > 0 Then
    
        If is_year_leap(d_end) Then
            result = DateDiff("d", CDate(DateSerial(year(d_end) - 1, 12, 31)), d_end)
        End If
        
    End If
    
     
    last_quartet_leap_year_days = result
    
End Function
      
      







>1, 2- " ".





B ^ {en el medio.  cuartetos} _ {días} = 366 * K_ {cuartetos} - K_ {100 años completos} + K_ {400 años completos}

– . 1999 – 19, 2001 – 20, 1.





400-.





Private Function middle_quartets_leap_year_days(ByVal d_begin As Date, ByVal d_end As Date) As Long
    
    Dim quartet_count As Long
    
    quartet_count = middle_quartets_count(d_begin, d_end)
    
    If quartet_count = 0 Then
    
        middle_quartets_leap_year_days = 0
        Exit Function
        
    End If
    
    Dim q_begin, q_end As Long
    
    q_begin = quartet_index(year(d_begin))
    q_end = quartet_index(year(d_end)) - 1
    
    Dim quot_25, quot_100 As Integer
    
    quot_25 = WorksheetFunction.Quotient(q_end, 25) - WorksheetFunction.Quotient(q_begin, 25)
    quot_100 = WorksheetFunction.Quotient(q_end, 100) - WorksheetFunction.Quotient(q_begin, 100)
    
    Dim result As Long
    
    result = (quartet_count - quot_25 + quot_100) * 366
    
    middle_quartets_leap_year_days = result
        
End Function
      
      







:





Public Function LEAP_DAYS(ByVal val_begin As Long, ByVal val_end As Long, Optional count_first_day = 0, Optional count_last_day = 1) As Long
    
    Dim d_begin, d_end As Date
    
    count_first_day = IIf(count_first_day <> 0, 1, 0)
    count_last_day = IIf(count_last_day <> 0, 1, 0)
    
    
    d_begin = CDate(val_begin)
    d_end = CDate(val_end)
    
    Dim check_error As Variant
    check_error = check_constrains(d_begin, d_end)
    
    If IsError(check_error) Then
        LEAP_DAYS = check_error
        Exit Function
    End If
    
    Dim result As Long
    result = 0
    
    If is_year_leap(d_begin) And count_first_day = 1 Then result = result + 1
    If is_year_leap(d_end) And count_last_day = 0 Then result = result - 1
    
    result = result + first_quartet_leap_year_days(d_begin, d_end) _
            + middle_quartets_leap_year_days(d_begin, d_end) _
            + last_quartet_leap_year_days(d_begin, d_end)
    
    LEAP_DAYS = result
    
End Function
      
      



count_first_day



count_last_day



1 0. Date . .





, , . 23-24 .





, .





, . , (2900 - 1900). , 2900 .





A continuación se muestra un enlace al github, donde se presenta la implementación completa de las funciones para calcular los días bisiestos y comunes en un período en VBA, diseñado para funcionar en Excel. Puede transferir fácilmente esta función a su idioma favorito y utilizarla en sus proyectos.





Github





Fuentes y enlaces adicionales

  1. Un artículo de la revista "Libro General" "Calculamos los intereses del préstamo: el primer día, el último día"





  2. Excel asume incorrectamente que 1900 es un año bisiesto.





  3. Artículo de Wikipedia "Calendario gregoriano"













All Articles