* static $db = array( * "Expires" => "Date", * ); * * * @todo Add localization support, see http://open.silverstripe.com/ticket/2931 * * @package framework * @subpackage model */ class Date extends DBField { public function setValue($value, $record = null) { if($value === false || $value === null || (is_string($value) && !strlen($value))) { // don't try to evaluate empty values with strtotime() below, as it returns "1970-01-01" when it should be // saved as NULL in database $this->value = null; return; } // @todo This needs tidy up (what if you only specify a month and a year, for example?) if(is_array($value)) { if(!empty($value['Day']) && !empty($value['Month']) && !empty($value['Year'])) { $this->value = $value['Year'] . '-' . $value['Month'] . '-' . $value['Day']; return; } else { // return nothing (so checks below don't fail on an empty array) return null; } } // Default to NZ date format - strtotime expects a US date if(preg_match('#^([0-9]+)/([0-9]+)/([0-9]+)$#', $value, $parts)) { $value = "$parts[2]/$parts[1]/$parts[3]"; } if(is_numeric($value)) { $this->value = date('Y-m-d', $value); } elseif(is_string($value)) { try{ $date = new DateTime($value); $this->value = $date->Format('Y-m-d'); return; }catch(Exception $e){ $this->value = null; return; } } } /** * Returns the date in the format dd/mm/yy */ public function Nice() { if($this->value) return $this->Format('d/m/Y'); } /** * Returns the date in US format: “01/18/2006” */ public function NiceUS() { if($this->value) return $this->Format('m/d/Y'); } /** * Returns the year from the given date */ public function Year() { if($this->value) return $this->Format('Y'); } /** * Returns the Full day, of the given date. */ public function Day(){ if($this->value) return $this->Format('l'); } /** * Returns a full textual representation of a month, such as January. */ public function Month() { if($this->value) return $this->Format('F'); } /** * Returns the short version of the month such as Jan */ public function ShortMonth() { if($this->value) return $this->Format('M'); } /** * Returns the day of the month. * @param boolean $includeOrdinals Include ordinal suffix to day, e.g. "th" or "rd" * @return string */ public function DayOfMonth($includeOrdinal = false) { if($this->value) { $format = 'j'; if ($includeOrdinal) $format .= 'S'; return $this->Format($format); } } /** * Returns the date in the format 24 December 2006 */ public function Long() { if($this->value) return $this->Format('j F Y'); } /** * Returns the date in the format 24 Dec 2006 */ public function Full() { if($this->value) return $this->Format('j M Y'); } /** * Return the date using a particular formatting string. * * @param string $format Format code string. e.g. "d M Y" (see http://php.net/date) * @return string The date in the requested format */ public function Format($format) { if($this->value){ $date = new DateTime($this->value); return $date->Format($format); } } /** * Return the date formatted using the given strftime formatting string. * * strftime obeys the current LC_TIME/LC_ALL when printing lexical values * like day- and month-names */ public function FormatI18N($formattingString) { if($this->value) { return strftime($formattingString, strtotime($this->value)); } } /** * Return a date formatted as per a CMS user's settings. * * @param Member $member * @return boolean | string A date formatted as per user-defined settings. */ public function FormatFromSettings($member = null) { require_once 'Zend/Date.php'; if(!$member) { if(!Member::currentUserID()) { return false; } $member = Member::currentUser(); } $formatD = $member->getDateFormat(); $zendDate = new Zend_Date($this->getValue(), 'y-MM-dd'); return $zendDate->toString($formatD); } /* * Return a string in the form "12 - 16 Sept" or "12 Aug - 16 Sept" * @param Date $otherDateObj Another date object specifying the end of the range * @param boolean $includeOrdinals Include ordinal suffix to day, e.g. "th" or "rd" * @return string */ public function RangeString($otherDateObj, $includeOrdinals = false) { $d1 = $this->DayOfMonth($includeOrdinals); $d2 = $otherDateObj->DayOfMonth($includeOrdinals); $m1 = $this->ShortMonth(); $m2 = $otherDateObj->ShortMonth(); $y1 = $this->Year(); $y2 = $otherDateObj->Year(); if($y1 != $y2) return "$d1 $m1 $y1 - $d2 $m2 $y2"; else if($m1 != $m2) return "$d1 $m1 - $d2 $m2 $y1"; else return "$d1 - $d2 $m1 $y1"; } public function Rfc822() { if($this->value) return date('r', strtotime($this->value)); } public function Rfc2822() { if($this->value) return date('Y-m-d H:i:s', strtotime($this->value)); } public function Rfc3339() { $timestamp = ($this->value) ? strtotime($this->value) : false; if(!$timestamp) return false; $date = date('Y-m-d\TH:i:s', $timestamp); $matches = array(); if(preg_match('/^([\-+])(\d{2})(\d{2})$/', date('O', $timestamp), $matches)) { $date .= $matches[1].$matches[2].':'.$matches[3]; } else { $date .= 'Z'; } return $date; } /** * Returns the number of seconds/minutes/hours/days or months since the timestamp. * * @param boolean $includeSeconds Show seconds, or just round to "less than a minute". * @param int $significance Minimum significant value of X for "X units ago" to display * @return String */ public function Ago($includeSeconds = true, $significance = 2) { if($this->value) { $time = SS_Datetime::now()->Format('U'); if(strtotime($this->value) == $time || $time > strtotime($this->value)) { return _t( 'Date.TIMEDIFFAGO', "{difference} ago", 'Natural language time difference, e.g. 2 hours ago', array('difference' => $this->TimeDiff($includeSeconds, $significance)) ); } else { return _t( 'Date.TIMEDIFFIN', "in {difference}", 'Natural language time difference, e.g. in 2 hours', array('difference' => $this->TimeDiff($includeSeconds, $significance)) ); } } } /** * @param boolean $includeSeconds Show seconds, or just round to "less than a minute". * @param int $significance Minimum significant value of X for "X units ago" to display * @return string */ public function TimeDiff($includeSeconds = true, $significance = 2) { if(!$this->value) return false; $time = SS_Datetime::now()->Format('U'); $ago = abs($time - strtotime($this->value)); if($ago < 60 && !$includeSeconds) { return _t('Date.LessThanMinuteAgo', 'less than a minute'); } elseif($ago < $significance * 60 && $includeSeconds) { return $this->TimeDiffIn('seconds'); } elseif($ago < $significance * 3600) { return $this->TimeDiffIn('minutes'); } elseif($ago < $significance * 86400) { return $this->TimeDiffIn('hours'); } elseif($ago < $significance * 86400 * 30) { return $this->TimeDiffIn('days'); } elseif($ago < $significance * 86400 * 365) { return $this->TimeDiffIn('months'); } else { return $this->TimeDiffIn('years'); } } /** * Gets the time difference, but always returns it in a certain format * * @param string $format The format, could be one of these: * 'seconds', 'minutes', 'hours', 'days', 'months', 'years'. * @return string The resulting formatted period */ public function TimeDiffIn($format) { if(!$this->value) return false; $time = SS_Datetime::now()->Format('U'); $ago = abs($time - strtotime($this->value)); switch($format) { case "seconds": $span = $ago; return ($span != 1) ? "{$span} "._t("Date.SECS", "secs") : "{$span} "._t("Date.SEC", "sec"); case "minutes": $span = round($ago/60); return ($span != 1) ? "{$span} "._t("Date.MINS", "mins") : "{$span} "._t("Date.MIN", "min"); case "hours": $span = round($ago/3600); return ($span != 1) ? "{$span} "._t("Date.HOURS", "hours") : "{$span} "._t("Date.HOUR", "hour"); case "days": $span = round($ago/86400); return ($span != 1) ? "{$span} "._t("Date.DAYS", "days") : "{$span} "._t("Date.DAY", "day"); case "months": $span = round($ago/86400/30); return ($span != 1) ? "{$span} "._t("Date.MONTHS", "months") : "{$span} "._t("Date.MONTH", "month"); case "years": $span = round($ago/86400/365); return ($span != 1) ? "{$span} "._t("Date.YEARS", "years") : "{$span} "._t("Date.YEAR", "year"); } } public function requireField() { $parts=Array('datatype'=>'date', 'arrayValue'=>$this->arrayValue); $values=Array('type'=>'date', 'parts'=>$parts); DB::require_field($this->tableName, $this->name, $values); } /** * Returns true if date is in the past. * @return boolean */ public function InPast() { return strtotime($this->value) < SS_Datetime::now()->Format('U'); } /** * Returns true if date is in the future. * @return boolean */ public function InFuture() { return strtotime($this->value) > SS_Datetime::now()->Format('U'); } /** * Returns true if date is today. * @return boolean */ public function IsToday() { return (date('Y-m-d', strtotime($this->value)) == SS_Datetime::now()->Format('Y-m-d')); } /** * Returns a date suitable for insertion into a URL and use by the system. */ public function URLDate() { return date('Y-m-d', strtotime($this->value)); } public function days_between($fyear, $fmonth, $fday, $tyear, $tmonth, $tday){ return abs((mktime ( 0, 0, 0, $fmonth, $fday, $fyear) - mktime ( 0, 0, 0, $tmonth, $tday, $tyear))/(60*60*24)); } public function day_before($fyear, $fmonth, $fday){ return date ("Y-m-d", mktime (0,0,0,$fmonth,$fday-1,$fyear)); } public function next_day($fyear, $fmonth, $fday){ return date ("Y-m-d", mktime (0,0,0,$fmonth,$fday+1,$fyear)); } public function weekday($fyear, $fmonth, $fday){ // 0 is a Monday return (((mktime ( 0, 0, 0, $fmonth, $fday, $fyear) - mktime ( 0, 0, 0, 7, 17, 2006))/(60*60*24))+700000) % 7; } public function prior_monday($fyear, $fmonth, $fday){ return date ("Y-m-d", mktime (0,0,0,$fmonth,$fday-$this->weekday($fyear, $fmonth, $fday),$fyear)); } /** * Return the nearest date in the past, based on day and month. * Automatically attaches the correct year. * * This is useful for determining a financial year start or end date. * * @param $fmonth int The number of the month (e.g. 3 is March, 4 is April) * @param $fday int The day of the month * @param $fyear int Determine historical value * @return string Date in YYYY-MM-DD format */ public static function past_date($fmonth, $fday = 1, $fyear = null) { if(!$fyear) $fyear = date('Y'); $fday = (int) $fday; $fmonth = (int) $fmonth; $fyear = (int) $fyear; $pastDate = mktime(0, 0, 0, $fmonth, $fday, $fyear); $curDate = mktime(0, 0, 0, date('m'), date('d'), $fyear); if($pastDate < $curDate) { return date('Y-m-d', mktime(0, 0, 0, $fmonth, $fday, $fyear)); } else { return date('Y-m-d', mktime(0, 0, 0, $fmonth, $fday, $fyear - 1)); } } public function scaffoldFormField($title = null, $params = null) { $field = DateField::create($this->name, $title); // Show formatting hints for better usability $field->setDescription(sprintf( _t('FormField.Example', 'e.g. %s', 'Example format'), Convert::raw2xml(Zend_Date::now()->toString($field->getConfig('dateformat'))) )); $field->setAttribute('placeholder', $field->getConfig('dateformat')); return $field; } }