mirror of
https://github.com/salesagility/SuiteCRM.git
synced 2024-11-21 15:37:57 +00:00
658 lines
20 KiB
PHP
Executable File
658 lines
20 KiB
PHP
Executable File
<?php
|
|
/**
|
|
*
|
|
* SugarCRM Community Edition is a customer relationship management program developed by
|
|
* SugarCRM, Inc. Copyright (C) 2004-2013 SugarCRM Inc.
|
|
*
|
|
* SuiteCRM is an extension to SugarCRM Community Edition developed by SalesAgility Ltd.
|
|
* Copyright (C) 2011 - 2018 SalesAgility Ltd.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it under
|
|
* the terms of the GNU Affero General Public License version 3 as published by the
|
|
* Free Software Foundation with the addition of the following permission added
|
|
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
|
|
* IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
|
|
* OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
* details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License along with
|
|
* this program; if not, see http://www.gnu.org/licenses or write to the Free
|
|
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
* 02110-1301 USA.
|
|
*
|
|
* You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
|
|
* SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
|
|
*
|
|
* The interactive user interfaces in modified source and object code versions
|
|
* of this program must display Appropriate Legal Notices, as required under
|
|
* Section 5 of the GNU Affero General Public License version 3.
|
|
*
|
|
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
|
|
* these Appropriate Legal Notices must retain the display of the "Powered by
|
|
* SugarCRM" logo and "Supercharged by SuiteCRM" logo. If the display of the logos is not
|
|
* reasonably feasible for technical reasons, the Appropriate Legal Notices must
|
|
* display the words "Powered by SugarCRM" and "Supercharged by SuiteCRM".
|
|
*/
|
|
|
|
|
|
/**
|
|
* Sugar DateTime container
|
|
* Extends regular PHP DateTime with useful services
|
|
* @api
|
|
*/
|
|
#[\AllowDynamicProperties]
|
|
class SugarDateTime extends DateTime
|
|
{
|
|
// Recognized properties and their formats
|
|
protected $formats = array(
|
|
"sec" => "s",
|
|
"min" => "i",
|
|
"hour" => "G",
|
|
"zhour" => "H",
|
|
"day" => "j",
|
|
"zday" => "d",
|
|
"days_in_month" => "t",
|
|
"day_of_week" => "w",
|
|
"day_of_year" => "z",
|
|
"week" => "W",
|
|
"month" => "n",
|
|
"zmonth" => "m",
|
|
"year" => "Y",
|
|
"am_pm" => "A",
|
|
"hour_12" => "g",
|
|
);
|
|
|
|
// Property aliases
|
|
protected $var_gets = array(
|
|
"24_hour" => "hour",
|
|
"day_of_week" => "day_of_week_long",
|
|
"day_of_week_short" => "day_of_week_short",
|
|
"month_name" => "month_long",
|
|
"hour" => "hour_12",
|
|
);
|
|
|
|
/**
|
|
* @var DateTimeZone
|
|
*/
|
|
protected static $_gmt;
|
|
|
|
/**
|
|
* Calendar strings
|
|
* @var array
|
|
*/
|
|
protected $_strings;
|
|
|
|
/**
|
|
* For testing - if we allowed to use PHP date parse
|
|
* @var bool
|
|
*/
|
|
public static $use_php_parser = true;
|
|
|
|
/**
|
|
* For testing - if we allowed to use strptime()
|
|
* @var bool
|
|
*/
|
|
public static $use_strptime = true;
|
|
|
|
/**
|
|
* Copy of DateTime::createFromFormat
|
|
*
|
|
* Needed to return right type of the object
|
|
*
|
|
* @param string $format Format like in date()
|
|
* @param string $time Time to parse
|
|
* @param DateTimeZone $timezone
|
|
* @return SugarDateTime
|
|
* @see DateTime::createFromFormat
|
|
*/
|
|
#[ReturnTypeWillChange]
|
|
public static function createFromFormat($format, $time, $timezone = null)
|
|
{
|
|
if (empty($time) || empty($format)) {
|
|
return false;
|
|
}
|
|
if (self::$use_php_parser && is_callable(array("DateTime", "createFromFormat"))) {
|
|
// 5.3, hurray!
|
|
if (!empty($timezone)) {
|
|
$d = parent::createFromFormat($format, $time, $timezone);
|
|
} else {
|
|
$d = parent::createFromFormat($format, $time);
|
|
}
|
|
} else {
|
|
// doh, 5.2, will have to simulate
|
|
$d = self::_createFromFormat($format, $time, $timezone);
|
|
}
|
|
if (!$d) {
|
|
return false;
|
|
}
|
|
$sd = new self($d->format(DateTime::ISO8601));
|
|
$sd->setTimezone($d->getTimezone());
|
|
return $sd;
|
|
}
|
|
|
|
/**
|
|
* Internal _createFromFormat implementation for 5.2
|
|
*
|
|
* @internal
|
|
* @deprecated No longer necessary since PHP 5.2 is no longer supported.
|
|
* @param string $format Format like in date()
|
|
* @param string $time Time string to parse
|
|
* @param \DateTimeZone|null $timezone TZ
|
|
* @return SugarDateTime
|
|
* @see DateTime::createFromFormat
|
|
*/
|
|
protected static function _createFromFormat($format, $time, DateTimeZone $timezone = null)
|
|
{
|
|
$res = new self();
|
|
if (!empty($timezone)) {
|
|
$res->setTimezone($timezone);
|
|
}
|
|
if (self::$use_strptime && function_exists("strptime")) {
|
|
$str_format = str_replace(array_keys(TimeDate::$format_to_str), array_values(TimeDate::$format_to_str), $format);
|
|
// for a reason unknown to modern science, %P doesn't work in strptime
|
|
$str_format = str_replace("%P", "%p", $str_format);
|
|
// strip spaces before am/pm as our formats don't have them
|
|
$time = preg_replace('/\s+(AM|PM)/i', '\1', $time);
|
|
// TODO: better way to not risk locale stuff problems?
|
|
$data = strptime($time, $str_format);
|
|
if (empty($data)) {
|
|
$GLOBALS['log']->error("Cannot parse $time for format $format");
|
|
return null;
|
|
}
|
|
if ($data["tm_year"] == 0) {
|
|
unset($data["tm_year"]);
|
|
}
|
|
if ($data["tm_mday"] == 0) {
|
|
unset($data["tm_mday"]);
|
|
}
|
|
if (isset($data["tm_year"])) {
|
|
$data["tm_year"] += 1900;
|
|
}
|
|
if (isset($data["tm_mon"])) {
|
|
$data["tm_mon"]++;
|
|
}
|
|
$data += self::$data_init; // fill in missing parts
|
|
} else {
|
|
// Windows, etc. might not have strptime - we'd have to work harder here
|
|
$data = $res->_strptime($time, $format);
|
|
}
|
|
if (empty($data)) {
|
|
$GLOBALS['log']->error("Cannot parse $time for format $format");
|
|
return null;
|
|
}
|
|
if (isset($data["tm_year"])) {
|
|
$res->setDate($data["tm_year"], $data["tm_mon"], $data["tm_mday"]);
|
|
}
|
|
$res->setTime($data["tm_hour"], $data["tm_min"], $data["tm_sec"]);
|
|
return $res;
|
|
}
|
|
|
|
/**
|
|
* Load language Calendar strings
|
|
* @internal
|
|
* @param string $name string section to return
|
|
* @return array
|
|
*/
|
|
protected function _getStrings($name)
|
|
{
|
|
if (empty($this->_strings)) {
|
|
$this->_strings = return_mod_list_strings_language($GLOBALS['current_language'], "Calendar");
|
|
}
|
|
return $this->_strings[$name];
|
|
}
|
|
|
|
/**
|
|
* Fetch property of the date by name
|
|
* @param string $var Property name
|
|
* @return mixed
|
|
*/
|
|
public function __get($var)
|
|
{
|
|
// simple formats
|
|
if (isset($this->formats[$var])) {
|
|
return $this->format($this->formats[$var]);
|
|
}
|
|
// conditional, derived and translated ones
|
|
switch ($var) {
|
|
case "ts":
|
|
return $this->format("U")+0;
|
|
case "tz_offset":
|
|
return $this->getTimezone()->getOffset($this);
|
|
case "days_in_year":
|
|
return $this->format("L") == '1'?366:365;
|
|
break;
|
|
case "day_of_week_short":
|
|
$str = $this->_getStrings('dom_cal_weekdays');
|
|
return $str[$this->day_of_week];
|
|
case "day_of_week_long":
|
|
$str = $this->_getStrings('dom_cal_weekdays_long');
|
|
return $str[$this->day_of_week];
|
|
case "month_short":
|
|
$str = $this->_getStrings('dom_cal_month');
|
|
return $str[$this->month];
|
|
case "month_long":
|
|
$str = $this->_getStrings('dom_cal_month_long');
|
|
return $str[$this->month];
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* Implement some get_ methods that fetch variables
|
|
*
|
|
* @param string $name
|
|
* @param array $args
|
|
* @return mixed
|
|
*/
|
|
public function __call($name, $args)
|
|
{
|
|
// fill in 5.2.x gaps
|
|
if ($name == "getTimestamp") {
|
|
return $this->format('U')+0;
|
|
}
|
|
if ($name == "setTimestamp") {
|
|
$sec = (int)$args[0];
|
|
$sd = new self("@$sec");
|
|
$sd->setTimezone($this->getTimezone());
|
|
return $sd;
|
|
}
|
|
|
|
// getters
|
|
if (substr($name, 0, 4) == "get_") {
|
|
$var = substr($name, 4);
|
|
|
|
if (isset($this->var_gets[$var])) {
|
|
return $this->__get($this->var_gets[$var]);
|
|
}
|
|
|
|
if (isset($this->formats[$var])) {
|
|
return $this->__get($var);
|
|
}
|
|
}
|
|
$GLOBALS['log']->fatal("SugarDateTime: unknowm method $name called");
|
|
sugar_die("SugarDateTime: unknowm method $name called");
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Get specific hour of today.
|
|
*
|
|
* @deprecated This function is unused and will be removed in a future release.
|
|
* @param int $hour_index
|
|
* @return SugarDateTime
|
|
*/
|
|
public function get_datetime_by_index_today($hour_index)
|
|
{
|
|
if ($hour_index < 0 || $hour_index > 23) {
|
|
sugar_die("hour is outside of range");
|
|
}
|
|
|
|
$newdate = clone $this;
|
|
$newdate->setTime($hour_index, 0, 0);
|
|
return $newdate;
|
|
}
|
|
|
|
/**
|
|
* Get the last second of current hour
|
|
*
|
|
* @deprecated This function is unused and will be removed in a future release.
|
|
* @return SugarDateTime
|
|
*/
|
|
public function get_hour_end_time()
|
|
{
|
|
$newdate = clone $this;
|
|
$newdate->setTime($this->hour, 59, 59);
|
|
return $newdate;
|
|
}
|
|
|
|
/**
|
|
* Get the last second of the current day
|
|
*
|
|
* @deprecated This function is unused and will be removed in a future release.
|
|
* @return SugarDateTime
|
|
*/
|
|
public function get_day_end_time()
|
|
{
|
|
$newdate = clone $this;
|
|
return $newdate->setTime(23, 59, 59);
|
|
}
|
|
|
|
/**
|
|
* Get the beginning of i's day of the week
|
|
* @param int $day_index Day, 0 is Sunday, 1 is Monday, etc.
|
|
* @return SugarDateTime
|
|
*/
|
|
public function get_day_by_index_this_week($day_index)
|
|
{
|
|
$newdate = clone $this;
|
|
$newdate->setDate($this->year, $this->month, $this->day +
|
|
($day_index - $this->day_of_week))->setTime(0, 0);
|
|
return $newdate;
|
|
}
|
|
|
|
/**
|
|
* Get the beginning of the last day of i's the month
|
|
*
|
|
* @deprecated This function is unused and will be removed in a future release.
|
|
* @param int $month_index Month, January is 0
|
|
* @return SugarDateTime
|
|
*/
|
|
public function get_day_by_index_this_year($month_index)
|
|
{
|
|
$newdate = clone $this;
|
|
$newdate->setDate($this->year, $month_index+1, 1);
|
|
$newdate->setDate($newdate->year, $newdate->month, $newdate->days_in_month);
|
|
$newdate->setTime(0, 0);
|
|
return $newdate;
|
|
}
|
|
|
|
/**
|
|
* Get the beginning of i's day of the month
|
|
*
|
|
* @param int $day_index 0 is the first day of the month (sic!)
|
|
* @return SugarDateTime
|
|
*/
|
|
public function get_day_by_index_this_month($day_index)
|
|
{
|
|
$newdate = clone $this;
|
|
return $newdate->setDate($this->year, $this->month, $day_index+1)->setTime(0, 0);
|
|
}
|
|
|
|
/**
|
|
* Get new date, modified by date expression
|
|
*
|
|
* @example $yesterday = $today->get("yesterday");
|
|
*
|
|
* @param string $expression
|
|
* @return SugarDateTime
|
|
*/
|
|
public function get($expression)
|
|
{
|
|
$newdate = clone $this;
|
|
$newdate->modify($expression);
|
|
return $newdate;
|
|
}
|
|
|
|
/**
|
|
* Create from ISO 8601 datetime
|
|
* @param string $str
|
|
* @return SugarDateTime
|
|
*/
|
|
public static function parse_utc_date_time($str)
|
|
{
|
|
return new self($str);
|
|
}
|
|
|
|
/**
|
|
* Create a list of time slots for calendar view
|
|
* Times must be in user TZ
|
|
*
|
|
* @deprecated This function is unused and will be removed in a future release.
|
|
* @param string $view Which view we are using - day, week, month
|
|
* @param SugarDateTime $start_time Start time
|
|
* @param SugarDateTime $end_time End time
|
|
* @return array
|
|
*/
|
|
public static function getHashList($view, $start_time, $end_time)
|
|
{
|
|
$hash_list = array();
|
|
|
|
if ($view != 'day') {
|
|
$end_time = $end_time->get_day_end_time();
|
|
}
|
|
|
|
$end = $end_time->ts;
|
|
if ($end <= $start_time->ts) {
|
|
$end = $start_time->ts+1;
|
|
}
|
|
|
|
$new_time = clone $start_time;
|
|
$new_time->setTime($new_time->hour, 0, 0);
|
|
|
|
while ($new_time->ts < $end) {
|
|
if ($view == 'day') {
|
|
$hash_list[] = $new_time->format(TimeDate::DB_DATE_FORMAT) . ":" . $new_time->hour;
|
|
$new_time->modify("next hour");
|
|
} else {
|
|
$hash_list[] = $new_time->format(TimeDate::DB_DATE_FORMAT);
|
|
$new_time->modify("next day");
|
|
}
|
|
}
|
|
|
|
return $hash_list;
|
|
}
|
|
|
|
/**
|
|
* Get the beginning of the given day
|
|
*
|
|
* @param int $day Day, starting with 1, default is current
|
|
* @param int $month Month, starting with 1, default is current
|
|
* @param int $year Year, default is current
|
|
* @return SugarDateTime
|
|
*/
|
|
public function get_day_begin($day = null, $month = null, $year = null)
|
|
{
|
|
$newdate = clone $this;
|
|
$newdate->setDate(
|
|
$year?$year:$this->year,
|
|
$month?$month:$this->month,
|
|
$day?$day:$this->day
|
|
);
|
|
$newdate->setTime(0, 0);
|
|
return $newdate;
|
|
}
|
|
|
|
/**
|
|
* Get the last second of the given day
|
|
*
|
|
* @param int $day Day, starting with 1, default is current
|
|
* @param int $month Month, starting with 1, default is current
|
|
* @param int $year Year, default is current
|
|
* @return SugarDateTime
|
|
*/
|
|
public function get_day_end($day = null, $month = null, $year = null)
|
|
{
|
|
$newdate = clone $this;
|
|
$newdate->setDate(
|
|
$year?$year:$this->year,
|
|
$month?$month:$this->month,
|
|
$day?$day:$this->day
|
|
);
|
|
$newdate->setTime(23, 59, 59);
|
|
return $newdate;
|
|
}
|
|
|
|
/**
|
|
* Get the beginning of the first day of the year
|
|
*
|
|
* @deprecated This function is unused and will be removed in a future release.
|
|
* @param int $year
|
|
* @return SugarDateTime
|
|
*/
|
|
public function get_year_begin($year)
|
|
{
|
|
$newdate = clone $this;
|
|
$newdate->setDate($year, 1, 1);
|
|
$newdate->setTime(0, 0);
|
|
return $newdate;
|
|
}
|
|
|
|
/**
|
|
* Print datetime in standard DB format
|
|
*
|
|
* Set $tz parameter to false if you are sure that the date is in UTC.
|
|
*
|
|
* @param bool $tz do conversion to UTC
|
|
* @return string
|
|
*/
|
|
public function asDb($tz = true)
|
|
{
|
|
if ($tz) {
|
|
if (empty(self::$_gmt)) {
|
|
self::$_gmt = new DateTimeZone("UTC");
|
|
}
|
|
$this->setTimezone(self::$_gmt);
|
|
}
|
|
return $this->format(TimeDate::DB_DATETIME_FORMAT);
|
|
}
|
|
|
|
/**
|
|
* Print date in standard DB format
|
|
*
|
|
* Set $tz parameter to false if you are sure that the date is in UTC.
|
|
*
|
|
* @param bool $tz do conversion to UTC
|
|
* @return string
|
|
*/
|
|
public function asDbDate($tz = true)
|
|
{
|
|
if ($tz) {
|
|
if (empty(self::$_gmt)) {
|
|
self::$_gmt = new DateTimeZone("UTC");
|
|
}
|
|
$this->setTimezone(self::$_gmt);
|
|
}
|
|
return $this->format(TimeDate::DB_DATE_FORMAT);
|
|
}
|
|
|
|
/**
|
|
* Get query string for the date, year=%d&month=%d&day=%d&hour=%d
|
|
*
|
|
* @return string
|
|
*/
|
|
public function get_date_str()
|
|
{
|
|
return sprintf("&year=%d&month=%d&day=%d&hour=%d", $this->year, $this->month, $this->day, $this->hour);
|
|
}
|
|
|
|
/**
|
|
* Convert date to string - 'r' format, like: Thu, 21 Dec 2000 16:01:07 +0200
|
|
* @return string
|
|
*/
|
|
public function __toString()
|
|
{
|
|
return $this->format('r');
|
|
}
|
|
|
|
/**
|
|
* Match between tm_ parts and date() format strings
|
|
* @var array
|
|
*/
|
|
protected static $parts_match = array(
|
|
'Y' => 'tm_year',
|
|
'm' => 'tm_mon',
|
|
'n' => 'tm_mon',
|
|
'd' => 'tm_mday',
|
|
'H' => 'tm_hour',
|
|
'h' => 'tm_hour',
|
|
'i' => 'tm_min',
|
|
's' => 'tm_sec',
|
|
);
|
|
|
|
protected static $data_init = array(
|
|
"tm_hour" => 0,
|
|
"tm_min" => 0,
|
|
"tm_sec" => 0,
|
|
);
|
|
|
|
protected static $strptime_short_mon;
|
|
protected static $strptime_long_mon;
|
|
|
|
/**
|
|
* DateTime homebrew parser
|
|
*
|
|
* Since some OSes and PHP versions (please upgrade to 5.3!) do not support built-in parsing functions,
|
|
* we have to restort to this ugliness.
|
|
*
|
|
* @deprecated This function is unnecessary since we no longer support PHP 5.2, and will be removed in a future release.
|
|
* @internal
|
|
* @param string $time Time formatted string
|
|
* @param string $format Format, as accepted by strptime()
|
|
* @return array Parsed parts
|
|
*/
|
|
protected function _strptime($time, $format)
|
|
{
|
|
$data = self::$data_init;
|
|
if (empty(self::$strptime_short_mon)) {
|
|
self::$strptime_short_mon = array_flip($this->_getStrings('dom_cal_month'));
|
|
unset(self::$strptime_short_mon[""]);
|
|
}
|
|
if (empty(self::$strptime_long_mon)) {
|
|
self::$strptime_long_mon = array_flip($this->_getStrings('dom_cal_month_long'));
|
|
unset(self::$strptime_long_mon[""]);
|
|
}
|
|
|
|
$regexp = TimeDate::get_regular_expression($format);
|
|
if (!preg_match('@'.$regexp['format'].'@', $time, $dateparts)) {
|
|
return false;
|
|
}
|
|
|
|
foreach (self::$parts_match as $part => $datapart) {
|
|
if (isset($regexp['positions'][$part]) && isset($dateparts[$regexp['positions'][$part]])) {
|
|
$data[$datapart] = (int)$dateparts[$regexp['positions'][$part]];
|
|
}
|
|
}
|
|
// now process non-numeric ones
|
|
if (isset($regexp['positions']['F']) && !empty($dateparts[$regexp['positions']['F']])) {
|
|
// FIXME: locale?
|
|
$mon = $dateparts[$regexp['positions']['F']];
|
|
if (isset(self::$sugar_strptime_long_mon[$mon])) {
|
|
$data["tm_mon"] = self::$sugar_strptime_long_mon[$mon];
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
if (isset($regexp['positions']['M']) && !empty($dateparts[$regexp['positions']['M']])) {
|
|
// FIXME: locale?
|
|
$mon = $dateparts[$regexp['positions']['M']];
|
|
if (isset(self::$sugar_strptime_short_mon[$mon])) {
|
|
$data["tm_mon"] = self::$sugar_strptime_short_mon[$mon];
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
if (isset($regexp['positions']['a']) && !empty($dateparts[$regexp['positions']['a']])) {
|
|
$ampm = trim($dateparts[$regexp['positions']['a']]);
|
|
if ($ampm == 'pm') {
|
|
if ($data["tm_hour"] != 12) {
|
|
$data["tm_hour"] += 12;
|
|
}
|
|
} else {
|
|
if ($ampm == 'am') {
|
|
if ($data["tm_hour"] == 12) {
|
|
// 12:00am is 00:00
|
|
$data["tm_hour"] = 0;
|
|
}
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isset($regexp['positions']['A']) && !empty($dateparts[$regexp['positions']['A']])) {
|
|
$ampm = trim($dateparts[$regexp['positions']['A']]);
|
|
if ($ampm == 'PM') {
|
|
if ($data["tm_hour"] != 12) {
|
|
$data["tm_hour"] += 12;
|
|
}
|
|
} else {
|
|
if ($ampm == 'AM') {
|
|
if ($data["tm_hour"] == 12) {
|
|
// 12:00am is 00:00
|
|
$data["tm_hour"] = 0;
|
|
}
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
}
|