setTimezone( new \DateTimeZone( 'Asia/Tehran' ) )->format( 'Y-m-d' ); } /** * Distinct purchase days, oldest first. * * @param list $dates Local dates of the sale orders, `Y-m-d`. * * @return list */ public static function purchaseDays( array $dates ): array { $days = array_values( array_unique( $dates ) ); sort( $days ); return $days; } /** * Average days between purchase days, to two decimals; null below two days. * * @param list $days Purchase days, oldest first. */ public static function averageInterval( array $days ): ?float { $count = count( $days ); if ( $count < 2 ) { return null; } $span = ( new \DateTimeImmutable( $days[ $count - 1 ] . ' 00:00:00', new \DateTimeZone( 'UTC' ) ) )->getTimestamp() - ( new \DateTimeImmutable( $days[0] . ' 00:00:00', new \DateTimeZone( 'UTC' ) ) )->getTimestamp(); return round( ( $span / 86400 ) / ( $count - 1 ), 2 ); } /** * The personal cycle in days, or null when the customer has none. * * @param list $days Purchase days, oldest first. */ public static function personal( array $days ): ?float { $average = self::averageInterval( $days ); if ( null === $average || count( $days ) < self::PERSONAL_FROM ) { return null; } return max( (float) self::MIN_DAYS, min( (float) self::MAX_DAYS, $average ) ); } }