Billing Period

May 18, 2018

We provide a function period that takes a year, month and day and returns the billing period within the requested year:

(define (period year month day)
  (define (first-of-month? julian)
    (call-with-values
      (lambda () (gregorian julian))
      (lambda (year month day) (= day 1))))
  (do ((j (julian year 1 1) (+ j 1))
       (p 0 (+ p (if (or (= (modulo j 7) 5) (first-of-month? j)) 1 0))))
      ((< (julian year month day) j) p)))

Here’s an example:

> (period 2018 5 18)
24

The 24 days on which billing periods start are:

1/1, 1/6, 1/13, 1/20, 1/27
2/1, 2/3, 2/10, 2/17, 2/24
3/1, 3/3, 3/10, 3/17, 3/24, 3/31
4/1, 4/7, 4/14, 4/21, 4/28
5/1, 5/5, 5/12

It is easy to see that the program is correct; it examines each day from January 1st to the requested date and increments a counter if the day is the 1st of a month or a Saturday. There’s another algorithm that divides the number of days from January 1st to the requested date by 7, then adjusts for Saturdays at the ends of the range, but after several tries I was unable to get it working, so I gave up. The moral of the story is that you want to write clear, simple code.

You can run the program at https://ideone.com/UhtqXu.

Advertisements

Pages: 1 2

5 Responses to “Billing Period”

  1. Steve said

    Mumps/Cache version

    billingperiod ;
             q
             ;
    go(dt)   ;
             n adt,dti,dts,dtsi,i,period
             s dti=$zdh(dt),dts="01/01/"_$p(dt,"/",3),dtsi=$zdh(dts)
             s period=0
             f i=dtsi:1:dti d
             . s adt=$zd(i)
             . i $p(adt,"/",2)="01" s period=period+1
             . e  i i#7=2 s period=period+1 ; Saturday
             q period
     
    ---
    
    f dt="01/01/2018","01/05/2018","01/06/2018","01/12/2018","01/13/2018","01/19/2018","01/20/2018","01/26/2018","01/27/2018","01/31/2018","02/01/2018","08/31/2018","09/01/2018","09/02/2018" w !,dt," -- ",$$go^billingperiod(dt)
    
    01/01/2018 -- 1
    01/05/2018 -- 1
    01/06/2018 -- 2
    01/12/2018 -- 2
    01/13/2018 -- 3
    01/19/2018 -- 3
    01/20/2018 -- 4
    01/26/2018 -- 4
    01/27/2018 -- 5
    01/31/2018 -- 5
    02/01/2018 -- 6
    08/31/2018 -- 42
    09/01/2018 -- 43
    09/02/2018 -- 43
    
  2. WWW.ХХХ.ZСVIW.ХУZ said

    What ?

  3. Steve said

    Mumps/Cache version, with comments and expanded commands, etc

    billingperiod ;
             quit
             ;
    go(dt)   ;
             new adt,dti,dts,dtsi,i,period ; initialize variables
             set dti=$zdh(dt),dts="01/01/"_$p(dt,"/",3),dtsi=$zdh(dts) ; get internal date,external Jan 1 date,internal Jan 1 date
             set period=0 ; initialize period count
             for i=dtsi:1:dti do  ; loop through dates from Jan 1 to target date
             . set adt=$zd(i) ; get external form of date from loop
             . if $piece(adt,"/",2)="01" set period=period+1 ; if loop date is first of the month, increment counter
             . else  if i#7=2 set period=period+1 ; Otherwise, if loop date is Saturday, increment counter
             q period
     
    ---
    ; For each of these dates, print date and calculated period  ($$go^billingperiod is the called function)
    for dt="01/01/2018","01/05/2018","01/06/2018","01/12/2018","01/13/2018","01/19/2018","01/20/2018","01/26/2018","01/27/2018","01/31/2018","02/01/2018","08/31/2018","09/01/2018","09/02/2018" write !,dt," -- ",$$go^billingperiod(dt)
    
    01/01/2018 -- 1
    01/05/2018 -- 1
    01/06/2018 -- 2
    01/12/2018 -- 2
    01/13/2018 -- 3
    01/19/2018 -- 3
    01/20/2018 -- 4
    01/26/2018 -- 4
    01/27/2018 -- 5
    01/31/2018 -- 5
    02/01/2018 -- 6
    08/31/2018 -- 42
    09/01/2018 -- 43
    09/02/2018 -- 43
    
  4. Daniel said

    Here’s a solution in C.

    #include <stdbool.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    int calc_julian(int year, int month, int day) {
      int m1 = (month-14)/12;
      int y1 = year+4800;
      int julian = 1461*(y1+m1)/4;
      julian += (367*(month-2-12*m1))/12;
      julian += (-3*((y1+m1+100)/100))/4;
      julian += day-32075;
      return julian;
    }
    
    int calc_billing_period(int year, int month, int day) {
      int billing_period = 0;
      int start = calc_julian(year, 1, 1);
      int end = calc_julian(year, month, day);
      // Add the number of full weeks (each of these includes a Saturday).
      int n_days = end - start + 1;
      int n_full_weeks = n_days / 7;
      billing_period += n_full_weeks;
      // Add 1 if there is a Saturday in the partial week.
      int remainder = n_days % 7;
      int start_day = start % 7;
      bool another_saturday = (remainder > 0)
        && (start_day != 6)
        && (start_day + remainder - 1 >= 5);
      billing_period += another_saturday;
      // Add the months that didn't start on Saturday.
      for (int i = 1; i <= month; ++i) {
        billing_period += (calc_julian(year, i, 1) % 7) != 5;
      }
      return billing_period;
    }
    
    int main(int argc, char* argv[]) {
      if (argc != 4) {
        fprintf(stderr, "Usage: %s <YEAR> <MONTH> <DAY>\n", argv[0]);
        return EXIT_FAILURE;
      }
      int year = atoi(argv[1]);
      int month = atoi(argv[2]);
      int day = atoi(argv[3]);
      int billing_period = calc_billing_period(year, month, day);
      printf("%d\n", billing_period);
      return EXIT_SUCCESS;
    }
    

    Example:

    $ ./billing_period 2018 5 18
    24
    
  5. V said

    In Ruby.

    require 'date'
    
    def billing_period(date_str)
      target_date  = Date.parse(date_str)
      jan_first    = Date.new(target_date.year, 1,1)
      (jan_first..target_date).reduce(0) do |period, date|
        date.day == 1 || date.saturday? ? period + 1 : period
      end
    end
    
    [
      '2018-01-01',
      '2018-01-03',
      '2018-01-06',
      '2018-01-10',
      '2018-01-26',
      '2018-02-01',
      '2018-05-18'
    ].each do |date|
      puts "#{date} -> #{billing_period(date)}"
    end
    
    

    Output:

    2018-01-01 -> 1
    2018-01-03 -> 1
    2018-01-06 -> 2
    2018-01-10 -> 2
    2018-01-26 -> 4
    2018-02-01 -> 6
    2018-05-18 -> 24

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s

%d bloggers like this: