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

Pages: 1 2

### 5 Responses to “Billing Period”

1. Steve said

Mumps/Cache version

```billingperiod ;
q
;
go(dt)   ;
s dti=\$zdh(dt),dts="01/01/"_\$p(dt,"/",3),dtsi=\$zdh(dts)
s period=0
f i=dtsi:1:dti d
. 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