<?php
class Prorate
{
// source and destination prices will be expressed internally as price per day once set.
private $source_price = 0;
private $destination_price = 0;
// source expiry is what you're pro-rating. destination expiry is what you're converting.
private $source_expiry;
private $destination_expiry;
// today defaults to the current date, but can be set to an arbitrary value using Prorate::set_today()
private $today;
// terms are important! they allow us to calculate the current price per day
private $source_term = 12;
private $destination_term = 12;
// source price is what you're pro-rating. Destination_price is what you're converting. Source and destination terms
// follow the same rules.
public function __construct($source_price = NULL, $destination_price = NULL, $source_term = 12, $destination_term = 12)
{
if ($source_price != NULL)
{
$this->set_source_price($source_price, $source_term);
}
if ($destination_price != NULL)
{
$this->set_destination_price($destination_price, $destination_term);
}
$this->source_expiry = new DateTime();
$this->destination_expiry = new DateTime();
$this->today = new DateTime();
$this->source_term = $source_term;
$this->destination_term = $destination_term;
}
// set the source expiry date. Valid inputs are either a DateTime object, or a string format listed at
// http://php.net/manual/en/datetime.formats.php
public function set_source_expiry($date)
{
if(is_object($date) && is_a($date, 'DateTime'))
{
$this->source_expiry = $date;
}
else
{
$this->source_expiry = new DateTime($date);
}
return $this;
}
public function get_source_expiry()
{
return $this->source_expiry;
}
// set the destination expiry date. Valid inputs are either a DateTime object, or a string format listed at
// http://php.net/manual/en/datetime.formats.php
public function set_destination_expiry($date)
{
if(is_object($date) && is_a($date, 'DateTime'))
{
$this->destination_expiry = $date;
}
else
{
$this->destination_expiry = new DateTime($date);
}
return $this;
}
public function get_destination_expiry()
{
return $this->destination_expiry;
}
// returns the number of (positive) days remaining between a source date in the future, and today
private function days_remaining($date)
{
return $this->diff_days($date, $this->today);
}
// determines the daily value of a given price over a given number of months.
// 30.44 is 365 days / 12 months
private function value_per_day($price_in_cents, $term_in_months)
{
$value_per_day = $price_in_cents / $term_in_months / 30.44;
return ceil($value_per_day) / 100;
}
// Allows you to change what "today" is defined as. Defaults to the current date. Valid values are
// a DateTime object, or a string representation of a date in one of the formats listed at:
// http://php.net/manual/en/datetime.formats.php
public function set_today($date = NULL)
{
if(is_object($date) && is_a($date, 'DateTime'))
{
$this->today = $date;
}
else
{
if (is_null($date))
{
$this->today = new DateTime();
}
else
{
$this->today = new DateTime($date);
}
}
}
// sets the source price (the thing you're pro-rating)
// valid inputs are a price in cents, and a term expressed in months
public function set_source_price($price_in_cents, $term_in_months)
{
$this->source_price = $this->value_per_day($price_in_cents, $term_in_months);
return $this;
}
public function get_source_price()
{
return $this->source_price;
}
// sets the target price (the thing you're converting)
// valid inputs are a price in cents, and a term expressed in months
public function set_destination_price($price_in_cents, $term_in_months)
{
$this->destination_price = $this->value_per_day($price_in_cents, $term_in_months);
return $this;
}
public function get_destination_price()
{
return $this->destination_price;
}
// calculates the target value based on the current calculated price per day and time
// remaining on expiry
public function target_value()
{
return $this->days_remaining($this->destination_expiry) * $this->destination_price;
}
// calculates the number of days to pro-rate. this does not include the number of days
// remaining on the source.
public function add_days()
{
return ceil($this->target_value() / $this->source_price);
}
// DateTime diff implementation for systems that are not new enough for PHP's regular DateTime::diff() method
// if there is a partial day, it is rounded up to count as a full day.
private function diff_days(DateTime $x, DateTime $y)
{
$y = strtotime($y->format('Y-m-d'));
$x = strtotime($x->format('Y-m-d'));
$diff = $x - $y;
if ($diff <= 0) return 0;
return ceil($diff / 86400);
}
// Call this once all your values are set. Returns an associative array containing the number of days
// remaining on the source, the number of days to add to the source, the total number of days (source + add)
// and a DateTime object representing the value defined as "today" plus the total days to add.
public function pro_rate()
{
$days = $this->diff_days($this->source_expiry, $this->today);
$result = array();
$add = $this->add_days();
$total_add = $days + $add;
$today = $this->today->format('Y-m-d');
$source = $this->source_expiry->format('Y-m-d');
$dest = $this->destination_expiry->format('Y-m-d');
$result['current_days'] = $days;
$result['pro_rate_days'] = $add;
$result['total_days'] = $total_add;
$tmp = clone $this->today;
$result['date'] = $tmp->modify("+{$result['total_days']} days");
return $result;
}
}
$prorate = new Prorate(19990, 19995);
$prorate->set_source_expiry('2014-07-23');
$prorate->set_destination_expiry('19-01-2014');
$result = $prorate->pro_rate();
var_dump($result);