/**
 * Finds full months between two dates and the remaining seconds after the end of the last full month. Takes into account
 * leap years and variable number of days in months. Uses wcs_add_months
 *
 * @param  numeric $start_timestamp unix timestamp of a start date
 * @param  numeric $end_timestamp   unix timestamp of an end date
 * @return array                    with keys 'months' (integer) and 'remainder' (seconds, integer)
 */
function wcs_find_full_months_between($start_timestamp, $end_timestamp, $interval = 1)
{
    $number_of_months = 0;
    $remainder = 0;
    $previous_remainder = 0;
    $months_in_period = 0;
    $remainder_in_period = 0;
    while (0 <= $remainder) {
        $previous_timestamp = $start_timestamp;
        $start_timestamp = wcs_add_months($start_timestamp, 1);
        $previous_remainder = $remainder;
        $remainder = $end_timestamp - $start_timestamp;
        $remainder_in_period += $start_timestamp - $previous_timestamp;
        if ($remainder >= 0) {
            $number_of_months++;
            $months_in_period++;
        } elseif (0 === $previous_remainder) {
            $previous_remainder = $end_timestamp - $previous_timestamp;
        }
        if ($months_in_period >= $interval) {
            $months_in_period = 0;
            $remainder_in_period = 0;
        }
    }
    $remainder_in_period += $remainder;
    $time_difference = array('months' => $number_of_months, 'remainder' => $remainder_in_period);
    return $time_difference;
}
Esempio n. 2
0
/**
 * Finds full months between two dates and the remaining seconds after the end of the last full month. Takes into account
 * leap years and variable number of days in months. Uses wcs_add_months
 *
 * @param  numeric $start_timestamp unix timestamp of a start date
 * @param  numeric $end_timestamp   unix timestamp of an end date
 * @return array                    with keys 'months' (integer) and 'remainder' (seconds, integer)
 */
function wcs_find_full_months_between($start_timestamp, $end_timestamp)
{
    $number_of_months = 0;
    $remainder = null;
    $previous_remainder = null;
    while (0 <= $remainder) {
        $previous_timestamp = $start_timestamp;
        $start_timestamp = wcs_add_months($start_timestamp, 1);
        $previous_remainder = $remainder;
        $remainder = $end_timestamp - $start_timestamp;
        if ($remainder >= 0) {
            $number_of_months++;
        } elseif (null === $previous_remainder) {
            $previous_remainder = $end_timestamp - $previous_timestamp;
        }
    }
    $time_difference = array('months' => $number_of_months, 'remainder' => $previous_remainder);
    return $time_difference;
}
 /**
  * Workaround the last day of month quirk in PHP's strtotime function.
  *
  * @since 1.2.5
  * @deprecated 2.0
  */
 public static function add_months($from_timestamp, $months_to_add)
 {
     _deprecated_function(__METHOD__, '2.0', 'wcs_add_months()');
     return wcs_add_months($from_timestamp, $months_to_add);
 }
 /**
  * Calculate the first payment date for a synced subscription.
  *
  * The date is calculated in UTC timezone.
  *
  * @param WC_Product $product A subscription product.
  * @param string $type (optional) The format to return the first payment date in, either 'mysql' or 'timestamp'. Default 'mysql'.
  * @param string $from_date (optional) The date to calculate the first payment from in GMT/UTC timzeone. If not set, it will use the current date. This should not include any trial period on the product.
  * @since 1.5
  */
 public static function calculate_first_payment_date($product, $type = 'mysql', $from_date = '')
 {
     if (!is_object($product)) {
         $product = WC_Subscriptions::get_product($product);
     }
     if (!self::is_product_synced($product)) {
         return 0;
     }
     $period = WC_Subscriptions_Product::get_period($product);
     $trial_period = WC_Subscriptions_Product::get_trial_period($product);
     $trial_length = WC_Subscriptions_Product::get_trial_length($product);
     $from_date_param = $from_date;
     if (empty($from_date)) {
         $from_date = gmdate('Y-m-d H:i:s');
     }
     // If the subscription has a free trial period, the first payment should be synced to a day after the free trial
     if ($trial_length > 0) {
         $from_date = WC_Subscriptions_Product::get_trial_expiration_date($product, $from_date);
     }
     $from_timestamp = strtotime($from_date) + get_option('gmt_offset') * 3600;
     // Site time
     $payment_day = self::get_products_payment_day($product);
     if ('week' == $period) {
         // strtotime() will figure out if the day is in the future or today (see: https://gist.github.com/thenbrent/9698083)
         $first_payment_timestamp = strtotime(self::$weekdays[$payment_day], $from_timestamp);
     } elseif ('month' == $period) {
         // strtotime() needs to know the month, so we need to determine if the specified day has occured this month yet or if we want the last day of the month (see: https://gist.github.com/thenbrent/9698083)
         if ($payment_day > 27) {
             // we actually want the last day of the month
             $payment_day = gmdate('t', $from_timestamp);
             $month = gmdate('F', $from_timestamp);
         } elseif (gmdate('j', $from_timestamp) > $payment_day) {
             // today is later than specified day in the from date, we need the next month
             $month = date('F', wcs_add_months($from_timestamp, 1));
         } else {
             // specified day is either today or still to come in the month of the from date
             $month = gmdate('F', $from_timestamp);
         }
         $first_payment_timestamp = strtotime("{$payment_day} {$month}", $from_timestamp);
     } elseif ('year' == $period) {
         // We can't use $wp_locale here because it is translated
         switch ($payment_day['month']) {
             case 1:
                 $month = 'January';
                 break;
             case 2:
                 $month = 'February';
                 break;
             case 3:
                 $month = 'March';
                 break;
             case 4:
                 $month = 'April';
                 break;
             case 5:
                 $month = 'May';
                 break;
             case 6:
                 $month = 'June';
                 break;
             case 7:
                 $month = 'July';
                 break;
             case 8:
                 $month = 'August';
                 break;
             case 9:
                 $month = 'September';
                 break;
             case 10:
                 $month = 'October';
                 break;
             case 11:
                 $month = 'November';
                 break;
             case 12:
                 $month = 'December';
                 break;
         }
         $first_payment_timestamp = strtotime("{$payment_day['day']} {$month}", $from_timestamp);
     }
     // Make sure the next payment is in the future and after the $from_date, as strtotime() will return the date this year for any day in the past when adding months or years (see: https://gist.github.com/thenbrent/9698083)
     if ('year' == $period || 'month' == $period) {
         // First make sure the day is in the past so that we don't end up jumping a month or year because of a few hours difference between now and the billing date
         if (gmdate('Ymd', $first_payment_timestamp) < gmdate('Ymd', $from_timestamp) || gmdate('Ymd', $first_payment_timestamp) < gmdate('Ymd')) {
             $i = 1;
             // Then make sure the date and time of the payment is in the future
             while (($first_payment_timestamp < gmdate('U') || $first_payment_timestamp < $from_timestamp) && $i < 30) {
                 $first_payment_timestamp = strtotime("+ 1 {$period}", $first_payment_timestamp);
                 $i = $i + 1;
             }
         }
     }
     // We calculated a timestamp for midnight on the specific day in the site's timezone, let's push it to 3am to account for any daylight savings changes
     $first_payment_timestamp += 3 * HOUR_IN_SECONDS;
     // And convert it to the UTC equivalent of 3am on that day
     $first_payment_timestamp -= get_option('gmt_offset') * HOUR_IN_SECONDS;
     $first_payment = 'mysql' == $type && 0 != $first_payment_timestamp ? date('Y-m-d H:i:s', $first_payment_timestamp) : $first_payment_timestamp;
     return apply_filters('woocommerce_subscriptions_synced_first_payment_date', $first_payment, $product, $type, $from_date, $from_date_param);
 }
 /**
  * Takes a subscription product's ID and returns the date on which the subscription trial will expire,
  * based on the subscription's trial length and calculated from either the $from_date if specified,
  * or the current date/time.
  *
  * @param int $product_id The product/post ID of the subscription
  * @param mixed $from_date A MySQL formatted date/time string from which to calculate the expiration date (in UTC timezone), or empty (default), which will use today's date/time (in UTC timezone).
  * @since 1.0
  */
 public static function get_trial_expiration_date($product_id, $from_date = '')
 {
     $trial_period = self::get_trial_period($product_id);
     $trial_length = self::get_trial_length($product_id);
     if ($trial_length > 0) {
         if (empty($from_date)) {
             $from_date = gmdate('Y-m-d H:i:s');
         }
         if ('month' == $trial_period) {
             $trial_expiration_date = date('Y-m-d H:i:s', wcs_add_months(strtotime($from_date), $trial_length));
         } else {
             // Safe to just add the billing periods
             $trial_expiration_date = date('Y-m-d H:i:s', strtotime("+ {$trial_length} {$trial_period}s", strtotime($from_date)));
         }
     } else {
         $trial_expiration_date = 0;
     }
     return apply_filters('woocommerce_subscriptions_product_trial_expiration_date', $trial_expiration_date, $product_id, $from_date);
 }