Hintergrund
Wie Sie wissen, "ist Faulheit der Motor des Fortschritts." Bei meiner Arbeit hatte ich einmal ein Problem, als es notwendig war, eine Tabelle zur Berechnung der Zinsen für einen Darlehensvertrag zu erstellen, in der die tatsächliche Anzahl von Tagen in einem Jahr für die Basis hätte sein sollen. Die Unannehmlichkeit war, dass Sie die Schaltjahre nicht vergessen und die Tage, die zum Schaltjahr gehören, und die Tage der Nicht-Schaltjahre trennen mussten. Eine einfache Formel wurde geschrieben, aber später fand ich heraus, dass die Berechnung von Schaltjahren nicht so einfach ist.
Beschreibung des Problems
Ich wollte die Formel verbessern. Im Internet habe ich viele Programmtexte gefunden, in denen die Anzahl der Schaltjahre und Nicht-Schaltjahre und -tage in einem Zeitraum berechnet wurde. Leider war ich nicht zufrieden damit, dass die Geschwindigkeit dieser Funktionen von der Anzahl der Jahre im Zeitraum abhing. Und ich wollte, dass die Funktion genauso schnell funktioniert, egal wie viele Jahre in diesem Zeitraum. Aber während der Entwicklung musste ich den zulässigen Zeitraum der Funktion begrenzen.
Grund 1
Die meisten Länder leben nach dem Gregorianischen Kalender, dessen Regeln für Schaltjahre bereits 1582 von Papst Gregor XIII. Festgelegt wurden :
1. Ein Jahr, dessen Zahl durch 400 teilbar ist, ist ein Schaltjahr;
2. Der Rest der Jahre, deren Anzahl ein Vielfaches von 100 ist, sind Nicht-Schaltjahre (zum Beispiel die Jahre 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 :
:
:
, , :
, , :
, , , 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- " ".
, , :
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- " ".
– . 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 .
Unten finden Sie einen Link zum Github, in dem die vollständige Implementierung der Funktionen zur Berechnung von Schalt- und gemeinsamen Tagen in einem Zeitraum in VBA für die Arbeit in Excel beschrieben ist. Sie können diese Funktion einfach in Ihre Lieblingssprache portieren und in Ihren Projekten verwenden.
Quellen und zusätzliche Links
-
Excel geht fälschlicherweise davon aus, dass 1900 ein Schaltjahr ist.
Wikipedia-Artikel "Gregorianischer Kalender"