lmi
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[lmi] Billing monthiversaries before age change [Was: Down a calendric r


From: Greg Chicares
Subject: [lmi] Billing monthiversaries before age change [Was: Down a calendric rabbit hole]
Date: Fri, 2 Jun 2017 01:31:57 +0000
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Thunderbird/45.8.0

On 2017-06-01 20:18, Greg Chicares wrote:
[...snip...]
> But now it seems that this will be unnecessary after all.

What we need instead is simpler than the work snipped above.

A group comprises many certificates (like a policy for each person),
which are issued at arbitrarily different dates. A group list bill
is wanted as of a single date, which cuts across certificate
anniversaries. Of any certificate's twelve monthly charges in a
billing year, we want to know how many occur before the certificate
anniversary (when the age changes for rating purposes) and after, so
that we can add different charges (at different rates) together.
Charges are levied at the beginning of each certificate month.

(Of course, if a bill covers less than a full year--three months,
or six, for example--then we just drop the charges after the end
of the billing period.)

I assumed that this would be difficult, so I started writing a unit
test and a trial implementation. After trying a hard implementation,
I saw an easy one. It's so easy that it's pointless to commit it to
the repository; but I had already worked through many examples and
written comments about them, so...in case this initially seems
difficult to anyone else, here's how I found it to be easy.

diff --git a/calendar_date_test.cpp b/calendar_date_test.cpp
index 02d5dd6..b5caf62 100644
--- a/calendar_date_test.cpp
+++ b/calendar_date_test.cpp
@@ -40,6 +40,7 @@ struct CalendarDateTest
     static void Test()
         {
         TestFundamentals();
+return; // Suppress time-consuming tests that are known to pass.
         TestAlgorithm199Bounds();
         TestYMDBounds();
         TestYmdToJdnAndJdnToYmd();
@@ -77,8 +78,144 @@ int test_main(int, char*[])
     return 0;
 }
 
+// Initial analysis, written before implementation:
+//
+//duration until age change
+//  duration_floor()
+//  // Anniversary of 'base_date' on or after which 'other_date' occurs.
+//  base_date:  cert eff date
+//  other_date: ListBillDate
+//  result: date on which age changes, i.e., next cert anniv
+//  then last cert anniv is easily obtained: it's (n-1) years after issue
+//then...
+//  years_and_months_since()
+//  // Full curtate years and months by which 'other_date' follows 'base_date'.
+//  base_date:  last cert anniv
+//  other_date: ListBillDate
+//  result: number of monthiversaries before age change
+
+int monthiversaries_before_age_change
+    (calendar_date const& cert_eff_date
+    ,calendar_date const& list_bill_date
+    )
+{
+#if 0
+    // Certificate anniversary on or before list bill date.
+    calendar_date const next_anniv = duration_ceiling
+        (cert_eff_date
+        ,list_bill_date
+        );
+    // Certificate anniversary on or after list bill date.
+    calendar_date const last_anniv = duration_floor
+        (cert_eff_date
+        ,list_bill_date
+        );
+    // no, duration_floor() returns an int, not a calendar date
+    //
+    // just call bracketing_anniversaries() instead?
+    // no, don't need both anniversaries
+
+    // Certificate anniversary on or after list bill date.
+    calendar_date const last_anniv = add_years
+        (cert_eff_date
+        ,duration_floor(cert_eff_date, list_bill_date)
+        ,true
+        );
+
+    // Full curtate months by which bill date follows last anniversary.
+    return years_and_months_since(last_anniv, list_bill_date).second;
+    // just use cert eff date, not last_anniv? --yes
+#endif // 0
+    // Full curtate months by which bill date follows last anniversary.
+    return 12 - years_and_months_since(cert_eff_date, list_bill_date).second;
+    // This is actually better, because certificate anniversaries occur
+    // an integral number of years after issue, which need not be one
+    // year after the last anniversary:
+    //   2004-02-29 issue
+    //   2007-02-28 third anniversary
+    //   2008-02-28 one year later--wrong answer
+    //   2008-02-29 fourth anniversary--right answer
+    // Consider:
+    //   2000-03-01 issue
+    //   2001-01-01 list bill date
+    // The bill date is ten months after the last anniversary, so
+    // two (12 - 10) months of the billing year precede the age change.
+}
+
 void CalendarDateTest::TestFundamentals()
 {
+    calendar_date cert_date;
+    calendar_date bill_date;
+    int n = 0;
+
+    // Dates coincide--the most obvious special case.
+    cert_date = calendar_date(2000,  1,  1);
+    bill_date = calendar_date(2000,  1,  1);
+    n = monthiversaries_before_age_change(cert_date, bill_date);
+    BOOST_TEST_EQUAL(12, n)
+
+    // Here, the first monthiversary that the bill encompasses is
+    // 2001-1-1, and the last is 2001-12-1. Age changes one day
+    // before the next bill, but that's outside the twelve
+    // monthiversaries included on the current bill, so all twelve
+    // months reflect the younger age.
+    cert_date = calendar_date(2000,  1,  1);
+    bill_date = calendar_date(2000,  1,  2);
+    n = monthiversaries_before_age_change(cert_date, bill_date);
+    BOOST_TEST_EQUAL(12, n)
+
+    // Age changes one day after the bill. The first month in the
+    // billing year reflects the age on the bill date, but age is
+    // one year greater for every subsequent month.
+    cert_date = calendar_date(2000,  1,  2);
+    bill_date = calendar_date(2001,  1,  1);
+    n = monthiversaries_before_age_change(cert_date, bill_date);
+    BOOST_TEST_EQUAL( 1, n)
+
+    cert_date = calendar_date(2000,  2,  1);
+    bill_date = calendar_date(2001,  1,  1);
+    n = monthiversaries_before_age_change(cert_date, bill_date);
+    BOOST_TEST_EQUAL( 1, n)
+
+    cert_date = calendar_date(2000,  3,  1);
+    bill_date = calendar_date(2001,  1,  1);
+    n = monthiversaries_before_age_change(cert_date, bill_date);
+    BOOST_TEST_EQUAL( 2, n)
+
+    cert_date = calendar_date(2000,  6, 30);
+    bill_date = calendar_date(2001,  1,  1);
+    n = monthiversaries_before_age_change(cert_date, bill_date);
+    BOOST_TEST_EQUAL( 6, n)
+
+    // The number of full years and months is calculated, but the
+    // years are discarded and only the months are used. The
+    // result is the same no matter how many years have passed.
+    cert_date = calendar_date(2000,  3,  1);
+    bill_date = calendar_date(2007,  1,  1);
+    n = monthiversaries_before_age_change(cert_date, bill_date);
+    BOOST_TEST_EQUAL( 2, n)
+
+    // Duplicate and modify this basic example if more tests are wanted.
+    // But that seems unnecessary: we just call years_and_months_since()
+    // (which is extensively tested elsewhere) and subtract the result
+    // from twelve.
+    cert_date = calendar_date(2000,  1,  1);
+    bill_date = calendar_date(2000,  1,  1);
+    n = monthiversaries_before_age_change(cert_date, bill_date);
+    BOOST_TEST_EQUAL(12, n)
+
+    // Out of range: the certificate hasn't yet been issued on the
+    // bill date, so it should not have been included in the census.
+
+    cert_date = calendar_date(2000,  1,  2);
+    bill_date = calendar_date(2000,  1,  1);
+    BOOST_TEST_THROW
+        (monthiversaries_before_age_change(cert_date, bill_date)
+        ,std::runtime_error
+        ,""
+        );
+
+return;
     calendar_date dublin_epoch;
     dublin_epoch.julian_day_number(2415020);





reply via email to

[Prev in Thread] Current Thread [Next in Thread]