2 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4 // +----------------------------------------------------------------------+
5 // | Akelos Framework - http://www.akelos.org |
6 // +----------------------------------------------------------------------+
7 // | Copyright (c) 2002-2006, Akelos Media, S.L. & Bermi Ferrer Martinez |
8 // | Released under the GNU Lesser General Public License, see LICENSE.txt|
9 // +----------------------------------------------------------------------+
14 * @author Bermi Ferrer <bermi a.t akelos c.om>
15 * @copyright Copyright (c) 2002-2006, Akelos Media, S.L. http://www.akelos.org
16 * @license GNU Lesser General Public License <http://www.gnu.org/copyleft/lesser.html>
20 if(!defined('AK_DATE_HELPER_DEFAULT_PREFIX')){
21 define('AK_DATE_HELPER_DEFAULT_PREFIX', 'date');
25 * The Date Helper primarily creates select/option tags for different kinds of dates and date elements. All of the select-type methods
26 * share a number of common options that are as follows:
28 * * <tt>:prefix</tt> - overwrites the default prefix of "date" used for the select names. So specifying "birthday" would give
29 * birthday[month] instead of date[month] if passed to the select_month method.
30 * * <tt>:include_blank</tt> - set to true if it should be possible to set an empty date.
31 * * <tt>:discard_type</tt> - set to true if you want to discard the type part of the select name. If set to true, the select_month
32 * method would use simply "date" (which can be overwritten using <tt>:prefix</tt>) instead of "date[month]".
34 class DateHelper
extends AkActionViewHelper
38 * Reports the approximate distance in time between two times given in seconds
39 * or in a valid ISO string like.
40 * For example, if the distance is 47 minutes, it'll return
41 * "about 1 hour". See the source for the complete wording list.
43 * Integers are interpreted as seconds. So,
44 * <tt>$date_helper->distance_of_time_in_words(50)</tt> returns "less than a minute".
46 * Set <tt>include_seconds</tt> to true if you want more detailed approximations if distance < 1 minute
48 function distance_of_time_in_words($from_time, $to_time = 0, $include_seconds = false)
50 $from_time = is_numeric($from_time) ?
$from_time : Ak
::getTimestamp($from_time);
51 $to_time = is_numeric($to_time) ?
$to_time : Ak
::getTimestamp($to_time);
52 $distance_in_minutes = round((abs($to_time - $from_time))/60);
53 $distance_in_seconds = round(abs($to_time - $from_time));
55 if($distance_in_minutes <= 1){
57 if($distance_in_seconds < 5){
58 return Ak
::t('less than %seconds seconds',array('%seconds'=>5),'localize/date');
59 }elseif($distance_in_seconds < 10){
60 return Ak
::t('less than %seconds seconds',array('%seconds'=>10),'localize/date');
61 }elseif($distance_in_seconds < 20){
62 return Ak
::t('less than %seconds seconds',array('%seconds'=>20),'localize/date');
63 }elseif($distance_in_seconds < 40){
64 return Ak
::t('half a minute',array(),'localize/date');
65 }elseif($distance_in_seconds < 60){
66 return Ak
::t('less than a minute',array(),'localize/date');
68 return Ak
::t('1 minute',array(),'localize/date');
71 return ($distance_in_minutes===0) ? Ak
::t('less than a minute', array(),'localize/date') : Ak
::t('1 minute', array(),'localize/date');
72 }elseif ($distance_in_minutes <= 45){
73 return Ak
::t('%minutes minutes',array('%minutes'=>$distance_in_minutes),'localize/date');
74 }elseif ($distance_in_minutes <= 90){
75 return Ak
::t('about 1 hour',array(),'localize/date');
76 }elseif ($distance_in_minutes <= 1440){
77 return Ak
::t('about %hours hours',array('%hours'=>round($distance_in_minutes/60)),'localize/date');
78 }elseif ($distance_in_minutes <= 2880){
79 return Ak
::t('1 day',array(),'localize/date');
81 return Ak
::t('%days days',array('%days'=>round($distance_in_minutes/1440)),'localize/date');
86 * Like distance_of_time_in_words, but where <tt>to_time</tt> is fixed to <tt>timestamp()</tt>.
88 function time_ago_in_words($from_time, $include_seconds = false)
90 return DateHelper
::distance_of_time_in_words($from_time, Ak
::time(), $include_seconds);
92 function distance_of_time_in_words_to_now($from_time, $include_seconds = false)
94 return DateHelper
::time_ago_in_words($from_time, $include_seconds);
98 * Returns a set of select tags (one for year, month, and day) pre-selected for accessing a specified date-based attribute (identified by
99 * +column_name+) on an object assigned to the template (identified by +object+). It's possible to tailor the selects through the +options+ array,
100 * which accepts all the keys that each of the individual select builders do (like 'use_month_numbers' for select_month) as well as a range of
101 * discard options. The discard options are <tt>'discard_year'</tt>, <tt>'discard_month'</tt> and <tt>'discard_day'</tt>. Set to true, they'll
102 * drop the respective select. Discarding the month select will also automatically discard the day select. It's also possible to explicitly
103 * set the order of the tags using the <tt>'order'</tt> option with an array(<tt>'year'</tt>, <tt>'month'</tt> and <tt>'day')</tt> in
106 * Passing 'disabled' => true as part of the +options+ will make elements inaccessible for change.
108 * NOTE: Discarded selects will default to 1. So if no month select is available, January will be assumed.
112 * $date_helper->date_select("post", "written_on");
113 * $date_helper->date_select("post", "written_on", array('start_year' => 1995));
114 * $date_helper->date_select("post", "written_on", array('start_year' => 1995, 'use_month_numbers' => true,
115 * 'discard_day' => true, 'include_blank' => true)));
116 * $date_helper->date_select("post", "written_on", array('order' => array('day', 'month', 'year')));
117 * $date_helper->date_select("user", "birthday", array('order' => array('month', 'day')));
119 * The selects are prepared for multi-parameter assignment to an Active Record object.
121 function date_select($object_name, $column_name, $options = array())
123 $defaults = array('discard_type' => true);
124 $options = array_merge($defaults, $options);
126 if(!empty($this->_controller
->$object_name) && $column_name[0] != '_' && method_exists($this->_controller
->$object_name,$column_name)){
127 $date = $this->_controller
->$object_name->$column_name();
128 }elseif(!empty($this->_controller
->$object_name)) {
129 $date = $this->_controller
->$object_name->get($column_name);
130 }elseif(!empty($this->_object
[$object_name])){
131 $date = $this->_object
[$object_name]->get($column_name);
134 $date = !empty($options['include_blank']) ?
(!empty($date) ?
$date : 0) : (!empty($date) ?
$date : Ak
::getDate());
136 $options['order'] = empty($options['order']) ?
explode(',',Ak
::t('year,month,day',array(),'localize/date')) : $options['order'];
139 'year'=>!empty($options['discard_year']),
140 'month'=>!empty($options['discard_month']),
141 'day'=>!empty($options['discard_day']) ||
!empty($options['discard_month'])
145 $codes = array('year'=>'1i','month'=>'2i','day'=>'3i');
146 foreach ($options['order'] as $param){
147 if(empty($discard[$param])){
148 $helper_method = 'select_'.$param;
149 $date_select .= DateHelper
::$helper_method($date, array_merge($options,array('prefix' => $object_name.'['.$column_name.'('.$codes[$param].')]')))."\n";
156 * Returns a set of select tags (one for year, month, day, hour, and minute) pre-selected for accessing a specified datetime-based
157 * attribute (identified by +column_name+) on an object assigned to the template (identified by +object+). Examples:
159 * datetime_select("post", "written_on");
160 * datetime_select("post", "written_on", array('start_year' => 1995));
162 * The selects are prepared for multi-parameter assignment to an Active Record object.
164 function datetime_select($object_name, $column_name, $options = array())
166 $defaults = array('discard_type' => true, 'order'=>explode(',',Ak
::t('year,month,day,hour,minute',array(),'localize/date')));
167 $options = array_merge($defaults, $options);
169 if(!empty($this->_controller
->$object_name) && $column_name[0] != '_' && method_exists($this->_controller
->$object_name,$column_name)){
170 $date = $this->_controller
->$object_name->$column_name();
171 }elseif(!empty($this->_controller
->$object_name)) {
172 $date = $this->_controller
->$object_name->get($column_name);
173 }elseif(!empty($this->_object
[$object_name])) {
174 $date = $this->_object
[$object_name]->get($column_name);
177 $date = !empty($options['include_blank']) ?
(!empty($date) ?
$date : 0) : (!empty($date) ?
$date : Ak
::getDate());
180 'year'=>!empty($options['discard_year']),
181 'month'=>!empty($options['discard_month']),
182 'day'=>!empty($options['discard_day']) ||
!empty($options['discard_month']),
183 'hour'=>!empty($options['discard_hour']),
184 'minute'=>!empty($options['discard_hour']) ||
!empty($options['discard_minute'])
187 $datetime_select = '';
188 $codes = array('year'=>'1i','month'=>'2i','day'=>'3i','hour'=>'4i','minute'=>'5i');
189 foreach ($options['order'] as $param){
190 if(empty($discard[$param])){
191 $helper_method = 'select_'.$param;
192 $datetime_select .= ($param == 'hour' ?
' — ' : ($param == 'minute' ?
' : ' : '')).
193 DateHelper
::$helper_method($date, array_merge($options,array('prefix' => $object_name.'['.$column_name.'('.$codes[$param].')]')))."\n";
196 return $datetime_select;
200 * Returns a set of html select-tags (one for year, month, and day) pre-selected with the +date+.
202 function select_date($date = null, $options = array())
204 $date = empty($date) ? Ak
::getDate() : $date;
205 return DateHelper
::select_year($date, $options) . DateHelper
::select_month($date, $options) . DateHelper
::select_day($date, $options);
209 * Returns a set of html select-tags (one for year, month, day, hour, and minute) pre-selected with the +datetime+.
211 function select_datetime($datetime = null, $options = array())
213 $datetime = empty($datetime) ? Ak
::getDate() : $datetime;
214 return DateHelper
::select_date($datetime, $options) . DateHelper
::select_time($datetime, $options);
218 * Returns a set of html select-tags (one for hour and minute)
220 function select_time($datetime = null, $options = array())
222 $datetime = empty($datetime) ? Ak
::getDate() : $datetime;
223 return DateHelper
::select_hour($datetime, $options) . DateHelper
::select_minute($datetime, $options) .
224 (!empty($options['include_seconds']) ? DateHelper
::select_second($datetime, $options) : '');
228 * Returns a select tag with options for each of the seconds 0 through 59 with the current second selected.
229 * The <tt>second</tt> can also be substituted for a second number.
230 * Override the field name using the <tt>field_name</tt> option, 'second' by default.
232 function select_second($datetime, $options = array())
234 return DateHelper
::_select_for('second',range(0,59),'s',$datetime, $options);
238 * Returns a select tag with options for each of the minutes 0 through 59 with the current minute selected.
239 * Also can return a select tag with options by <tt>minute_step</tt> from 0 through 59 with the 00 minute selected
240 * The <tt>minute</tt> can also be substituted for a minute number.
241 * Override the field name using the <tt>field_name</tt> option, 'minute' by default.
243 function select_minute($datetime, $options = array())
245 return DateHelper
::_select_for('minute',range(0,59),'i',$datetime, $options);
249 * Returns a select tag with options for each of the hours 0 through 23 with the current hour selected.
250 * The <tt>hour</tt> can also be substituted for a hour number.
251 * Override the field name using the <tt>:field_name</tt> option, 'hour' by default
253 function select_hour($datetime, $options = array())
255 return DateHelper
::_select_for('hour',range(0,23),'H',$datetime, $options);
259 * Returns a select tag with options for each of the days 1 through 31 with the current day selected.
260 * The <tt>date</tt> can also be substituted for a hour number.
261 * Override the field name using the <tt>field_name</tt> option, 'day' by default.
263 function select_day($date, $options = array())
265 return DateHelper
::_select_for('day',range(1,31),'j',$date, $options,false);
269 * Returns a select tag with options for each of the months January through December with the current month selected.
270 * The month names are presented as keys (what's shown to the user) and the month numbers (1-12) are used as values
271 * (what's submitted to the server). It's also possible to use month numbers for the presentation instead of names --
272 * set the <tt>use_month_numbers</tt> key in +options+ to true for this to happen. If you want both numbers and names,
273 * set the <tt>add_month_numbers</tt> key in +options+ to true. Examples:
275 * $date_helper->select_month(Ak::getDate()); // Will use keys like "January", "March"
276 * $date_helper->select_month(Ak::getDate(), array('use_month_numbers' => true)); // Will use keys like "1", "3"
277 * $date_helper->select_month(Ak::getDate(), array('add_month_numbers' => true)); // Will use keys like "1 - January", "3 - March"
279 * Override the field name using the <tt>field_name</tt> option, 'month' by default.
281 * If you would prefer to show month names as abbreviations, set the
282 * <tt>use_short_month</tt> key in +options+ to true.
284 function select_month($date=null, $options = array())
286 if(!empty($options['use_month_numbers'])){
287 $month_details = '1,2,3,4,5,6,7,8,9,10,11,12';
288 }elseif(!empty($options['add_month_numbers']) && empty($options['use_short_month'])){
289 $month_details = Ak
::t('1 - January,2 - February,3 - March,4 - April,5 - May,6 - June,7 - July,8 - August,'.
290 '9 - September,10 - October,11 - November,12 - December',array(),'localize/date');
291 }elseif(!empty($options['add_month_numbers']) && !empty($options['use_short_month'])){
292 $month_details = Ak
::t('1 - Jan,2 - Feb,3 - Mar,4 - Apr,5 - May,6 - Jun,7 - Jul,8 - Aug,9 - Sep,10 - Oct,11 - Nov,12 - Dec',array(),'localize/date');
293 }elseif(empty($options['add_month_numbers']) && !empty($options['use_short_month'])){
294 $month_details = Ak
::t('Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec',array(),'localize/date');
296 $month_details = Ak
::t('January,February,March,April,May,June,July,August,September,October,November,December',array(),'localize/date');
299 return DateHelper
::_select_for('month', explode(',',$month_details),'n',(empty($date) ? Ak
::getDate() : $date), $options,'_add_one');
303 * Returns a select tag with options for each of the five years on each side of the current, which is selected. The five year radius
304 * can be changed using the <tt>:start_year</tt> and <tt>:end_year</tt> keys in the +options+. Both ascending and descending year
305 * lists are supported by making <tt>start_year</tt> less than or greater than <tt>end_year</tt>. The <tt>date</tt> can also be
306 * substituted for a year given as a number. Example:
308 * $date_helper->select_year(Ak::getDate(), array('start_year' => 1992, 'end_year' => 2007)); // ascending year values
309 * $date_helper->select_year(Ak::getDate(), array('start_year' => 2005, 'end_year' => 1900)); // descending year values
311 * Override the field name using the <tt>field_name</tt> option, 'year' by default.
313 function select_year($date = null, $options = array())
315 $year = Ak
::getDate(Ak
::getTimestamp(isset($date) ?
$date.'-01-01' : null),'Y');
317 $start_year = !empty($options['start_year']) ?
$options['start_year'] : $year-5;
318 $end_year = !empty($options['end_year']) ?
$options['end_year'] : $year+
5;
320 $range = range($start_year,$end_year);
321 $start_year < $end_year ?
array_reverse($range): null;
323 return DateHelper
::_select_for('year',$range,'Y',$date, $options,false);
326 function _select_for($select_type, $range, $date_format, $datetime, $options = array(), $unit_format_callback = '_leading_zero_on_single_digits')
328 $options_array = array();
329 $datetime = empty($datetime) ? Ak
::getDate() : $datetime;
330 $datetime_unit = Ak
::getDate(Ak
::getTimestamp($datetime),$date_format);
332 foreach ($range as $k=>$time_unit){
333 if(is_string($time_unit)){
334 $k = !empty($unit_format_callback) ? DateHelper
::$unit_format_callback($k) : $k;
335 $options_array[] = '<option value="'.$k.'"'.($k == $datetime_unit ?
' selected="selected"' : '').">$time_unit</option>";
337 $time_unit = !empty($unit_format_callback) ? DateHelper
::$unit_format_callback($time_unit) : $time_unit;
338 $options_array[] = '<option value="'.$time_unit.'"'.($time_unit == $datetime_unit ?
' selected="selected"' : '').">$time_unit</option>";
341 return DateHelper
::_select_html(empty($options['field_name']) ?
$select_type : $options['field_name'],
342 $options_array, @$options['prefix'], @$options['include_blank'], @$options['discard_type'], @$options['disabled']);
345 function _select_html($type, $options, $prefix = null, $include_blank = false, $discard_type = false, $disabled = false)
347 return '<select name="'.(empty($prefix) ? AK_DATE_HELPER_DEFAULT_PREFIX
: $prefix).
348 ($discard_type ?
'' : $type).'"'.
349 ($disabled ?
' disabled="disabled"' : '').">\n".
350 ($include_blank ?
"<option value=\"\"></option>\n" : '').
351 (!empty($options) ?
join("\n",$options) : '')."\n</select>\n";
354 function _leading_zero_on_single_digits($number)
356 return $number > 9 ?
$number : "0$number";
359 function _add_one($number)
366 * Converts an ISO date to current locale format
369 function locale_date_time($iso_date_time = null)
371 $timestamp = Ak
::getTimestamp($iso_date_time);
372 $format = Ak
::locale('date_time_format');
373 return Ak
::getDate($timestamp, $format);
377 * Converts an ISO date to current locale format
380 function locale_date($iso_date = null)
382 $timestamp = Ak
::getTimestamp($iso_date);
383 $format = Ak
::locale('date_format');
384 return Ak
::getDate($timestamp, $format);