diff --git a/include/class.forms.php b/include/class.forms.php index 980f67c56ea9b991e67de2cc7c9cfaf5afb3539b..c9ec6f3ae11660664abd105be47db6efb9715b30 100644 --- a/include/class.forms.php +++ b/include/class.forms.php @@ -1974,10 +1974,25 @@ class DatetimeField extends FormField { 'w' => _N('week', 'weeks', $count), 'm' => _N('month', 'months', $count), ); - return $i ? $intervals[$i] : $intervals; } + static function periods($period='') { + $periods = array( + 'td' => __('Today'), + 'yd' => __('Yesterday'), + 'tw' => __('This Week'), + 'tm' => __('This Month'), + 'tq' => __('This Quater'), + 'ty' => __('This Year'), + 'lw' => __('Last Week'), + 'lm' => __('Last Month'), + 'lq' => __('Last Quater'), + 'ly' => __('Last Year'), + ); + return $period ? $periods[$period] : $periods; + } + // Get php DatateTime object of the field - null if value is empty function getDateTime($value=null) { return Format::parseDateTime($value ?: $this->value); @@ -2190,6 +2205,7 @@ class DatetimeField extends FormField { 'before' => __('before'), 'after' => __('after'), 'between' => __('between'), + 'period' => __('period'), 'ndaysago' => __('in the last n days'), 'ndays' => __('in the next n days'), 'future' => __('in the future'), @@ -2239,6 +2255,9 @@ class DatetimeField extends FormField { 'right' => new DatetimeField(), ), )), + 'period' => array('ChoiceField', array( + 'choices' => self::periods(), + )), 'ndaysago' => array('InlineformField', array('form'=>$nday_form())), 'ndays' => array('InlineformField', array('form'=>$nday_form())), 'distfut' => array('InlineformField', array('form'=>$nday_form())), @@ -2247,6 +2266,8 @@ class DatetimeField extends FormField { } function getSearchQ($method, $value, $name=false) { + global $cfg; + static $intervals = array( 'm' => 'MONTH', 'w' => 'WEEK', @@ -2257,10 +2278,9 @@ class DatetimeField extends FormField { $name = $name ?: $this->get('name'); $now = SqlFunction::NOW(); $config = $this->getConfiguration(); - if (is_int($value)) $value = DateTime::createFromFormat('U', !$config['gmt'] ? Misc::gmtime($value) : $value) ?: $value; - elseif (is_string($value)) + elseif (is_string($value) && strlen($value) > 2) $value = Format::parseDateTime($value) ?: $value; switch ($method) { @@ -2322,6 +2342,27 @@ class DatetimeField extends FormField { return new Q(array( "{$name}__gte" => $now->plus($interval), )); + case 'period': + // Get the period range boundaries - timezone doesn't matter + $period = Misc::date_range($value, Misc::gmtime('now')); + $tz = new DateTimeZone($cfg->getTimezone()); + // Get datetime boundaries in user's effective timezone + $tz = new DateTimeZone($cfg->getTimezone()); + $start = new DateTime($period->start->format('Y-m-d H:i:s'), + $tz); + $end = new DateTime($period->end->format('Y-m-d H:i:s'), $tz); + // Convert boundaries to db time + $dbtz = new DateTimeZone($cfg->getDbTimezone()); + $start->setTimezone($dbtz); + $end->setTimezone($dbtz); + // Set the range + return new Q(array( + "{$name}__range" => array( + $start->format('Y-m-d H:i:s'), + $end->format('Y-m-d H:i:s') + ) + )); + break; default: return parent::getSearchQ($method, $value, $name); } @@ -2347,6 +2388,8 @@ class DatetimeField extends FormField { return __('%1$s is in the future'); case 'past': return __('%1$s is in the past'); + case 'period': + return __('%1$s is %2$s'); default: return parent::describeSearchMethod($method); } @@ -2375,6 +2418,8 @@ class DatetimeField extends FormField { case 'before': case 'after': return sprintf($desc, $name, $this->toString($value)); + case 'period': + return sprintf($desc, $name, self::periods($value) ?: $value); default: return parent::describeSearch($method, $value, $name); } diff --git a/include/class.misc.php b/include/class.misc.php index 57c18a8df5f0456bd8cce201c7c003ae52fdcee4..65b1c357a677707a480e4819eb21f4796c465713 100644 --- a/include/class.misc.php +++ b/include/class.misc.php @@ -143,6 +143,85 @@ class Misc { return ((float)$usec + (float)$sec); } + // Date range for the period in a given time + function date_range($period, $time=false) { + $time = $time ?: self::gmtime(); + if (!($dt = Format::parseDateTime($time))) + return null; + // Force UTC + $dt->setTimezone(new DateTimeZone('UTC')); + + // Make dt Immutable. + $dt = DateTimeImmutable::createFromMutable($dt); + switch ($period) { + case 'td': + case 'today': + $start = $end = $dt->modify('today'); + break; + case 'yd': + case 'yesterday': + $start = $end = $dt->modify('yesterday'); + break; + case 'tw': + case 'this-week': + $N = $dt->format('N'); + $start = $dt->modify($N == 1 ? 'today' : 'last monday'); + $end = $start->modify('next sunday'); + break; + case 'tm': + case 'this-month'; + $start = $dt->modify('first day of this month'); + $end = $dt->modify('last day of this month'); + break; + case 'tq': + case 'this-quater': + $offset = ($dt->format('m') - 1) % 3; + $start = $dt->modify(" - $offset month") + ->modify('first day of this month'); + $end = $start->modify('+ 3 month')->modify('- 1 day'); + break; + case 'ty': + case 'this-year': + $start = $dt->modify('january')->modify('first day of this month'); + $end = $dt->modify('december')->modify('last day of this month'); + break; + case 'lw': + case 'last-week': + //TODO: address edge cases + $start = $dt->modify('- 1 week')->modify('last monday'); + $end = $start->modify('next sunday'); + break; + case 'lm': + case 'last-month'; + $start = $dt->modify('- 1 month')->modify('first day of this month'); + $end = $start->modify('last day of this month'); + break; + case 'lq': + case 'last-quater': + $offset = (($dt->format('m') - 1) % 3)+3; + $start = $dt->modify(" - $offset month") + ->modify('first day of this month'); + $end = $start->modify('+ 3 month')->modify('- 1 day'); + break; + case 'ly': + case 'last-year': + $start = $dt->modify('- 1 year') + ->modify('january') + ->modify('first day of this month'); + $end = $start->modify('december')->modify('last day of this month'); + break; + default: + return null; + } + + if ($start) + $start = $start->setTime(00, 00, 00); + if ($end) + $end = $end->setTime(23, 59, 59); + + return (object) array('start' => $start, 'end' => $end); + } + //Current page function currentURL() {