Fungsi jumlah hari kabisat dalam suatu periode

Latar Belakang

Seperti yang Anda ketahui, "Kemalasan adalah mesin kemajuan." Dalam pekerjaan saya, saya pernah menghadapi masalah ketika perlu membuat tabel untuk menghitung bunga pada perjanjian pinjaman, di mana jumlah hari sebenarnya dalam setahun seharusnya menjadi basis. Ketidaknyamanannya adalah Anda tidak boleh melupakan tahun kabisat dan memisahkan hari-hari yang termasuk tahun kabisat dan hari-hari tahun bukan kabisat. Sebuah rumus sederhana telah ditulis, tetapi kemudian saya menemukan bahwa menghitung tahun kabisat tidaklah sesederhana itu.





Deskripsi masalah

Saya ingin meningkatkan formula. Di Internet, saya menemukan banyak teks program di mana jumlah tahun dan hari kabisat atau bukan kabisat dalam suatu periode dihitung. Namun, sayangnya, saya tidak puas dengan fakta bahwa kecepatan fungsi-fungsi ini bergantung pada jumlah tahun periode tersebut. Dan saya ingin fungsinya bekerja dengan cepat, tidak peduli berapa tahun dalam periode tersebut. Tetapi selama pengembangan, saya harus membatasi periode yang diizinkan dari fungsi tersebut.





Alasan 1

Sebagian besar negara hidup menurut kalender Gregorian, aturan tahun kabisat yang ditentukan pada tahun 1582 oleh Paus Gregorius XIII :





1. Tahun yang jumlahnya habis dibagi 400 adalah tahun kabisat;





2. Sisa tahun, yang jumlahnya merupakan kelipatan 100, adalah tahun bukan kabisat (misalnya, tahun 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 :





Lompatan ^ {total} _ {hari} = B ^ {dari tanggal mulai} _ {hari sampai akhir kuartet} + B ^ {di antaranya.  quartets} _ {days} + B ^ {dari awal kuartal terakhir} _ {hingga hari terakhir}

:





:





Dalam ^ {from the start date} _ {to the end of quartet} = Date_ {end} - Date_ {start}

, , :





Dalam ^ {from the start date} _ {sampai akhir kuartet} = Date_ {end} - 31 Des (Year_ {date end} -1)

, , :





Dalam ^ {from the start date} _ {to the end of quartet} = 31 Des Year_ {start date} - Date_ {start}

, , , 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- " ".





, , :





Dalam ^ {dari awal kuartet} _ {hingga tanggal akhir} = Tanggal_ {akhir} - 31 Des (Tahun_ {tanggal akhir} -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 ^ {di antaranya.  kuartet} _ {hari} = 366 * K_ {kuartet} - K_ {100 tahun penuh} + K_ {400 tahun penuh}

– . 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 .





Di bawah ini adalah tautan ke github, di mana implementasi penuh fungsi untuk menghitung lompatan dan hari-hari umum dalam suatu periode di VBA diletakkan, dirancang untuk bekerja di Excel. Anda dapat dengan mudah mem-port fungsi ini ke bahasa favorit Anda dan menggunakannya dalam proyek Anda.





Github





Sumber dan tautan tambahan

  1. Artikel dari majalah "Buku Umum" "Kami menghitung bunga pinjaman: hari pertama, hari terakhir"





  2. Excel salah mengasumsikan 1900 sebagai tahun kabisat.





  3. Artikel Wikipedia "Kalender Gregorian"













All Articles