Issues (4069)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

include/TimeDate.php (21 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
3
/*********************************************************************************
4
 * SugarCRM Community Edition is a customer relationship management program developed by
5
 * SugarCRM, Inc. Copyright (C) 2004-2013 SugarCRM Inc.
6
7
 * SuiteCRM is an extension to SugarCRM Community Edition developed by Salesagility Ltd.
8
 * Copyright (C) 2011 - 2014 Salesagility Ltd.
9
 *
10
 * This program is free software; you can redistribute it and/or modify it under
11
 * the terms of the GNU Affero General Public License version 3 as published by the
12
 * Free Software Foundation with the addition of the following permission added
13
 * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
14
 * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
15
 * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
16
 *
17
 * This program is distributed in the hope that it will be useful, but WITHOUT
18
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
19
 * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
20
 * details.
21
 *
22
 * You should have received a copy of the GNU Affero General Public License along with
23
 * this program; if not, see http://www.gnu.org/licenses or write to the Free
24
 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25
 * 02110-1301 USA.
26
 *
27
 * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
28
 * SW2-130, Cupertino, CA 95014, USA. or at email address [email protected].
29
 *
30
 * The interactive user interfaces in modified source and object code versions
31
 * of this program must display Appropriate Legal Notices, as required under
32
 * Section 5 of the GNU Affero General Public License version 3.
33
 *
34
 * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
35
 * these Appropriate Legal Notices must retain the display of the "Powered by
36
 * SugarCRM" logo and "Supercharged by SuiteCRM" logo. If the display of the logos is not
37
 * reasonably feasible for  technical reasons, the Appropriate Legal Notices must
38
 * display the words  "Powered by SugarCRM" and "Supercharged by SuiteCRM".
39
 ********************************************************************************/
40
41
require_once 'include/SugarDateTime.php';
42
43
/**
44
  * New Time & Date handling class
45
  * @api
46
  * Migration notes:
47
  * - to_db_time() requires either full datetime or time, won't work with just date
48
  * 	The reason is that it's not possible to know if short string has only date or only time,
49
  *     and it makes more sense to assume time for the time conversion function.
50
  */
51
class TimeDate
52
{
53
	const DB_DATE_FORMAT = 'Y-m-d';
54
	const DB_TIME_FORMAT = 'H:i:s';
55
    // little optimization
56
	const DB_DATETIME_FORMAT = 'Y-m-d H:i:s';
57
	const RFC2616_FORMAT = 'D, d M Y H:i:s \G\M\T';
58
59
    const SECONDS_IN_A_DAY = 86400;
60
61
    // Standard DB date/time formats
62
    // they are constant, vars are for compatibility
63
	public $dbDayFormat = self::DB_DATE_FORMAT;
64
    public $dbTimeFormat = self::DB_TIME_FORMAT;
65
66
    /**
67
     * Regexp for matching format elements
68
     * @var array
69
     */
70
    protected static $format_to_regexp = array(
71
    	'a' => '[ ]*[ap]m',
72
    	'A' => '[ ]*[AP]M',
73
    	'd' => '[0-9]{1,2}',
74
    	'j' => '[0-9]{1,2}',
75
    	'h' => '[0-9]{1,2}',
76
    	'H' => '[0-9]{1,2}',
77
    	'g' => '[0-9]{1,2}',
78
    	'G' => '[0-9]{1,2}',
79
   		'i' => '[0-9]{1,2}',
80
    	'm' => '[0-9]{1,2}',
81
    	'n' => '[0-9]{1,2}',
82
    	'Y' => '[0-9]{4}',
83
        's' => '[0-9]{1,2}',
84
    	'F' => '\w+',
85
    	"M" => '[\w]{1,3}',
86
    );
87
88
    /**
89
     * Relation between date() and strftime() formats
90
     * @var array
91
     */
92
    public static $format_to_str = array(
93
		// date
94
    	'Y' => '%Y',
95
96
    	'm' => '%m',
97
    	'M' => '%b',
98
    	'F' => '%B',
99
	    'n' => '%m',
100
101
       	'd' => '%d',
102
    	//'j' => '%e',
103
    	// time
104
       	'a' => '%P',
105
       	'A' => '%p',
106
107
    	'h' => '%I',
108
       	'H' => '%H',
109
    	//'g' => '%l',
110
       	//'G' => '%H',
111
112
       	'i' => '%M',
113
       	's' => '%S',
114
    );
115
116
    /**
117
     * GMT timezone object
118
     *
119
     * @var DateTimeZone
120
     */
121
    protected static $gmtTimezone;
122
123
    /**
124
     * Current time
125
     * @var SugarDateTime
126
     */
127
    protected $now;
128
129
    /**
130
     * The current user
131
     *
132
     * @var User
133
     */
134
    protected $user;
135
136
    /**
137
     * Current user's ID
138
     *
139
     * @var string
140
     */
141
    protected $current_user_id;
142
    /**
143
     * Current user's TZ
144
     * @var DateTimeZone
145
     */
146
    protected $current_user_tz;
147
148
    /**
149
     * Separator for current user time format
150
     *
151
     * @var string
152
     */
153
    protected $time_separator;
154
155
    /**
156
     * Always consider user TZ to be GMT and date format DB format - for SOAP etc.
157
     *
158
     * @var bool
159
     */
160
    protected $always_db = false;
161
162
    /**
163
     * Global instance of TimeDate
164
     * @var TimeDate
165
     */
166
    protected static $timedate;
167
168
    /**
169
     * Allow returning cached now() value
170
     * If false, new system time is checked each time now() is required
171
     * If true, same value is returned for whole request.
172
     * Also, current user's timezone is cached.
173
     * @var bool
174
     */
175
    public $allow_cache = true;
176
177
    /**
178
     * Create TimeDate handler
179
     * @param User $user User to work with, default if current user
180
     */
181 2
    public function __construct(User $user = null)
182
    {
183 2
        if (self::$gmtTimezone == null) {
184
            self::$gmtTimezone = new DateTimeZone("UTC");
185
        }
186 2
        $this->now = new SugarDateTime();
187 2
        $this->tzGMT($this->now);
188 2
        $this->user = $user;
189 2
    }
190
191
    /**
192
     * Set flag specifying we should always use DB format
193
     * @param bool $flag
194
     * @return TimeDate
195
     */
196
    public function setAlwaysDb($flag = true)
197
    {
198
        $this->always_db = $flag;
199
        $this->clearCache();
200
        return $this;
201
    }
202
203
    /**
204
     * Get "always use DB format" flag
205
     * @return bool
206
     */
207 167
    public function isAlwaysDb()
208
    {
209 167
        return !empty($GLOBALS['disable_date_format']) || $this->always_db;
210
    }
211
212
    /**
213
     * Get TimeDate instance
214
     * @return TimeDate
215
     */
216 30
    public static function getInstance()
217
    {
218 30
        if(empty(self::$timedate)) {
219
            if(ini_get('date.timezone') == '') {
220
                // Remove warning about default timezone
221
                date_default_timezone_set(@date('e'));
222
                try {
223
                    $tz = self::guessTimezone();
224
                } catch(Exception $e) {
225
                    $tz = "UTC"; // guess failed, switch to UTC
226
                }
227
                if(isset($GLOBALS['log'])) {
228
                    $GLOBALS['log']->fatal("Configuration variable date.timezone is not set, guessed timezone $tz. Please set date.timezone=\"$tz\" in php.ini!");
229
                }
230
                date_default_timezone_set($tz);
231
            }
232
            self::$timedate = new self;
233
        }
234 30
        return self::$timedate;
235
    }
236
237
    /**
238
     * Set current user for this object
239
     *
240
     * @param User $user User object, default is current user
241
     * @return TimeDate
242
     */
243
    public function setUser(User $user = null)
244
    {
245
        $this->user = $user;
246
        $this->clearCache();
247
        return $this;
248
    }
249
250
    /**
251
     * Figure out what the required user is
252
     *
253
     * The order is: supplied parameter, TimeDate's user, global current user
254
     *
255
     * @param User $user User object, default is current user
256
     * @internal
257
     * @return User
258
     */
259 167
    protected function _getUser(User $user = null)
260
    {
261 167
        if (empty($user)) {
262 163
            $user = $this->user;
263
        }
264 167
        if (empty($user)) {
265 163
            $user = $GLOBALS['current_user'];
266
        }
267 167
        return $user;
268
    }
269
270
    /**
271
     * Get timezone for the specified user
272
     *
273
     * @param User $user User object, default is current user
274
     * @return DateTimeZone
275
     */
276 159
    protected function _getUserTZ(User $user = null)
277
    {
278 159
        $user = $this->_getUser($user);
279 159
        if (empty($user) || $this->isAlwaysDb()) {
280
            return self::$gmtTimezone;
281
        }
282
283 159
        if ($this->allow_cache && $user->id == $this->current_user_id && ! empty($this->current_user_tz)) {
284
            // current user is cached
285
            return $this->current_user_tz;
286
        }
287
288 159
        $usertimezone = $user->getPreference('timezone');
289 159
        if(empty($usertimezone)) {
290 159
            return self::$gmtTimezone;
291
        }
292
        try {
293
            $tz = new DateTimeZone($usertimezone);
294
        } catch (Exception $e) {
295
            $GLOBALS['log']->fatal('Unknown timezone: ' . $usertimezone);
296
            return self::$gmtTimezone;
297
        }
298
299
        if (empty($this->current_user_id)) {
300
            $this->current_user_id = $user->id;
301
            $this->current_user_tz = $tz;
302
        }
303
304
        return $tz;
305
    }
306
307
    /**
308
     * Clears all cached data regarding current user
309
     */
310
    public function clearCache()
311
    {
312
        $this->current_user_id = null;
313
        $this->current_user_tz = null;
314
        $this->time_separator = null;
315
        $this->now = new SugarDateTime();
316
    }
317
318
    /**
319
     * Get user date format.
320
     * @todo add caching
321
     *
322
     * @param User $user user object, current user if not specified
323
     * @return string
324
     */
325 57
    public function get_date_format(User $user = null)
326
    {
327 57
        $user = $this->_getUser($user);
328
329 57
        if (empty($user) || $this->isAlwaysDb()) {
330
            return self::DB_DATE_FORMAT;
331
        }
332
333 57
        $datef = $user->getPreference('datef');
334 57
        if(empty($datef) && isset($GLOBALS['current_user']) && $GLOBALS['current_user'] !== $user) {
335
            // if we got another user and it has no date format, try current user
336
            $datef = $GLOBALS['current_user']->getPreference('datef');
337
        }
338 57
        if (empty($datef)) {
339
            $datef = $GLOBALS['sugar_config']['default_date_format'];
340
        }
341 57
        if (empty($datef)) {
342
            $datef = '';
343
        }
344
345 57
        return $datef;
346
    }
347
348
    /**
349
     * Get user time format.
350
     * @todo add caching
351
     *
352
     * @param User $user user object, current user if not specified
353
     * @return string
354
     */
355 25
    public function get_time_format(/*User*/ $user = null)
356
    {
357 25
        if(is_bool($user) || func_num_args() > 1) {
358
            // BC dance - old signature was boolean, User
359
            $GLOBALS['log']->fatal('TimeDate::get_time_format(): Deprecated API used, please update you code - get_time_format() now has one argument of type User');
360
            if(func_num_args() > 1) {
361
                $user = func_get_arg(1);
362
            } else {
363
                $user = null;
364
            }
365
        }
366 25
        $user = $this->_getUser($user);
367
368 25
        if (empty($user) || $this->isAlwaysDb()) {
369
            return self::DB_TIME_FORMAT;
370
        }
371
372 25
        $timef = $user->getPreference('timef');
373 25
        if(empty($timef) && isset($GLOBALS['current_user']) && $GLOBALS['current_user'] !== $user) {
374
            // if we got another user and it has no time format, try current user
375
            $timef = $GLOBALS['current_user']->getPreference('$timef');
376
        }
377 25
        if (empty($timef)) {
378
            $timef = $GLOBALS['sugar_config']['default_time_format'];
379
        }
380 25
        if (empty($timef)) {
381
            $timef = '';
382
        }
383 25
        return $timef;
384
    }
385
386
    /**
387
     * Get user datetime format.
388
     *
389
     * @param User $user user object, current user if not specified
390
     * @return string
391
     */
392 129
    public function get_date_time_format($user = null)
393
    {
394
        // BC fix - had (bool, user) signature before
395 129
        if(!($user instanceof User)) {
396 124
            if(func_num_args() > 1) {
397
                $user = func_get_arg(1);
398
                if(!($user instanceof User)) {
399
                    $user = null;
400
                }
401
            } else {
402 124
                $user = null;
403
            }
404
        }
405
406 129
        $cacheKey= $this->get_date_time_format_cache_key($user);
407 129
        $cachedValue = sugar_cache_retrieve($cacheKey);
408
409 129
        if(!empty($cachedValue) )
410
        {
411 129
            return $cachedValue;
412
        }
413
        else
414
        {
415 2
            $value = $this->merge_date_time($this->get_date_format($user), $this->get_time_format($user));
416 2
            sugar_cache_put($cacheKey,$value,0);
417 2
            return $value;
418
        }
419
    }
420
421
    /**
422
     * Retrieve the cache key used for user date/time formats
423
     *
424
     * @param $user
425
     * @return string
426
     */
427 129
    public function get_date_time_format_cache_key($user)
428
    {
429 129
        $cacheKey = get_class($this) ."dateTimeFormat";
430 129
        $user = $this->_getUser($user);
431
432 129
        if($user instanceof User)
433
        {
434 129
           $cacheKey .= "_{$user->id}";
435
        }
436
437 129
        if( $this->isAlwaysDb() )
438
            $cacheKey .= '_asdb';
439
        
440 129
        return $cacheKey;
441
    }
442
443
    /**
444
     * Get user's first day of week setting.
445
     *
446
     * @param User $user user object, current user if not specified
447
     * @return int Day, 0 = Sunday, 1 = Monday, etc...
448
     */
449
    public function get_first_day_of_week(User $user = null)
450
    {
451
        $user = $this->_getUser($user);
452
        $fdow = 0;
453
454
        if (!empty($user))
455
        {
456
          $fdow = $user->getPreference('fdow');
457
          if (empty($fdow))
458
              $fdow = 0;
459
        }
460
461
        return $fdow;
462
    }
463
464
465
    /**
466
     * Make one datetime string from date string and time string
467
     *
468
     * @param string $date
469
     * @param string $time
470
     * @return string New datetime string
471
     */
472 9
    function merge_date_time($date, $time)
473
    {
474 9
        return $date . ' ' . $time;
475
    }
476
477
    /**
478
     * Split datetime string into date & time
479
     *
480
     * @param string $datetime
481
     * @return array
482
     */
483 4
    function split_date_time($datetime)
484
    {
485 4
        return explode(' ', $datetime, 2);
486
    }
487
488
489
    /**
490
     * Get user date format in Javascript form
491
     * @return string
492
     */
493 13
    function get_cal_date_format()
494
    {
495 13
        return str_replace(array_keys(self::$format_to_str), array_values(self::$format_to_str), $this->get_date_format());
496
    }
497
498
    /**
499
     * Get user time format in Javascript form
500
     * @return string
501
     */
502
    function get_cal_time_format()
503
    {
504
        return str_replace(array_keys(self::$format_to_str), array_values(self::$format_to_str), $this->get_time_format());
505
    }
506
507
    /**
508
     * Get user date&time format in Javascript form
509
     * @return string
510
     */
511
    function get_cal_date_time_format()
512
    {
513
        return str_replace(array_keys(self::$format_to_str), array_values(self::$format_to_str), $this->get_date_time_format());
514
    }
515
516
    /**
517
     * Verify if the date string conforms to a format
518
     *
519
     * @param string $date
520
     * @param string $format Format to check
521
     *
522
     * @internal
523
     * @return bool Is the date ok?
524
     */
525
    public function check_matching_format($date, $format)
526
    {
527
        try {
528
            $dt = SugarDateTime::createFromFormat($format, $date);
529
            if (!is_object($dt)) {
530
                return false;
531
            }
532
        } catch (Exception $e) {
533
            return false;
534
        }
535
        return true;
536
    }
537
538
    /**
539
     * Format DateTime object as DB datetime
540
     *
541
     * @param DateTime $date
542
     * @return string
543
     */
544 86
    public function asDb(DateTime $date)
545
    {
546 86
        $date->setTimezone(self::$gmtTimezone);
547 86
        return $date->format($this->get_db_date_time_format());
548
    }
549
550
    /**
551
     * Format date as DB-formatted field type
552
     * @param DateTime $date
553
     * @param string $type Field type - date, time, datetime[combo]
554
     * @return string Formatted date
555
     */
556
    public function asDbType(DateTime $date, $type)
557
    {
558
        switch($type) {
559
            case "date":
560
                return $this->asDbDate($date);
561
                break;
0 ignored issues
show
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
562
            case 'time':
563
                return $this->asDbtime($date);
564
                break;
0 ignored issues
show
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
565
            case 'datetime':
566
            case 'datetimecombo':
567
            default:
568
                return $this->asDb($date);
569
        }
570
    }
571
572
    /**
573
     * Format DateTime object as user datetime
574
     *
575
     * @param DateTime $date
576
     * @param User $user
577
     * @return string
578
     */
579 38
    public function asUser(DateTime $date, User $user = null)
580
    {
581 38
        $this->tzUser($date, $user);
582 38
        return $date->format($this->get_date_time_format($user));
583
    }
584
585
    /**
586
     * Format date as user-formatted field type
587
     * @param DateTime $date
588
     * @param string $type Field type - date, time, datetime[combo]
589
     * @param User $user
590
     * @return string
591
     */
592
    public function asUserType(DateTime $date, $type, User $user = null)
593
    {
594
        switch($type) {
595
            case "date":
596
                return $this->asUserDate($date, true, $user);
597
                break;
0 ignored issues
show
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
598
            case 'time':
599
                return $this->asUserTime($date, true, $user);
0 ignored issues
show
The call to TimeDate::asUserTime() has too many arguments starting with $user.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
600
                break;
0 ignored issues
show
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
601
            case 'datetime':
602
            case 'datetimecombo':
603
            default:
604
                return $this->asUser($date, $user);
605
        }
606
    }
607
608
    /**
609
     * Produce timestamp offset by user's timezone
610
     *
611
     * So if somebody converts it to format assuming GMT, it would actually display user's time.
612
     * This is used by Javascript.
613
     *
614
     * @param DateTime $date
615
     * @param User $user
616
     * @return int
617
     */
618 3
    public function asUserTs(DateTime $date, User $user = null)
619
    {
620 3
        return $date->format('U')+$this->_getUserTZ($user)->getOffset($date);
621
    }
622
623
    /**
624
     * Format DateTime object as DB date
625
     * Note: by default does not convert TZ!
626
     * @param DateTime $date
627
     * @param boolean $tz Perform TZ conversion?
628
     * @return string
629
     */
630 4
    public function asDbDate(DateTime $date, $tz = false)
631
    {
632 4
        if($tz) $date->setTimezone(self::$gmtTimezone);
633 4
        return $date->format($this->get_db_date_format());
634
    }
635
636
    /**
637
     * Format DateTime object as user date
638
     * Note: by default does not convert TZ!
639
     * @param DateTime $date
640
     * @param boolean $tz Perform TZ conversion?
641
     * @param User $user
642
     * @return string
643
     */
644 37
    public function asUserDate(DateTime $date, $tz = false, User $user = null)
645
    {
646 37
        if($tz) $this->tzUser($date, $user);
647 37
        return $date->format($this->get_date_format($user));
648
    }
649
650
    /**
651
     * Format DateTime object as DB time
652
     *
653
     * @param DateTime $date
654
     * @return string
655
     */
656 2
    public function asDbTime(DateTime $date)
657
    {
658 2
        $date->setTimezone(self::$gmtTimezone);
659 2
        return $date->format($this->get_db_time_format());
660
    }
661
662
    /**
663
     * Format DateTime object as user time
664
     *
665
     * @param DateTime $date
666
     * @param User $user
667
     * @return string
668
     */
669 1
    public function asUserTime(DateTime $date, User $user = null)
670
    {
671 1
        $this->tzUser($date, $user);
672 1
        return $date->format($this->get_time_format($user));
673
    }
674
675
    /**
676
     * Get DateTime from DB datetime string
677
     *
678
     * @param string $date
679
     * @return SugarDateTime
680
     */
681 12
    public function fromDb($date)
682
    {
683
        try {
684 12
            return SugarDateTime::createFromFormat(self::DB_DATETIME_FORMAT, $date, self::$gmtTimezone);
685
        } catch (Exception $e) {
686
            $GLOBALS['log']->error("fromDb: Conversion of $date from DB format failed: {$e->getMessage()}");
687
            return null;
688
        }
689
    }
690
691
    /**
692
     * Create a date from a certain type of field in DB format
693
     * The types are: date, time, datatime[combo]
694
     * @param string $date the datetime string
695
     * @param string $type string type
696
     * @return SugarDateTime
697
     */
698
    public function fromDbType($date, $type)
699
    {
700
        switch($type) {
701
            case "date":
702
                return $this->fromDbDate($date);
703
                break;
0 ignored issues
show
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
704
            case 'time':
705
                return $this->fromDbFormat($date, self::DB_TIME_FORMAT);
706
                break;
0 ignored issues
show
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
707
            case 'datetime':
708
            case 'datetimecombo':
709
            default:
710
                return $this->fromDb($date);
711
        }
712
    }
713
714
    /**
715
     * Get DateTime from DB date string
716
     *
717
     * @param string $date
718
     * @return SugarDateTime
719
     */
720
    public function fromDbDate($date)
721
    {
722
        try {
723
            return SugarDateTime::createFromFormat(self::DB_DATE_FORMAT, $date, self::$gmtTimezone);
724
        } catch (Exception $e) {
725
            $GLOBALS['log']->error("fromDbDate: Conversion of $date from DB format failed: {$e->getMessage()}");
726
            return null;
727
        }
728
    }
729
730
    /**
731
     * Get DateTime from DB datetime string using non-standard format
732
     *
733
     * Non-standard format usually would be only date, only time, etc.
734
     *
735
     * @param string $date
736
     * @param string $format format to accept
737
     * @return SugarDateTime
738
     */
739
    public function fromDbFormat($date, $format)
740
    {
741
        try {
742
            return SugarDateTime::createFromFormat($format, $date, self::$gmtTimezone);
743
        } catch (Exception $e) {
744
            $GLOBALS['log']->error("fromDbFormat: Conversion of $date from DB format $format failed: {$e->getMessage()}");
745
            return null;
746
        }
747
    }
748
749
    /**
750
     * Get DateTime from user datetime string
751
     *
752
     * @param string $date
753
     * @param User $user
754
     * @return SugarDateTime
755
     */
756 7
    public function fromUser($date, User $user = null)
757
    {
758 7
        $res = null;
759
        try {
760 7
            $res = SugarDateTime::createFromFormat($this->get_date_time_format($user), $date, $this->_getUserTZ($user));
761
        } catch (Exception $e) {
762
            $GLOBALS['log']->error("fromUser: Conversion of $date exception: {$e->getMessage()}");
763
        }
764 7
        if(!($res instanceof DateTime)) {
765 4
            $uf = $this->get_date_time_format($user);
766 4
            $GLOBALS['log']->error("fromUser: Conversion of $date from user format $uf failed");
767 4
            return null;
768
        }
769 3
        return $res;
770
    }
771
772
    /**
773
     * Create a date from a certain type of field in user format
774
     * The types are: date, time, datatime[combo]
775
     * @param string $date the datetime string
776
     * @param string $type string type
777
     * @param User $user
778
     * @return SugarDateTime
779
     */
780
    public function fromUserType($date, $type, $user = null)
781
    {
782
        switch($type) {
783
            case "date":
784
                return $this->fromUserDate($date, $user);
785
                break;
0 ignored issues
show
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
786
            case 'time':
787
                return $this->fromUserTime($date, $user);
788
                break;
0 ignored issues
show
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
789
            case 'datetime':
790
            case 'datetimecombo':
791
            default:
792
                return $this->fromUser($date, $user);
793
        }
794
    }
795
796
    /**
797
     * Get DateTime from user time string
798
     *
799
     * @param string $date
800
     * @param User $user
801
     * @return SugarDateTime
802
     */
803
    public function fromUserTime($date, User $user = null)
804
    {
805
        try {
806
            return SugarDateTime::createFromFormat($this->get_time_format($user), $date, $this->_getUserTZ($user));
807
        } catch (Exception $e) {
808
            $uf = $this->get_time_format($user);
809
            $GLOBALS['log']->error("fromUserTime: Conversion of $date from user format $uf failed: {$e->getMessage()}");
810
            return null;
811
        }
812
    }
813
814
    /**
815
     * Get DateTime from user date string
816
	 * Usually for calendar-related functions like holidays
817
     * Note: by default does not convert tz!
818
     * @param string $date
819
     * @param bool $convert_tz perform TZ converson?
820
     * @param User $user
821
     * @return SugarDateTime
822
     */
823 13
    public function fromUserDate($date, $convert_tz = false, User $user = null)
824
    {
825
        try {
826 13
            return SugarDateTime::createFromFormat($this->get_date_format($user), $date, $convert_tz?$this->_getUserTZ($user):self::$gmtTimezone);
827
        } catch (Exception $e) {
828
            $uf = $this->get_date_format($user);
829
            $GLOBALS['log']->error("fromUserDate: Conversion of $date from user format $uf failed: {$e->getMessage()}");
830
            return null;
831
        }
832
    }
833
834
    /**
835
     * Create a date object from any string
836
     *
837
     * Same formats accepted as for DateTime ctor
838
     *
839
     * @param string $date
840
     * @param User $user
841
     * @return SugarDateTime
842
     */
843 18
    public function fromString($date, User $user = null)
844
    {
845
        try {
846 18
            return new SugarDateTime($date, $this->_getUserTZ($user));
847
        } catch (Exception $e) {
848
            $GLOBALS['log']->error("fromString: Conversion of $date from string failed: {$e->getMessage()}");
849
            return null;
850
        }
851
    }
852
853
    /**
854
     * Create DateTime from timestamp
855
     *
856
     * @param interger|string $ts
857
     * @return SugarDateTime
858
     */
859 2
    public function fromTimestamp($ts)
860
    {
861 2
        return new SugarDateTime("@$ts");
862
    }
863
864
    /**
865
     * Convert DateTime to GMT timezone
866
     * @param DateTime $date
867
     * @return DateTime
868
     */
869 2
    public function tzGMT(DateTime $date)
870
    {
871 2
        return $date->setTimezone(self::$gmtTimezone);
872
    }
873
874
    /**
875
     * Convert DateTime to user timezone
876
     * @param DateTime $date
877
     * @param User $user
878
     * @return DateTime
879
     */
880 76
    public function tzUser(DateTime $date, User $user = null)
881
    {
882 76
        return $date->setTimezone($this->_getUserTZ($user));
883
    }
884
885
    /**
886
     * Get string defining midnight in current user's format
887
     * @param string $format Time format to use
888
     * @return string
889
     */
890 4
    protected function _get_midnight($format = null)
891
    {
892 4
        $zero = new DateTime("@0", self::$gmtTimezone);
893 4
        return $zero->format($format?$format:$this->get_time_format());
894
    }
895
896
    /**
897
     *
898
     * Basic conversion function
899
     *
900
     * Converts between two string dates in different formats and timezones
901
     *
902
     * @param string $date
903
     * @param string $fromFormat
904
     * @param DateTimeZone $fromTZ
905
     * @param string $toFormat
906
     * @param DateTimeZone|null $toTZ
907
     * @param bool $expand If string lacks time, expand it to include time
908
     * @return string
909
     */
910 100
    protected function _convert($date, $fromFormat, $fromTZ, $toFormat, $toTZ, $expand = false)
911
    {
912 100
        $date = trim($date);
913 100
        if (empty($date)) {
914 3
            return $date;
915
        }
916
        try {
917 98
            if ($expand && strlen($date) <= 10) {
918 4
                $date = $this->expandDate($date, $fromFormat);
919
            }
920 98
            $phpdate = SugarDateTime::createFromFormat($fromFormat, $date, $fromTZ);
921 98
            if ($phpdate == false) {
922 1
                $GLOBALS['log']->error("convert: Conversion of $date from $fromFormat to $toFormat failed");
923 1
                return '';
924
            }
925 97
            if ($fromTZ !== $toTZ && $toTZ != null) {
926
                $phpdate->setTimeZone($toTZ);
927
            }
928 97
            return $phpdate->format($toFormat);
929
        } catch (Exception $e) {
930
            $GLOBALS['log']->error("Conversion of $date from $fromFormat to $toFormat failed: {$e->getMessage()}");
931
            return '';
932
        }
933
    }
934
935
    /**
936
     * Convert DB datetime to local datetime
937
     *
938
     * TZ conversion is controlled by parameter
939
     *
940
     * @param string $date Original date in DB format
941
     * @param bool $meridiem Ignored for BC
942
     * @param bool $convert_tz Perform TZ conversion?
943
     * @param User $user User owning the conversion formats
944
     * @return string Date in display format
945
     */
946 85
    function to_display_date_time($date, $meridiem = true, $convert_tz = true, $user = null)
947
    {
948 85
        return $this->_convert($date, self::DB_DATETIME_FORMAT, self::$gmtTimezone, $this->get_date_time_format($user),
949 85
            $convert_tz ? $this->_getUserTZ($user) : self::$gmtTimezone, true);
950
    }
951
952
    /**
953
     * Converts DB time string to local time string
954
     *
955
     * TZ conversion depends on parameter
956
     *
957
     * @param string $date Time in DB format
958
     * @param bool $meridiem
959
     * @param bool $convert_tz Perform TZ conversion?
960
     * @return string Time in user-defined format
961
     */
962 4
    public function to_display_time($date, $meridiem = true, $convert_tz = true)
963
    {
964 4
        if($convert_tz && strpos($date, ' ') === false) {
965
            // we need TZ adjustment but have no date, assume today
966
            $date = $this->expandTime($date, self::DB_DATETIME_FORMAT, self::$gmtTimezone);
967
        }
968 4
        return $this->_convert($date,
969 4
            $convert_tz ? self::DB_DATETIME_FORMAT : self::DB_TIME_FORMAT, self::$gmtTimezone,
970 4
            $this->get_time_format(), $convert_tz ? $this->_getUserTZ() : self::$gmtTimezone);
971
    }
972
973
    /**
974
     * Splits time in given format into components
975
     *
976
     * Components: h, m, s, a (am/pm) if format requires it
977
     * If format has am/pm, hour is 12-based, otherwise 24-based
978
     *
979
     * @param string $date
980
     * @param string $format
981
     * @return array
982
     */
983
    public function splitTime($date, $format)
984
    {
985
        if (! ($date instanceof DateTime)) {
986
            $date = SugarDateTime::createFromFormat($format, $date);
987
        }
988
        $ampm = strpbrk($format, 'aA');
989
        $datearr = array(
990
        	"h" => ($ampm == false) ? $date->format("H") : $date->format("h"),
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $ampm of type string to the boolean false. If you are specifically checking for an empty string, consider using the more explicit === '' instead.
Loading history...
991
        	'm' => $date->format("i"),
992
        	's' => $date->format("s")
993
        );
994
        if ($ampm) {
995
            $datearr['a'] = ($ampm{0} == 'a') ? $date->format("a") : $date->format("A");
996
        }
997
        return $datearr;
998
    }
999
1000
    /**
1001
     * Converts DB date string to local date string
1002
     *
1003
     * TZ conversion depens on parameter
1004
     *
1005
     * @param string $date Date in DB format
1006
     * @param bool $convert_tz Perform TZ conversion?
1007
     * @return string Date in user-defined format
1008
     */
1009 2
    public function to_display_date($date, $convert_tz = true)
1010
    {
1011 2
        return $this->_convert($date,
1012 2
            self::DB_DATETIME_FORMAT, self::$gmtTimezone,
1013 2
            $this->get_date_format(), $convert_tz ? $this->_getUserTZ() : self::$gmtTimezone, true);
1014
    }
1015
1016
    /**
1017
     * Convert date from format to format
1018
     *
1019
     * No TZ conversion is performed!
1020
     *
1021
     * @param string $date
1022
     * @param string $from Source format
1023
     * @param string $to Destination format
1024
     * @return string Converted date
1025
     */
1026
    function to_display($date, $from, $to)
1027
    {
1028
        return $this->_convert($date, $from, self::$gmtTimezone, $to, self::$gmtTimezone);
1029
    }
1030
1031
    /**
1032
     * Get DB datetime format
1033
     * @return string
1034
     */
1035 87
    public function get_db_date_time_format()
1036
    {
1037 87
        return self::DB_DATETIME_FORMAT;
1038
    }
1039
1040
    /**
1041
     * Get DB date format
1042
     * @return string
1043
     */
1044 4
    public function get_db_date_format()
1045
    {
1046 4
        return self::DB_DATE_FORMAT;
1047
    }
1048
1049
    /**
1050
     * Get DB time format
1051
     * @return string
1052
     */
1053 2
    public function get_db_time_format()
1054
    {
1055 2
        return self::DB_TIME_FORMAT;
1056
    }
1057
1058
    /**
1059
     * Convert date from local datetime to GMT-based DB datetime
1060
     *
1061
     * Includes TZ conversion.
1062
     *
1063
     * @param string $date
1064
     * @return string Datetime in DB format
1065
     */
1066 11
    public function to_db($date)
1067
    {
1068 11
        return $this->_convert($date,
1069 11
            $this->get_date_time_format(), $this->_getUserTZ(),
1070 11
            $this->get_db_date_time_format(), self::$gmtTimezone,
1071 11
            true);
1072
    }
1073
1074
    /**
1075
     * Convert local datetime to DB date
1076
     *
1077
     * TZ conversion depends on parameter. If false, only format conversion is performed.
1078
     *
1079
     * @param string $date Local date
1080
     * @param bool $convert_tz Should time and TZ be taken into account?
1081
     * @return string Date in DB format
1082
     */
1083 5
    public function to_db_date($date, $convert_tz = true)
1084
    {
1085 5
        return $this->_convert($date,
1086 5
            $this->get_date_time_format(), $convert_tz ? $this->_getUserTZ() : self::$gmtTimezone,
1087 5
            self::DB_DATE_FORMAT, self::$gmtTimezone, true);
1088
    }
1089
1090
    /**
1091
     * Convert local datetime to DB time
1092
     *
1093
     * TZ conversion depends on parameter. If false, only format conversion is performed.
1094
     *
1095
     * @param string $date Local date
1096
     * @param bool $convert_tz Should time and TZ be taken into account?
1097
     * @return string Time in DB format
1098
     */
1099
    public function to_db_time($date, $convert_tz = true)
1100
    {
1101
        $format = $this->get_date_time_format();
1102
        $tz = $convert_tz ? $this->_getUserTZ() : self::$gmtTimezone;
1103
        if($convert_tz && strpos($date, ' ') === false) {
1104
            // we need TZ adjustment but have short string, expand it to full one
1105
            // FIXME: if the string is short, should we assume date or time?
1106
            $date = $this->expandTime($date, $format, $tz);
1107
        }
1108
        return $this->_convert($date,
1109
            $convert_tz ? $format : $this->get_time_format(),
1110
            $tz,
1111
            self::DB_TIME_FORMAT, self::$gmtTimezone);
1112
    }
1113
1114
    /**
1115
     * Takes a Date & Time value in local format and converts them to DB format
1116
     * No TZ conversion!
1117
     *
1118
     * @param string $date
1119
     * @param string $time
1120
     * @return array Date & time in DB format
1121
     **/
1122
    public function to_db_date_time($date, $time)
1123
    {
1124
        try {
1125
            $phpdate = SugarDateTime::createFromFormat($this->get_date_time_format(),
1126
                $this->merge_date_time($date, $time), self::$gmtTimezone);
1127
            if ($phpdate == false) {
1128
                return array('', '');
1129
            }
1130
            return array($this->asDbDate($phpdate), $this->asDbTime($phpdate));
1131
        } catch (Exception $e) {
1132
            $GLOBALS['log']->error("Conversion of $date,$time failed");
1133
            return array('', '');
1134
        }
1135
    }
1136
1137
    /**
1138
     * Return current time in DB format
1139
     * @return string
1140
     */
1141 86
    public function nowDb()
1142
    {
1143 86
        if(!$this->allow_cache) {
1144
            $nowGMT = $this->getNow();
1145
        } else {
1146 86
            $nowGMT = $this->now;
1147
        }
1148 86
        return $this->asDb($nowGMT);
1149
    }
1150
1151
    /**
1152
     * Return current date in DB format
1153
     * @return string
1154
     */
1155 1
    public function nowDbDate()
1156
    {
1157 1
        if(!$this->allow_cache) {
1158
            $nowGMT = $this->getNow();
1159
        } else {
1160 1
            $nowGMT = $this->now;
1161
        }
1162 1
        return $this->asDbDate($nowGMT, true);
1163
    }
1164
1165
    /**
1166
     * Get 'now' DateTime object
1167
     * @param bool $userTz return in user timezone?
1168
     * @return SugarDateTime
1169
     */
1170 84
    public function getNow($userTz = false)
1171
    {
1172 84
        if(!$this->allow_cache) {
1173
            return new SugarDateTime("now", $userTz?$this->_getUserTz():self::$gmtTimezone);
1174
        }
1175
        // TODO: should we return clone?
1176 84
        $now = clone $this->now;
1177 84
        if($userTz) {
1178 62
            return $this->tzUser($now);
1179
        }
1180 29
        return $now;
1181
    }
1182
1183
    /**
1184
     * Set 'now' time
1185
     * For testability - predictable time value
1186
     * @param DateTime $now
1187
     * @return TimeDate $this
1188
     */
1189
    public function setNow($now)
1190
    {
1191
        $this->now = $now;
0 ignored issues
show
Documentation Bug introduced by
$now is of type object<DateTime>, but the property $now was declared to be of type object<SugarDateTime>. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
1192
        return $this;
1193
    }
1194
1195
    /**
1196
     * Return current datetime in local format
1197
     * @return string
1198
     */
1199 7
    public function now()
1200
    {
1201 7
        return  $this->asUser($this->getNow());
1202
    }
1203
1204
    /**
1205
     * Return current date in User format
1206
     * @return string
1207
     */
1208 1
    public function nowDate()
1209
    {
1210 1
        return  $this->asUserDate($this->getNow());
1211
    }
1212
1213
    /**
1214
     * Get user format's time separator
1215
     * @return string
1216
     */
1217 4
    public function timeSeparator()
1218
    {
1219 4
        if (empty($this->time_separator)) {
1220 4
            $this->time_separator = $this->timeSeparatorFormat($this->get_time_format());
1221
        }
1222 4
        return $this->time_separator;
1223
    }
1224
1225
    /**
1226
     * Find out format's time separator
1227
     * @param string $timeformat Time format
1228
     * @return stringS
1229
     */
1230 4
    public function timeSeparatorFormat($timeformat)
1231
    {
1232 4
        $date = $this->_convert("00:11:22", self::DB_TIME_FORMAT, null, $timeformat, null);
1233 4
        if (preg_match('/\d+(.+?)11/', $date, $matches)) {
1234 4
            $sep = $matches[1];
1235
        } else {
1236
            $sep = ':';
1237
        }
1238 4
        return $sep;
1239
    }
1240
1241
    /**
1242
     * Returns start and end of a certain local date in GMT
1243
     * Example: for May 19 in PDT start would be 2010-05-19 07:00:00, end would be 2010-05-20 06:59:59
1244
     * @param string|DateTime $date Date in any suitable format
1245
     * @param User $user
1246
     * @return array Start & end date in start, startdate, starttime, end, enddate, endtime
1247
     */
1248
    public function getDayStartEndGMT($date, User $user = null)
1249
    {
1250
        if ($date instanceof DateTime) {
1251
            $min = clone $date;
1252
            $min->setTimezone($this->_getUserTZ($user));
1253
            $max = clone $date;
1254
            $max->setTimezone($this->_getUserTZ($user));
1255
        } else {
1256
            $min = new DateTime($date, $this->_getUserTZ($user));
1257
            $max = new DateTime($date, $this->_getUserTZ($user));
1258
        }
1259
        $min->setTime(0, 0);
1260
        $max->setTime(23, 59, 59);
1261
1262
        $min->setTimezone(self::$gmtTimezone);
1263
        $max->setTimezone(self::$gmtTimezone);
1264
1265
        $result['start'] = $this->asDb($min);
1266
        $result['startdate'] = $this->asDbDate($min);
1267
        $result['starttime'] = $this->asDbTime($min);
1268
        $result['end'] = $this->asDb($max);
1269
        $result['enddate'] = $this->asDbDate($max);
1270
        $result['endtime'] = $this->asDbtime($max);
1271
1272
        return $result;
1273
    }
1274
1275
    /**
1276
     * Expand date format by adding midnight to it
1277
     * Note: date is assumed to be in target format already
1278
     * @param string $date
1279
     * @param string $format Target format
1280
     * @return string
1281
     */
1282 4
    public function expandDate($date, $format)
1283
    {
1284 4
        $formats = $this->split_date_time($format);
1285 4
        if(isset($formats[1])) {
1286 4
            return $this->merge_date_time($date, $this->_get_midnight($formats[1]));
1287
        }
1288
        return $date;
1289
    }
1290
1291
    /**
1292
     * Expand time format by adding today to it
1293
     * Note: time is assumed to be in target format already
1294
     * @param string $date
1295
     * @param string $format Target format
1296
     * @param DateTimeZone $tz
1297
     * @return string
1298
     */
1299
    public function expandTime($date, $format, $tz)
1300
    {
1301
        $formats = $this->split_date_time($format);
1302
        if(isset($formats[1])) {
1303
            $now = clone $this->getNow();
1304
            $now->setTimezone($tz);
1305
            return $this->merge_date_time($now->format($formats[0]), $date);
1306
        }
1307
        return $date;
1308
    }
1309
1310
    /**
1311
	 * Get midnight (start of the day) in local time format
1312
	 *
1313
	 * @return Time string
1314
	 */
1315
	function get_default_midnight()
1316
	{
1317
        return $this->_get_midnight($this->get_time_format());
1318
	}
1319
1320
	/**
1321
	 * Get the name of the timezone for the user
1322
	 * @param User $user User, default - current user
1323
	 * @return string
1324
	 */
1325 8
	public static function userTimezone(User $user = null)
1326
	{
1327 8
	    $user = self::getInstance()->_getUser($user);
1328 8
	    if(empty($user)) {
1329
	        return '';
1330
	    }
1331 8
	    $tz = self::getInstance()->_getUserTZ($user);
1332 8
	    if($tz) {
1333 8
	        return $tz->getName();
1334
	    }
1335
	    return '';
1336
	}
1337
1338
    /**
1339
     * Guess the timezone for the current user
1340
     * @param int $userOffset Offset from GMT in minutes
1341
     * @return string
1342
     */
1343
	public static function guessTimezone($userOffset = 0)
1344
	{
1345
	    if(!is_numeric($userOffset)) {
1346
		    return '';
1347
	    }
1348
	    $defaultZones= array(
1349
	        'America/Anchorage', 'America/Los_Angeles', 'America/Phoenix', 'America/Chicago',
1350
	    	'America/New_York', 'America/Argentina/Buenos_Aires', 'America/Montevideo',
1351
	        'Europe/London', 'Europe/Amsterdam', 'Europe/Athens', 'Europe/Moscow',
1352
	        'Asia/Tbilisi', 'Asia/Omsk', 'Asia/Jakarta', 'Asia/Hong_Kong',
1353
	        'Asia/Tokyo', 'Pacific/Guam', 'Australia/Sydney', 'Australia/Perth',
1354
	    );
1355
1356
	    $now = new DateTime();
1357
	    $tzlist = timezone_identifiers_list();
1358
	    if($userOffset == 0) {
1359
    	     $gmtOffset = date('Z');
1360
	         $nowtz = date('e');
1361
	         if(in_array($nowtz, $tzlist)) {
1362
    	         array_unshift($defaultZones, $nowtz);
1363
	         } else {
1364
	             $nowtz = timezone_name_from_abbr(date('T'), $gmtOffset, date('I'));
1365
	             if(in_array($nowtz, $tzlist)) {
1366
	                 array_unshift($defaultZones, $nowtz);
1367
	             }
1368
	         }
1369
    	} else {
1370
    	    $gmtOffset = $userOffset * 60;
1371
    	}
1372
    	foreach($defaultZones as $zoneName) {
1373
	        $tz = new DateTimeZone($zoneName);
1374
	        if($tz->getOffset($now) == $gmtOffset) {
1375
                return $tz->getName();
1376
	        }
1377
	    }
1378
    	// try all zones
1379
	    foreach($tzlist as $zoneName) {
1380
	        $tz = new DateTimeZone($zoneName);
1381
	        if($tz->getOffset($now) == $gmtOffset) {
1382
                return $tz->getName();
1383
	        }
1384
	    }
1385
	    return null;
1386
	}
1387
1388
    /**
1389
     * Get the description of the user timezone for specific date
1390
     * Like: PST(+08:00)
1391
     * We need the date because it can be DST or non-DST
1392
     * Note it's different from TZ name in tzName() that relates to current date
1393
     * @param DateTime $date Current date
1394
     * @param User $user User, default - current user
1395
     * @return string
1396
     */
1397 5
	public static function userTimezoneSuffix(DateTime $date, User $user = null)
1398
	{
1399 5
	    $user = self::getInstance()->_getUser($user);
1400 5
	    if(empty($user)) {
1401
	        return '';
1402
	    }
1403 5
	    self::getInstance()->tzUser($date, $user);
1404 5
	    return $date->format('T(P)');
1405
	}
1406
1407
	/**
1408
	 * Get display name for a certain timezone
1409
	 * Note: it uses current date for GMT offset, so it may be not suitable for displaying generic dates
1410
	 * @param string|DateTimeZone $name TZ name
1411
	 * @return string
1412
	 */
1413 7
	public static function tzName($name)
1414
	{
1415 7
	    if(empty($name)) {
1416
	        return '';
1417
	    }
1418 7
	    if($name instanceof DateTimeZone) {
1419
	        $tz = $name;
1420
	    } else {
1421 7
            $tz = timezone_open($name);
1422
	    }
1423 7
        if(!$tz) {
1424
            return "???";
1425
        }
1426 7
        $now = new DateTime("now", $tz);
1427 7
        $off = $now->getOffset();
1428 7
        $translated = translate('timezone_dom','',$name);
0 ignored issues
show
It seems like $name defined by parameter $name on line 1413 can also be of type object<DateTimeZone>; however, translate() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
1429 7
        if(is_string($translated) && !empty($translated)) {
1430
            $name = $translated;
1431
        }
1432 7
        return sprintf("%s (GMT%+2d:%02d)%s", str_replace('_',' ', $name), $off/3600, (abs($off)/60)%60, "");//$now->format('I')==1?"(+DST)":"");
1433
	}
1434
1435
1436
    /**
1437
     * Timezone sorting helper
1438
     * Sorts by name
1439
     * @param array $a
1440
     * @param array $b
1441
     * @internal
1442
     * @return int
1443
     */
1444
	public static function _sortTz($a, $b)
1445
	{
1446
	    if($a[0] == $b[0]) {
1447
            return strcmp($a[1], $b[1]);
1448
	    } else {
1449
	        return $a[0]<$b[0]?-1:1;
1450
	    }
1451
	}
1452
1453
	/**
1454
	 * Get list of all timezones in the system
1455
	 * @return array
1456
	 */
1457
	public static function getTimezoneList()
1458
	{
1459
        $now = new DateTime();
1460
        $res_zones = $zones = array();
1461
	    foreach(timezone_identifiers_list() as $zoneName) {
1462
            $tz = new DateTimeZone($zoneName);
1463
	        $zones[$zoneName] = array($tz->getOffset($now), self::tzName($zoneName));
1464
	    }
1465
	    uasort($zones, array('TimeDate', '_sortTz'));
1466
	    foreach($zones as $name => $zonedef) {
1467
	        $res_zones[$name] = $zonedef[1];
1468
	    }
1469
	    return $res_zones;
1470
	}
1471
1472
    /**
1473
     * Print timestamp in RFC2616 format:
1474
     * @param int|null $ts Null means current ts
1475
     * @return string
1476
     */
1477
	public static function httpTime($ts = null)
1478
	{
1479
	    if($ts === null) {
1480
	        $ts = time();
1481
	    }
1482
	    return gmdate(self::RFC2616_FORMAT, $ts);
1483
	}
1484
1485
	/**
1486
	 * Create datetime object from calendar array
1487
	 * @param array $time
1488
	 * @return SugarDateTime
1489
	 */
1490
	public function fromTimeArray($time)
1491
	{
1492
		if (! isset( $time) || count($time) == 0 )
1493
		{
1494
			return $this->nowDb();
1495
		}
1496
		elseif ( isset( $time['ts']))
1497
		{
1498
			return $this->fromTimestamp($time['ts']);
1499
		}
1500
		elseif ( isset( $time['date_str']))
1501
		{
1502
		    return $this->fromDb($time['date_str']);
1503
		}
1504
		else
1505
		{
1506
    		$hour = 0;
1507
    		$min = 0;
1508
    		$sec = 0;
1509
    		$now = $this->getNow(true);
1510
    		$day = $now->day;
1511
    		$month = $now->month;
1512
    		$year = $now->year;
1513
		    if (isset($time['sec']))
1514
			{
1515
        			$sec = $time['sec'];
1516
			}
1517
			if (isset($time['min']))
1518
			{
1519
        			$min = $time['min'];
1520
			}
1521
			if (isset($time['hour']))
1522
			{
1523
        			$hour = $time['hour'];
1524
			}
1525
			if (isset($time['day']))
1526
			{
1527
        			$day = $time['day'];
1528
			}
1529
			if (isset($time['month']))
1530
			{
1531
        			$month = $time['month'];
1532
			}
1533
			if (isset($time['year']) && $time['year'] >= 1970)
1534
			{
1535
        			$year = $time['year'];
1536
			}
1537
			return $now->setDate($year, $month, $day)->setTime($hour, $min, $sec)->setTimeZone(self::$gmtTimezone);
1538
		}
1539
        return null;
0 ignored issues
show
return null; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
1540
	}
1541
1542
	/**
1543
	 * Returns the date portion of a datetime string
1544
	 *
1545
	 * @param string $datetime
1546
	 * @return string
1547
	 */
1548
	public function getDatePart($datetime)
1549
	{
1550
	    list($date, $time) = $this->split_date_time($datetime);
0 ignored issues
show
The assignment to $time is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
1551
	    return $date;
1552
	}
1553
1554
	/**
1555
	 * Returns the time portion of a datetime string
1556
	 *
1557
	 * @param string $datetime
1558
	 * @return string
1559
	 */
1560
	public function getTimePart($datetime)
1561
	{
1562
	    list($date, $time) = $this->split_date_time($datetime);
0 ignored issues
show
The assignment to $date is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
1563
	    return $time;
1564
	}
1565
1566
    /**
1567
     * Returns the offset from user's timezone to GMT
1568
     * @param User $user
1569
     * @param DateTime $time When the offset is taken, default is now
1570
     * @return int Offset in minutes
1571
     */
1572 12
    public function getUserUTCOffset(User $user = null, DateTime $time = null)
1573
    {
1574 12
        if(empty($time)) {
1575 12
            $time = $this->now;
1576
        }
1577 12
        return $this->_getUserTZ($user)->getOffset($time) / 60;
1578
    }
1579
1580
    /**
1581
     * Create regexp from datetime format
1582
     * @param string $format
1583
     * @return string Regular expression string
1584
     */
1585 4
    public static function get_regular_expression($format)
1586
    {
1587 4
        $newFormat = '';
1588 4
        $regPositions = array();
1589 4
        $ignoreNextChar = false;
1590 4
        $count = 1;
1591 4
        foreach (str_split($format) as $char) {
1592 4
            if (! $ignoreNextChar && isset(self::$format_to_regexp[$char])) {
1593 4
                $newFormat .= '(' . self::$format_to_regexp[$char] . ')';
1594 4
                $regPositions[$char] = $count;
1595 4
                $count ++;
1596
            } else {
1597 4
                $ignoreNextChar = false;
1598 4
                $newFormat .= $char;
1599
1600
            }
1601 4
            if ($char == "\\") {
1602 4
                $ignoreNextChar = true;
1603
            }
1604
        }
1605
1606 4
        return array('format' => $newFormat, 'positions' => $regPositions);
1607
    }
1608
1609
    // format - date expression ('' means now) for start and end of the range
1610
    protected $date_expressions = array(
1611
        'yesterday' =>    array("-1 day", "-1 day"),
1612
        'today' =>        array("", ""),
1613
        'tomorrow' =>     array("+1 day", "+1 day"),
1614
        'last_7_days' =>  array("-6 days", ""),
1615
        'next_7_days' =>  array("", "+6 days"),
1616
        'last_30_days' => array("-29 days", ""),
1617
        'next_30_days' => array("", "+29 days"),
1618
    );
1619
1620
    /**
1621
     * Parse date template
1622
     * @internal
1623
     * @param string $template Date expression
1624
     * @param bool $daystart Do we want start or end of the day?
1625
     * @param User $user
1626
     * @param bool $adjustForTimezone
1627
     * @return SugarDateTime
1628
     */
1629
    protected function parseFromTemplate($template, $daystart, User $user = null, $adjustForTimezone = true)
1630
	{
1631
        $rawTime = $this->getNow();
1632
        $now = $adjustForTimezone?$this->tzUser($rawTime, $user):$rawTime;
1633
        if(!empty($template)) {
1634
            $now->modify($template);
1635
        }
1636
        if($daystart) {
1637
            return $now->get_day_begin();
0 ignored issues
show
It seems like you code against a specific sub-type and not the parent class DateTime as the method get_day_begin() does only exist in the following sub-classes of DateTime: SugarDateTime. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
1638
        } else {
1639
            return $now->get_day_end();
0 ignored issues
show
It seems like you code against a specific sub-type and not the parent class DateTime as the method get_day_end() does only exist in the following sub-classes of DateTime: SugarDateTime. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
1640
        }
1641
	}
1642
1643
    /**
1644
     * Get month-long range mdiff months from now
1645
     * @internal
1646
     * @param int $mdiff
1647
     * @param User $user
1648
     * @param bool $adjustForTimezone
1649
     * @return array
1650
     */
1651
	protected function diffMon($mdiff, User $user = null, $adjustForTimezone = true)
1652
	{
1653
        $rawTime = $this->getNow();
1654
        $now = $adjustForTimezone?$this->tzUser($rawTime, $user):$rawTime;
1655
	    $now->setDate($now->year, $now->month+$mdiff, 1);
1656
	    $start = $now->get_day_begin();
0 ignored issues
show
It seems like you code against a specific sub-type and not the parent class DateTime as the method get_day_begin() does only exist in the following sub-classes of DateTime: SugarDateTime. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
1657
	    $end = $now->setDate($now->year, $now->month, $now->days_in_month)->setTime(23, 59, 59);
1658
	    return array($start, $end);
1659
	}
1660
1661
    /**
1662
     * Get year-long range ydiff years from now
1663
     * @internal
1664
     * @param int $ydiff
1665
     * @param User $user
1666
     * @param bool $adjustForTimezone
1667
     * @return array
1668
     */
1669
	protected function diffYear($ydiff, User $user = null, $adjustForTimezone = true)
1670
	{
1671
        $rawTime = $this->getNow();
1672
        $now = $adjustForTimezone?$this->tzUser($rawTime, $user):$rawTime;
1673
        $now->setDate($now->year+$ydiff, 1, 1);
1674
	    $start = $now->get_day_begin();
0 ignored issues
show
It seems like you code against a specific sub-type and not the parent class DateTime as the method get_day_begin() does only exist in the following sub-classes of DateTime: SugarDateTime. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
1675
	    $end = $now->setDate($now->year, 12, 31)->setTime(23, 59, 59);
1676
	    return array($start, $end);
1677
	}
1678
1679
	/**
1680
	 * Parse date range expression
1681
	 * Returns beginning and end of the range as a date
1682
	 * @param string $range
1683
	 * @param User $user
1684
     * @param bool $adjustForTimezone Do we need to adjust for timezone?
1685
	 * @return array of two Date objects, start & end
1686
	 */
1687
	public function parseDateRange($range, User $user = null, $adjustForTimezone = true)
1688
	{
1689
        if(isset($this->date_expressions[$range])) {
1690
            return array($this->parseFromTemplate($this->date_expressions[$range][0], true, $user, $adjustForTimezone),
1691
                $this->parseFromTemplate($this->date_expressions[$range][1], false, $user, $adjustForTimezone)
1692
            );
1693
        }
1694
	    switch($range) {
1695
			case 'next_month':
1696
			    return $this->diffMon(1,  $user, $adjustForTimezone);
1697
		    case 'last_month':
1698
			    return $this->diffMon(-1,  $user, $adjustForTimezone);
1699
		    case 'this_month':
1700
			    return $this->diffMon(0,  $user, $adjustForTimezone);
1701
	        case 'last_year':
1702
			    return $this->diffYear(-1,  $user, $adjustForTimezone);
1703
	        case 'this_year':
1704
			    return $this->diffYear(0,  $user, $adjustForTimezone);
1705
	        case 'next_year':
1706
			    return $this->diffYear(1,  $user, $adjustForTimezone);
1707
	        default:
1708
			    return null;
1709
	    }
1710
	}
1711
1712
    /********************* OLD functions, should not be used publicly anymore ****************/
1713
    /**
1714
     * Merge time without am/pm with am/pm string
1715
     * @TODO find better way to do this!
1716
     * @deprecated for public use
1717
     * @param string $date
1718
     * @param string $format User time format
1719
     * @param string $mer
1720
     * @return string
1721
     */
1722
    function merge_time_meridiem($date, $format, $mer)
1723
    {
1724
        $date = trim($date);
1725
        if (empty($date)) {
1726
            return $date;
1727
        }
1728
        $fakeMerFormat = str_replace(array('a', 'A'), array('@~@', '@~@'), $format);
1729
        $noMerFormat = trim(str_replace(array('a', 'A'), array('', ''), $format));
1730
        $newDate = $this->swap_formats($date, $noMerFormat, $fakeMerFormat);
0 ignored issues
show
Deprecated Code introduced by
The method TimeDate::swap_formats() has been deprecated with message: for public use
Convert date from one format to another

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
1731
        return str_replace('@~@', $mer, $newDate);
1732
    }
1733
1734
    /**
1735
     * @deprecated for public use
1736
     * Convert date from one format to another
1737
     *
1738
     * @param string $date
1739
     * @param string $from
1740
     * @param string $to
1741
     * @return string
1742
     */
1743
    public function swap_formats($date, $from, $to)
1744
    {
1745
        return $this->_convert($date, $from, self::$gmtTimezone, $to, self::$gmtTimezone);
1746
    }
1747
1748
    /**
1749
     * @deprecated for public use
1750
     * handles offset values for Timezones and DST
1751
     * @param	$date	     string		date/time formatted in user's selected format
1752
     * @param	$format	     string		destination format value as passed to PHP's date() funtion
1753
     * @param	$to		     boolean
1754
     * @param	$user	     object		user object from which Timezone and DST
1755
     * @param	$usetimezone string		timezone name
1756
     * values will be derived
1757
     * @return 	 string		date formatted and adjusted for TZ and DST
1758
     */
1759 2
    function handle_offset($date, $format, $to = true, $user = null, $usetimezone = null)
1760
    {
1761 2
        $tz = empty($usetimezone)?$this->_getUserTZ($user):new DateTimeZone($usetimezone);
1762 2
        $dateobj = new SugarDateTime($date, $to? self::$gmtTimezone : $tz);
1763 2
        $dateobj->setTimezone($to ? $tz: self::$gmtTimezone);
1764 2
        return $dateobj->format($format);
1765
//        return $this->_convert($date, $format, $to ? self::$gmtTimezone : $tz, $format, $to ? $tz : self::$gmtTimezone);
1766
    }
1767
1768
    /**
1769
     * @deprecated for public use
1770
     * Get current GMT datetime in DB format
1771
     * @return string
1772
     */
1773
    function get_gmt_db_datetime()
1774
    {
1775
        return $this->nowDb();
1776
    }
1777
1778
    /**
1779
     * @deprecated for public use
1780
     * Get current GMT date in DB format
1781
     * @return string
1782
     */
1783
    function get_gmt_db_date()
1784
    {
1785
        return $this->nowDbDate();
1786
    }
1787
1788
    /**
1789
     * @deprecated for public use
1790
     * this method will take an input $date variable (expecting Y-m-d format)
1791
     * and get the GMT equivalent - with an hour-level granularity :
1792
     * return the max value of a given locale's
1793
     * date+time in GMT metrics (i.e., if in PDT, "2005-01-01 23:59:59" would be
1794
     * "2005-01-02 06:59:59" in GMT metrics)
1795
     * @param $date
1796
     * @return array
1797
     */
1798
    function handleOffsetMax($date)
1799
    {
1800
        $min = new DateTime($date, $this->_getUserTZ());
1801
        $min->setTime(0, 0);
1802
        $max = new DateTime($date, $this->_getUserTZ());
1803
        $max->setTime(23, 59, 59);
1804
1805
        $min->setTimezone(self::$gmtTimezone);
1806
        $max->setTimezone(self::$gmtTimezone);
1807
1808
        $gmtDateTime['date'] = $this->asDbDate($max, false);
1809
        $gmtDateTime['time'] = $this->asDbDate($max, false);
1810
        $gmtDateTime['min'] = $this->asDb($min);
1811
        $gmtDateTime['max'] = $this->asDb($max);
1812
1813
        return $gmtDateTime;
1814
    }
1815
1816
    /**
1817
     * @deprecated for public use
1818
     * this returns the adjustment for a user against the server time
1819
     *
1820
     * @return integer number of minutes to adjust a time by to get the appropriate time for the user
1821
     */
1822
    public function adjustmentForUserTimeZone()
1823
    {
1824
        $tz = $this->_getUserTZ();
1825
        $server_tz = new DateTimeZone(date_default_timezone_get());
1826
        if ($tz && $server_tz) {
1827
            return ($server_tz->getOffset($this->now) - $tz->getOffset($this->now)) / 60;
1828
        }
1829
        return 0;
1830
    }
1831
1832
    /**
1833
     * @deprecated for public use
1834
     * assumes that olddatetime is in Y-m-d H:i:s format
1835
     * @param $olddatetime
1836
     * @return string
1837
     */
1838
    function convert_to_gmt_datetime($olddatetime)
1839
    {
1840
        if (! empty($olddatetime)) {
1841
            return date('Y-m-d H:i:s', strtotime($olddatetime) - date('Z'));
1842
        }
1843
        return '';
1844
    }
1845
1846
    /**
1847
     * @deprecated for public use
1848
     * get user timezone info
1849
     * @param User $user
1850
     * @return array
1851
     */
1852
    public function getUserTimeZone(User $user = null)
1853
    {
1854
        $tz = $this->_getUserTZ($user);
1855
        return array("gmtOffset" => $tz->getOffset($this->now) / 60);
1856
    }
1857
1858
    /**
1859
     * @deprecated for public use
1860
     * get timezone start & end
1861
     * @param $year
1862
     * @param string $zone
1863
     * @return array
1864
     */
1865
    public function getDSTRange($year, $zone = null)
1866
    {
1867
    	if(!empty($zone)) {
1868
    		$tz = timezone_open($zone);
1869
    	}
1870
    	if(empty($tz)) {
1871
    		$tz = $this->_getUserTZ();
1872
    	}
1873
1874
        $year_date = SugarDateTime::createFromFormat("Y", $year, self::$gmtTimezone);
1875
        $year_end = clone $year_date;
1876
        $year_end->setDate((int) $year, 12, 31);
1877
        $year_end->setTime(23, 59, 59);
1878
        $year_date->setDate((int) $year, 1, 1);
1879
        $year_date->setTime(0, 0, 0);
1880
		$result = array();
1881
        $transitions = $tz->getTransitions($year_date->ts, $year_end->ts);
1882
        $idx = 0;
1883
        if(version_compare(PHP_VERSION, '5.3.0', '<')) {
1884
        	// <5.3.0 ignores parameters, advance manually to current year
1885
        	$start_ts = $year_date->ts;
1886
        	while(isset($transitions[$idx]) && $transitions[$idx]["ts"] < $start_ts) $idx++;
1887
        }
1888
        // get DST start
1889
        while (isset($transitions[$idx]) && !$transitions[$idx]["isdst"]) $idx++;
1890
        if(isset($transitions[$idx])) {
1891
        	$result["start"] = $this->fromTimestamp($transitions[$idx]["ts"])->asDb();
1892
        }
1893
        // get DST end
1894
        while (isset($transitions[$idx]) && $transitions[$idx]["isdst"]) $idx++;
1895
        if(isset($transitions[$idx])) {
1896
        	$result["end"] = $this->fromTimestamp($transitions[$idx]["ts"])->asDb();
1897
        }
1898
        return $result;
1899
    }
1900
1901
/****************** GUI stuff that really shouldn't be here, will be moved ************/
1902
    /**
1903
     * Get Javascript variables setup for user date format validation
1904
     * @deprecated moved to SugarView
1905
     * @return string JS code
1906
     */
1907
    function get_javascript_validation()
1908
    {
1909
        return SugarView::getJavascriptValidation();
1910
    }
1911
1912
    /**
1913
     * AMPMMenu
1914
     * This method renders a SELECT HTML form element based on the
1915
     * user's time format preferences, with give date's value highlighted.
1916
     *
1917
     * If user's prefs have no AM/PM string, returns empty string.
1918
     *
1919
     * @todo There is hardcoded HTML in here that does not allow for localization
1920
     * of the AM/PM am/pm Strings in this drop down menu.  Also, perhaps
1921
     * change to the substr_count function calls to strpos
1922
     * TODO: Remove after full switch to fields
1923
     * @deprecated
1924
     * @param string $prefix Prefix for SELECT
1925
     * @param string $date Date in display format
1926
     * @param string $attrs Additional attributes for SELECT
1927
     * @return string SELECT HTML
1928
     */
1929 4
    function AMPMMenu($prefix, $date, $attrs = '')
1930
    {
1931 4
        $tf = $this->get_time_format();
1932 4
        $am = strpbrk($tf, 'aA');
1933 4
        if ($am == false) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $am of type string to the boolean false. If you are specifically checking for an empty string, consider using the more explicit === '' instead.
Loading history...
1934 4
            return '';
1935
        }
1936
        $selected = array("am" => "", "pm" => "", "AM" => "", "PM" => "");
1937
        if (preg_match('/([ap]m)/i', $date, $match)) {
1938
            $selected[$match[1]] = " selected";
1939
        }
1940
1941
        $menu = "<select name='" . $prefix . "meridiem' " . $attrs . ">";
1942
        if ($am{0} == 'a') {
1943
            $menu .= "<option value='am'{$selected["am"]}>am";
1944
            $menu .= "<option value='pm'{$selected["pm"]}>pm";
1945
        } else {
1946
            $menu .= "<option value='AM'{$selected["AM"]}>AM";
1947
            $menu .= "<option value='PM'{$selected["PM"]}>PM";
1948
        }
1949
1950
        return $menu . '</select>';
1951
    }
1952
1953
    /**
1954
     * Get user format in JS form
1955
     * TODO: Remove after full switch to fields
1956
     * @return string
1957
     */
1958 9
    function get_user_date_format()
1959
    {
1960 9
        return str_replace(array('Y', 'm', 'd'), array('yyyy', 'mm', 'dd'), $this->get_date_format());
1961
    }
1962
1963
    /**
1964
     * Get user time format example
1965
     * TODO: Remove after full switch to fields
1966
     * @deprecated
1967
     * @return string
1968
     */
1969 9
    function get_user_time_format()
1970
    {
1971 9
        global $sugar_config;
1972 9
        $time_pref = $this->get_time_format();
1973
1974 9
        if (! empty($time_pref) && ! empty($sugar_config['time_formats'][$time_pref])) {
1975 9
            return $sugar_config['time_formats'][$time_pref];
1976
        }
1977
1978
        return '23:00'; //default
1979
    }
1980
1981
}
1982