]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - include/TimeDate.php
Release 6.2.4
[Github/sugarcrm.git] / include / TimeDate.php
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-2011 SugarCRM Inc.
6  * 
7  * This program is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU Affero General Public License version 3 as published by the
9  * Free Software Foundation with the addition of the following permission added
10  * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
11  * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
12  * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
13  * 
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16  * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
17  * details.
18  * 
19  * You should have received a copy of the GNU Affero General Public License along with
20  * this program; if not, see http://www.gnu.org/licenses or write to the Free
21  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22  * 02110-1301 USA.
23  * 
24  * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
25  * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
26  * 
27  * The interactive user interfaces in modified source and object code versions
28  * of this program must display Appropriate Legal Notices, as required under
29  * Section 5 of the GNU Affero General Public License version 3.
30  * 
31  * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
32  * these Appropriate Legal Notices must retain the display of the "Powered by
33  * SugarCRM" logo. If the display of the logo is not reasonably feasible for
34  * technical reasons, the Appropriate Legal Notices must display the words
35  * "Powered by SugarCRM".
36  ********************************************************************************/
37
38 require_once 'include/SugarDateTime.php';
39
40 /**
41   *
42   * New Time & Date handling class
43   * Migration notes:
44   * - to_db_time() requires either full datetime or time, won't work with just date
45   *     The reason is that it's not possible to know if short string has only date or only time,
46   *     and it makes more sense to assume time for the time conversion function.
47   */
48 class TimeDate
49 {
50         const DB_DATE_FORMAT = 'Y-m-d';
51         const DB_TIME_FORMAT = 'H:i:s';
52     // little optimization
53         const DB_DATETIME_FORMAT = 'Y-m-d H:i:s';
54         const RFC2616_FORMAT = 'D, d M Y H:i:s \G\M\T';
55
56     const SECONDS_IN_A_DAY = 86400;
57
58     // Standard DB date/time formats
59     // they are constant, vars are for compatibility
60         public $dbDayFormat = self::DB_DATE_FORMAT;
61     public $dbTimeFormat = self::DB_TIME_FORMAT;
62
63     /**
64      * Regexp for matching format elements
65      * @var array
66      */
67     protected static $format_to_regexp = array(
68         'a' => '[ ]*[ap]m',
69         'A' => '[ ]*[AP]M',
70         'd' => '[0-9]{1,2}',
71         'j' => '[0-9]{1,2}',
72         'h' => '[0-9]{1,2}',
73         'H' => '[0-9]{1,2}',
74         'g' => '[0-9]{1,2}',
75         'G' => '[0-9]{1,2}',
76                 'i' => '[0-9]{1,2}',
77         'm' => '[0-9]{1,2}',
78         'n' => '[0-9]{1,2}',
79         'Y' => '[0-9]{4}',
80         's' => '[0-9]{1,2}',
81         'F' => '\w+',
82         "M" => '[\w]{1,3}',
83     );
84
85     /**
86      * Relation between date() and strftime() formats
87      * @var array
88      */
89     public static $format_to_str = array(
90                 // date
91         'Y' => '%Y',
92
93         'm' => '%m',
94         'M' => '%b',
95         'F' => '%B',
96             'n' => '%m',
97
98         'd' => '%d',
99         //'j' => '%e',
100         // time
101         'a' => '%P',
102         'A' => '%p',
103
104         'h' => '%I',
105         'H' => '%H',
106         //'g' => '%l',
107         //'G' => '%H',
108
109         'i' => '%M',
110         's' => '%S',
111     );
112
113     /**
114      * GMT timezone object
115      *
116      * @var DateTimeZone
117      */
118     protected static $gmtTimezone;
119
120     /**
121      * Current time
122      * @var SugarDateTime
123      */
124     protected $now;
125
126     /**
127      * The current user
128      *
129      * @var User
130      */
131     protected $user;
132
133     /**
134      * Current user's ID
135      *
136      * @var string
137      */
138     protected $current_user_id;
139     /**
140      * Current user's TZ
141      * @var DateTimeZone
142      */
143     protected $current_user_tz;
144
145     /**
146      * Separator for current user time format
147      *
148      * @var string
149      */
150     protected $time_separator;
151
152     /**
153      * Always consider user TZ to be GMT and date format DB format - for SOAP etc.
154      *
155      * @var bool
156      */
157     protected $always_db = false;
158
159     /**
160      * Global instance of TimeDate
161      * @var TimeDate
162      */
163     protected static $timedate;
164
165     public $allow_cache = true;
166
167     public function __construct(User $user = null)
168     {
169         if (self::$gmtTimezone == null) {
170             self::$gmtTimezone = new DateTimeZone("UTC");
171         }
172         $this->now = new SugarDateTime();
173         $this->tzGMT($this->now);
174         $this->user = $user;
175     }
176
177     /**
178      * Set flag specifying we should always use DB format
179      * @param bool $flag
180      * @return TimeDate
181      */
182     public function setAlwaysDb($flag = true)
183     {
184         $this->always_db = $flag;
185         $this->clearCache();
186         return $this;
187     }
188
189     /**
190      * Get "always use DB format" flag
191      * @return bool
192      */
193     public function isAlwaysDb()
194     {
195         return !empty($GLOBALS['disable_date_format']) || $this->always_db;
196     }
197
198     /**
199      * Get TimeDate instance
200      * @return TimeDate
201      */
202     public static function getInstance()
203     {
204         if(empty(self::$timedate)) {
205             if(ini_get('date.timezone') == '') {
206                 // Remove warning about default timezone
207                 date_default_timezone_set(@date('e'));
208                 try {
209                     $tz = self::guessTimezone();
210                 } catch(Exception $e) {
211                     $tz = "UTC"; // guess failed, switch to UTC
212                 }
213                 if(isset($GLOBALS['log'])) {
214                     $GLOBALS['log']->fatal("Configuration variable date.timezone is not set, guessed timezone $tz. Please set date.timezone=\"$tz\" in php.ini!");
215                 }
216                 date_default_timezone_set($tz);
217             }
218             self::$timedate = new self;
219         }
220         return self::$timedate;
221     }
222
223     /**
224      * Set current user for this object
225      *
226      * @param $user
227      * @return TimeDate
228      */
229     public function setUser(User $user = null)
230     {
231         $this->user = $user;
232         $this->clearCache();
233         return $this;
234     }
235
236      /**
237      * Figure out what the required user is
238      *
239      * The order is: supplied parameter, TimeDate's user, global current user
240      *
241      * @param User $user
242      * @return User
243      */
244     protected function _getUser(User $user = null)
245     {
246         if (empty($user)) {
247             $user = $this->user;
248         }
249         if (empty($user)) {
250             $user = $GLOBALS['current_user'];
251         }
252         return $user;
253     }
254
255     /**
256      * Get timezone for the specified user
257      *
258      * @param User $user
259      * @return DateTimeZone
260      */
261     protected function _getUserTZ(User $user = null)
262     {
263         $user = $this->_getUser($user);
264
265         if (empty($user) || $this->isAlwaysDb()) {
266             return self::$gmtTimezone;
267         }
268
269         if ($this->allow_cache && $user->id == $this->current_user_id && ! empty($this->current_user_tz)) {
270             // current user is cached
271             return $this->current_user_tz;
272         }
273
274         try {
275             $usertimezone = $user->getPreference('timezone');
276             if(empty($usertimezone)) {
277                 return self::$gmtTimezone;
278             }
279             $tz = new DateTimeZone($usertimezone);
280         } catch (Exception $e) {
281             $GLOBALS['log']->fatal('Unknown timezone: ' . $usertimezone);
282             return self::$gmtTimezone;
283         }
284
285         if (empty($this->current_user_id)) {
286             $this->current_user_id = $user->id;
287             $this->current_user_tz = $tz;
288         }
289
290         return $tz;
291     }
292
293     /**
294      * Clears all cached data regarding current user
295      */
296     public function clearCache()
297     {
298         $this->current_user_id = null;
299         $this->current_user_tz = null;
300         $this->time_separator = null;
301         $this->now = new SugarDateTime();
302     }
303
304     /**
305      * Get user date format.
306      * @todo add caching
307      *
308      * @param [User] $user user object, current user if not specified
309      * @return string
310      */
311     public function get_date_format(User $user = null)
312     {
313         $user = $this->_getUser($user);
314
315         if (empty($user) || $this->isAlwaysDb()) {
316             return self::DB_DATE_FORMAT;
317         }
318
319         $datef = $user->getPreference('datef');
320         if(empty($datef) && isset($GLOBALS['current_user']) && $GLOBALS['current_user'] !== $user) {
321             // if we got another user and it has no date format, try current user
322             $datef = $GLOBALS['current_user']->getPreference('datef');
323         }
324         if (empty($datef)) {
325             $datef = $GLOBALS['sugar_config']['default_date_format'];
326         }
327         if (empty($datef)) {
328             $datef = '';
329         }
330
331         return $datef;
332     }
333
334     /**
335      * Get user time format.
336      * @todo add caching
337      *
338      * @param User $user user object, current user if not specified
339      * @return string
340      */
341     public function get_time_format(/*User*/ $user = null)
342     {
343         if(is_bool($user) || func_num_args() > 1) {
344             // BC dance - old signature was boolean, User
345             $GLOBALS['log']->fatal('TimeDate::get_time_format(): Deprecated API used, please update you code - get_time_format() now has one argument of type User');
346             if(func_num_args() > 1) {
347                 $user = func_get_arg(1);
348             } else {
349                 $user = null;
350             }
351         }
352         $user = $this->_getUser($user);
353
354         if (empty($user) || $this->isAlwaysDb()) {
355             return self::DB_TIME_FORMAT;
356         }
357
358         $timef = $user->getPreference('timef');
359         if(empty($timef) && isset($GLOBALS['current_user']) && $GLOBALS['current_user'] !== $user) {
360             // if we got another user and it has no time format, try current user
361             $timef = $GLOBALS['current_user']->getPreference('$timef');
362         }
363         if (empty($timef)) {
364             $timef = $GLOBALS['sugar_config']['default_time_format'];
365         }
366         if (empty($timef)) {
367             $timef = '';
368         }
369         return $timef;
370     }
371
372     /**
373      * Get user datetime format.
374      * @todo add caching
375      *
376      * @param [User] $user user object, current user if not specified
377      * @return string
378      */
379     public function get_date_time_format($user = null)
380     {
381         // BC fix - had (bool, user) signature before
382         if(!($user instanceof User)) {
383             if(func_num_args() > 1) {
384                 $user = func_get_arg(1);
385                 if(!($user instanceof User)) {
386                     $user = null;
387                 }
388             } else {
389                 $user = null;
390             }
391         }
392         return $this->merge_date_time($this->get_date_format($user), $this->get_time_format($user));
393     }
394
395     /**
396      * Make one datetime string from date string and time string
397      *
398      * @param string $date
399      * @param string $time
400      * @return string New datetime string
401      */
402     function merge_date_time($date, $time)
403     {
404         return $date . ' ' . $time;
405     }
406
407     /**
408      * Split datetime string into date & time
409      *
410      * @param string $datetime
411      * @return array
412      */
413     function split_date_time($datetime)
414     {
415         return explode(' ', $datetime);
416     }
417
418     function get_cal_date_format()
419     {
420         return str_replace(array_keys(self::$format_to_str), array_values(self::$format_to_str), $this->get_date_format());
421     }
422
423     function get_cal_time_format()
424     {
425         return str_replace(array_keys(self::$format_to_str), array_values(self::$format_to_str), $this->get_time_format());
426     }
427
428     function get_cal_date_time_format()
429     {
430         return str_replace(array_keys(self::$format_to_str), array_values(self::$format_to_str), $this->get_date_time_format());
431     }
432
433     /**
434      * Verify if the date string conforms to a format
435      *
436      * @param string $date
437      * @param string $format Format to check
438      * @param string $toformat
439      * @return bool Is the date ok?
440      */
441     public function check_matching_format($date, $format)
442     {
443         try {
444             $dt = SugarDateTime::createFromFormat($format, $date);
445             if (!is_object($dt)) {
446                 return false;
447             }
448         } catch (Exception $e) {
449             return false;
450         }
451         return true;
452     }
453
454     /**
455      * Format DateTime object as DB datetime
456      *
457      * @param DateTime $date
458      * @return string
459      */
460     public function asDb(DateTime $date)
461     {
462         $date->setTimezone(self::$gmtTimezone);
463         return $date->format($this->get_db_date_time_format());
464     }
465
466     /**
467      * Format date as DB-formatted field type
468      * @param DateTime $date
469      * @param string $type Field type - date, time, datetime[combo]
470      */
471     public function asDbType(DateTime $date, $type)
472     {
473         switch($type) {
474             case "date":
475                 return $this->asDbDate($date);
476                 break;
477             case 'time':
478                 return $this->asDbtime($date);
479                 break;
480             case 'datetime':
481             case 'datetimecombo':
482             default:
483                 return $this->asDb($date);
484         }
485     }
486
487     /**
488      * Format DateTime object as user datetime
489      *
490      * @param DateTime $date
491      * @return string
492      */
493     public function asUser(DateTime $date, User $user = null)
494     {
495         $this->tzUser($date, $user);
496         return $date->format($this->get_date_time_format($user));
497     }
498
499     /**
500      * Format date as user-formatted field type
501      * @param DateTime $date
502      * @param string $type Field type - date, time, datetime[combo]
503      */
504     public function asUserType(DateTime $date, $type, User $user = null)
505     {
506         switch($type) {
507             case "date":
508                 return $this->asUserDate($date, true, $user);
509                 break;
510             case 'time':
511                 return $this->asUserTime($date, true, $user);
512                 break;
513             case 'datetime':
514             case 'datetimecombo':
515             default:
516                 return $this->asUser($date, $user);
517         }
518     }
519
520     /**
521      * Produce timestamp offset by user's timezone
522      *
523      * So if somebody converts it to format assuming GMT, it would actually display user's time.
524      * This is used by Javascript.
525      *
526      * @param DateTime $date
527      * @return int
528      */
529     public function asUserTs(DateTime $date, User $user = null)
530     {
531         return $date->format('U')+$this->_getUserTZ($user)->getOffset($date);
532     }
533
534     /**
535      * Format DateTime object as DB date
536      * Note: by default does not convert TZ!
537      * @param DateTime $date
538      * @param boolean $tz Perform TZ conversion?
539      * @return string
540      */
541     public function asDbDate(DateTime $date, $tz = false)
542     {
543         if($tz) $date->setTimezone(self::$gmtTimezone);
544         return $date->format($this->get_db_date_format());
545     }
546
547     /**
548      * Format DateTime object as user date
549      * Note: by default does not convert TZ!
550      * @param DateTime $date
551      * @param boolean $tz Perform TZ conversion?
552      * @return string
553      */
554     public function asUserDate(DateTime $date, $tz = false, User $user = null)
555     {
556         if($tz) $this->tzUser($date, $user);
557         return $date->format($this->get_date_format($user));
558     }
559
560     /**
561      * Format DateTime object as DB time
562      *
563      * @param DateTime $date
564      * @return string
565      */
566     public function asDbTime(DateTime $date)
567     {
568         $date->setTimezone(self::$gmtTimezone);
569         return $date->format($this->get_db_time_format());
570     }
571
572     /**
573      * Format DateTime object as user time
574      *
575      * @param DateTime $date
576      * @return string
577      */
578     public function asUserTime(DateTime $date, User $user = null)
579     {
580         $this->tzUser($date, $user);
581         return $date->format($this->get_time_format($user));
582     }
583
584     /**
585      * Get DateTime from DB datetime string
586      *
587      * @param string $date
588      * @return SugarDateTime
589      */
590     public function fromDb($date)
591     {
592         try {
593             return SugarDateTime::createFromFormat(self::DB_DATETIME_FORMAT, $date, self::$gmtTimezone);
594         } catch (Exception $e) {
595             $GLOBALS['log']->error("fromDb: Conversion of $date from DB format failed: {$e->getMessage()}");
596             return null;
597         }
598     }
599
600     /**
601      * Create a date from a certain type of field in DB format
602      * The types are: date, time, datatime[combo]
603      * @param string $date the datetime string
604      * @param string $type string type
605      * @return SugarDateTime
606      */
607     public function fromDbType($date, $type)
608     {
609         switch($type) {
610             case "date":
611                 return $this->fromDbDate($date);
612                 break;
613             case 'time':
614                 return $this->fromDbFormat($date, self::DB_TIME_FORMAT);
615                 break;
616             case 'datetime':
617             case 'datetimecombo':
618             default:
619                 return $this->fromDb($date);
620         }
621     }
622
623     /**
624      * Get DateTime from DB date string
625      *
626      * @param string $date
627      * @return SugarDateTime
628      */
629     public function fromDbDate($date)
630     {
631         try {
632             return SugarDateTime::createFromFormat(self::DB_DATE_FORMAT, $date, self::$gmtTimezone);
633         } catch (Exception $e) {
634             $GLOBALS['log']->error("fromDbDate: Conversion of $date from DB format failed: {$e->getMessage()}");
635             return null;
636         }
637     }
638
639     /**
640      * Get DateTime from DB datetime string using non-standard format
641      *
642      * Non-standard format usually would be only date, only time, etc.
643      *
644      * @param string $date
645      * @param string $format format to accept
646      * @return SugarDateTime
647      */
648     public function fromDbFormat($date, $format)
649     {
650         try {
651             return SugarDateTime::createFromFormat($format, $date, self::$gmtTimezone);
652         } catch (Exception $e) {
653             $GLOBALS['log']->error("fromDbFormat: Conversion of $date from DB format $format failed: {$e->getMessage()}");
654             return null;
655         }
656     }
657
658     /**
659      * Get DateTime from user datetime string
660      *
661      * @param string $date
662      * @return SugarDateTime
663      */
664     public function fromUser($date, User $user = null)
665     {
666         try {
667             return SugarDateTime::createFromFormat($this->get_date_time_format($user), $date, $this->_getUserTZ($user));
668         } catch (Exception $e) {
669             $uf = $this->get_date_time_format($user);
670             $GLOBALS['log']->error("fromUser: Conversion of $date from user format $uf failed: {$e->getMessage()}");
671             return null;
672         }
673     }
674
675     /**
676      * Create a date from a certain type of field in user format
677      * The types are: date, time, datatime[combo]
678      * @param string $date the datetime string
679      * @param string $type string type
680      * @param User $user
681      * @return SugarDateTime
682      */
683     public function fromUserType($date, $type, $user = null)
684     {
685         switch($type) {
686             case "date":
687                 return $this->fromUserDate($date, $user);
688                 break;
689             case 'time':
690                 return $this->fromUserTime($date, $user);
691                 break;
692             case 'datetime':
693             case 'datetimecombo':
694             default:
695                 return $this->fromUser($date, $user);
696         }
697     }
698
699     /**
700      * Get DateTime from user time string
701      *
702      * @param string $date
703      * @return SugarDateTime
704      */
705     public function fromUserTime($date, User $user = null)
706     {
707         try {
708             return SugarDateTime::createFromFormat($this->get_time_format($user), $date, $this->_getUserTZ($user));
709         } catch (Exception $e) {
710             $uf = $this->get_time_format($user);
711             $GLOBALS['log']->error("fromUserTime: Conversion of $date from user format $uf failed: {$e->getMessage()}");
712             return null;
713         }
714     }
715
716     /**
717      * Get DateTime from user date string
718          * Usually for calendar-related functions like holidays
719      * Note: by default does not convert tz!
720      * @param string $date
721      * @param bool $convert_tz perform TZ converson?
722      * @param User $user
723      * @return SugarDateTime
724      */
725     public function fromUserDate($date, $convert_tz = false, User $user = null)
726     {
727         try {
728             return SugarDateTime::createFromFormat($this->get_date_format($user), $date, $convert_tz?$this->_getUserTZ($user):self::$gmtTimezone);
729         } catch (Exception $e) {
730             $uf = $this->get_date_format($user);
731             $GLOBALS['log']->error("fromUserDate: Conversion of $date from user format $uf failed: {$e->getMessage()}");
732             return null;
733         }
734     }
735
736     /**
737      * Create a date object from any string
738      *
739      * Same formats accepted as for DateTime ctor
740      *
741      * @param string $date
742      * @param User $user
743      * @return SugarDateTime
744      */
745     public function fromString($date, User $user = null)
746     {
747         try {
748             return new SugarDateTime($date, $this->_getUserTZ($user));
749         } catch (Exception $e) {
750             $GLOBALS['log']->error("fromString: Conversion of $date from string failed: {$e->getMessage()}");
751             return null;
752         }
753     }
754
755     /**
756      * Create DateTime from timestamp
757      *
758      * @param interger|string $ts
759      * @return SugarDateTime
760      */
761     public function fromTimestamp($ts)
762     {
763         return new SugarDateTime("@$ts");
764     }
765
766     /**
767      * Convert DateTime to GMT timezone
768      * @param DateTime $date
769      * @return DateTime
770      */
771     public function tzGMT(DateTime $date)
772     {
773         return $date->setTimezone(self::$gmtTimezone);
774     }
775
776     /**
777      * Convert DateTime to user timezone
778      * @param DateTime $date
779      * @param [User] $user
780      * @return DateTime
781      */
782     public function tzUser(DateTime $date, User $user = null)
783     {
784         return $date->setTimezone($this->_getUserTZ($user));
785     }
786
787     /**
788      * Get string defining midnight in current user's format
789      * @param string $format Time format to use
790      * @return string
791      */
792     protected function _get_midnight($format = null)
793     {
794         $zero = new DateTime("@0", self::$gmtTimezone);
795         return $zero->format($format?$format:$this->get_time_format());
796     }
797
798     /**
799      *
800      * Basic conversion function
801      *
802      * @param string $date
803      * @param string $fromFormat
804      * @param DateTimeZone $fromTZ
805      * @param string $toFormat
806      * @param DateTimeZone $toTZ
807      * @param bool $expand
808      */
809     protected function _convert($date, $fromFormat, $fromTZ, $toFormat, $toTZ, $expand = false)
810     {
811         $date = trim($date);
812         if (empty($date)) {
813             return $date;
814         }
815         try {
816             if ($expand && strlen($date) <= 10) {
817                 $date = $this->expandDate($date, $fromFormat);
818             }
819             $phpdate = SugarDateTime::createFromFormat($fromFormat, $date, $fromTZ);
820             if ($phpdate == false) {
821                 //                      var_dump($date, $phpdate, $fromFormat,  DateTime::getLastErrors() );
822                 $GLOBALS['log']->error("convert: Conversion of $date from $fromFormat to $toFormat failed");
823                 return '';
824             }
825             if ($fromTZ !== $toTZ) {
826                 $phpdate->setTimeZone($toTZ);
827             }
828             return $phpdate->format($toFormat);
829         } catch (Exception $e) {
830             //          var_dump($date, $phpdate, $fromFormat, $fromTZ,  DateTime::getLastErrors() );
831             $GLOBALS['log']->error("Conversion of $date from $fromFormat to $toFormat failed: {$e->getMessage()}");
832             return '';
833         }
834     }
835
836     /**
837      * Convert DB datetime to local datetime
838      *
839      * TZ conversion is controlled by parameter
840      *
841      * @param string $date Original date in DB format
842      * @param bool $meridiem Ignored for BC
843      * @param bool $convert_tz Perform TZ conversion?
844      * @param User $user User owning the conversion formats
845      * @return string Date in display format
846      */
847     function to_display_date_time($date, $meridiem = true, $convert_tz = true, $user = null)
848     {
849         return $this->_convert($date,
850             self::DB_DATETIME_FORMAT, self::$gmtTimezone, $this->get_date_time_format($user),
851             $convert_tz ? $this->_getUserTZ($user) : self::$gmtTimezone, true);
852     }
853
854     /**
855      * Converts DB time string to local time string
856      *
857      * TZ conversion depends on parameter
858      *
859      * @param string $date Time in DB format
860      * @param bool $meridiem
861      * @param bool $convert_tz Perform TZ conversion?
862      * @return string Time in user-defined format
863      */
864     public function to_display_time($date, $meridiem = true, $convert_tz = true)
865     {
866         if($convert_tz && strpos($date, ' ') === false) {
867             // we need TZ adjustment but have no date, assume today
868             $date = $this->expandTime($date, self::DB_DATETIME_FORMAT, self::$gmtTimezone);
869         }
870         return $this->_convert($date,
871             $convert_tz ? self::DB_DATETIME_FORMAT : self::DB_TIME_FORMAT, self::$gmtTimezone,
872             $this->get_time_format(), $convert_tz ? $this->_getUserTZ() : self::$gmtTimezone);
873     }
874
875     /**
876      * Splits time in given format into components
877      *
878      * Components: h, m, s, a (am/pm) if format requires it
879      * If format has am/pm, hour is 12-based, otherwise 24-based
880      *
881      * @param string $date
882      * @param string $format
883      * @return array
884      */
885     public function splitTime($date, $format)
886     {
887         if (! ($date instanceof DateTime)) {
888             $date = SugarDateTime::createFromFormat($format, $date);
889         }
890         $ampm = strpbrk($format, 'aA');
891         $datearr = array(
892                 "h" => ($ampm == false) ? $date->format("H") : $date->format("h"),
893                 'm' => $date->format("i"),
894                 's' => $date->format("s")
895         );
896         if ($ampm) {
897             $datearr['a'] = ($ampm{0} == 'a') ? $date->format("a") : $date->format("A");
898         }
899         return $datearr;
900     }
901
902     /**
903      * Converts DB date string to local date string
904      *
905      * TZ conversion depens on parameter
906      *
907      * @param string $date Date in DB format
908      * @param bool $convert_tz Perform TZ conversion?
909      * @return string Date in user-defined format
910      */
911     public function to_display_date($date, $convert_tz = true)
912     {
913         return $this->_convert($date,
914             self::DB_DATETIME_FORMAT, self::$gmtTimezone,
915             $this->get_date_format(), $convert_tz ? $this->_getUserTZ() : self::$gmtTimezone, true);
916     }
917
918     /**
919      * Convert date from format to format
920      *
921      * No TZ conversion is performed!
922      *
923      * @param string $date
924      * @param string $fromformat Source format
925      * @param string $toformat Target format
926      * @return string Converted date
927      */
928     function to_display($date, $from, $to)
929     {
930         return $this->_convert($date, $from, self::$gmtTimezone, $to, self::$gmtTimezone);
931     }
932
933     /**
934      * Get DB datetime format
935      * @return string
936      */
937     public function get_db_date_time_format()
938     {
939         return self::DB_DATETIME_FORMAT;
940     }
941
942     /**
943      * Get DB date format
944      * @return string
945      */
946     public function get_db_date_format()
947     {
948         return self::DB_DATE_FORMAT;
949     }
950
951     /**
952      * Get DB time format
953      * @return string
954      */
955     public function get_db_time_format()
956     {
957         return self::DB_TIME_FORMAT;
958     }
959
960     /**
961      * Convert date from local datetime to GMT-based DB datetime
962      *
963      * Includes TZ conversion.
964      *
965      * @param string $date
966      * @return string Datetime in DB format
967      */
968     public function to_db($date)
969     {
970         return $this->_convert($date,
971             $this->get_date_time_format(), $this->_getUserTZ(),
972             $this->get_db_date_time_format(), self::$gmtTimezone,
973             true);
974     }
975
976     /**
977      * Convert local datetime to DB date
978      *
979      * TZ conversion depends on parameter. If false, only format conversion is performed.
980      *
981      * @param string $date Local date
982      * @param bool $convert_tz Should time and TZ be taken into account?
983      * @return string Date in DB format
984      */
985     public function to_db_date($date, $convert_tz = true)
986     {
987         return $this->_convert($date,
988             $this->get_date_time_format(), $convert_tz ? $this->_getUserTZ() : self::$gmtTimezone,
989             self::DB_DATE_FORMAT, self::$gmtTimezone, true);
990     }
991
992     /**
993      * Convert local datetime to DB time
994      *
995      * TZ conversion depends on parameter. If false, only format conversion is performed.
996      *
997      * @param string $date Local date
998      * @param bool $convert_tz Should time and TZ be taken into account?
999      * @return string Time in DB format
1000      */
1001     public function to_db_time($date, $convert_tz = true)
1002     {
1003         $format = $this->get_date_time_format();
1004         $tz = $convert_tz ? $this->_getUserTZ() : self::$gmtTimezone;
1005         if($convert_tz && strpos($date, ' ') === false) {
1006             // we need TZ adjustment but have short string, expand it to full one
1007             // FIXME: if the string is short, should we assume date or time?
1008             $date = $this->expandTime($date, $format, $tz);
1009         }
1010         return $this->_convert($date,
1011             $convert_tz ? $format : $this->get_time_format(),
1012             $tz,
1013             self::DB_TIME_FORMAT, self::$gmtTimezone);
1014     }
1015
1016     /**
1017      * Takes a Date & Time value in local format and converts them to DB format
1018      * No TZ conversion!
1019      *
1020      * @param string $date
1021      * @param string $time
1022      * @return array Date & time in DB format
1023      **/
1024     public function to_db_date_time($date, $time)
1025     {
1026         try {
1027             $phpdate = SugarDateTime::createFromFormat($this->get_date_time_format(),
1028                 $this->merge_date_time($date, $time), self::$gmtTimezone);
1029             if ($phpdate == false) {
1030                 return array('', '');
1031             }
1032             return array($this->asDbDate($phpdate), $this->asDbTime($phpdate));
1033         } catch (Exception $e) {
1034             $GLOBALS['log']->error("Conversion of $date,$time failed");
1035             return array('', '');
1036         }
1037     }
1038
1039     /**
1040      * Return current time in DB format
1041      * @return string
1042      */
1043     public function nowDb()
1044     {
1045         if(!$this->allow_cache) {
1046             $nowGMT = $this->getNow();
1047         } else {
1048             $nowGMT = $this->now;
1049         }
1050         return $this->asDb($nowGMT);
1051     }
1052
1053     /**
1054      * Return current date in DB format
1055      * @return string
1056      */
1057     public function nowDbDate()
1058     {
1059         if(!$this->allow_cache) {
1060             $nowGMT = $this->getNow();
1061         } else {
1062             $nowGMT = $this->now;
1063         }
1064         return $this->asDbDate($nowGMT, true);
1065     }
1066
1067     /**
1068      * Get 'now' DateTime object
1069      * @param bool $userTz return in user timezone?
1070      * @return SugarDateTime
1071      */
1072     public function getNow($userTz = false)
1073     {
1074         if(!$this->allow_cache) {
1075             return new SugarDateTime("now", $userTz?$this->_getUserTz():self::$gmtTimezone);
1076         }
1077         // TODO: should we return clone?
1078         $now = clone $this->now;
1079         if($userTz) {
1080             return $this->tzUser($now);
1081         }
1082         return $now;
1083     }
1084
1085     /**
1086      * Set 'now' time
1087      * For testability - predictable time value
1088      * @param DateTime $now
1089      */
1090     public function setNow($now)
1091     {
1092         $this->now = $now;
1093         return $this;
1094     }
1095
1096     /**
1097      * Return current datetime in local format
1098      * @return string
1099      */
1100     public function now()
1101     {
1102         return  $this->asUser($this->getNow());
1103     }
1104
1105     /**
1106      * Return current date in User format
1107      * @return string
1108      */
1109     public function nowDate()
1110     {
1111         return  $this->asUserDate($this->getNow());
1112     }
1113
1114     /**
1115      * Get user format's time separator
1116      * @return string
1117      */
1118     public function timeSeparator()
1119     {
1120         if (empty($this->time_separator)) {
1121             $this->time_separator = $this->timeSeparatorFormat($this->get_time_format());
1122         }
1123         return $this->time_separator;
1124     }
1125
1126     /**
1127      * Find out format's time separator
1128      * @param string $timeformat Time format
1129      */
1130     public function timeSeparatorFormat($timeformat)
1131     {
1132         $date = $this->_convert("00:11:22", self::DB_TIME_FORMAT, null, $timeformat, null);
1133         if (preg_match('/\d+(.+?)11/', $date, $matches)) {
1134             $sep = $matches[1];
1135         } else {
1136             $sep = ':';
1137         }
1138         return $sep;
1139     }
1140
1141     /**
1142      * Returns start and end of a certain local date in GMT
1143      * Example: for May 19 in PDT start would be 2010-05-19 07:00:00, end would be 2010-05-20 06:59:59
1144      * @param string|DateTime $date Date in any suitable format
1145      * @return array Start & end date in start, startdate, starttime, end, enddate, endtime
1146      */
1147     public function getDayStartEndGMT($date, User $user = null)
1148     {
1149         if ($date instanceof DateTime) {
1150             $min = clone $date;
1151             $min->setTimezone($this->_getUserTZ($user));
1152             $max = clone $date;
1153             $max->setTimezone($this->_getUserTZ($user));
1154         } else {
1155             $min = new DateTime($date, $this->_getUserTZ($user));
1156             $max = new DateTime($date, $this->_getUserTZ($user));
1157         }
1158         $min->setTime(0, 0);
1159         $max->setTime(23, 59, 59);
1160
1161         $min->setTimezone(self::$gmtTimezone);
1162         $max->setTimezone(self::$gmtTimezone);
1163
1164         $result['start'] = $this->asDb($min);
1165         $result['startdate'] = $this->asDbDate($min);
1166         $result['starttime'] = $this->asDbTime($min);
1167         $result['end'] = $this->asDb($max);
1168         $result['enddate'] = $this->asDbDate($max);
1169         $result['endtime'] = $this->asDbtime($max);
1170
1171         return $result;
1172     }
1173
1174     /**
1175      * Expand date format by adding midnight to it
1176      * Note: date is assumed to be in target format already
1177      * @param string $date
1178      * @param string $format Target format
1179      * @return string
1180      */
1181     public function expandDate($date, $format)
1182     {
1183         $formats = $this->split_date_time($format);
1184         if(isset($formats[1])) {
1185             return $this->merge_date_time($date, $this->_get_midnight($formats[1]));
1186         }
1187         return $date;
1188     }
1189
1190     /**
1191      * Expand time format by adding today to it
1192      * Note: time is assumed to be in target format already
1193      * @param string $date
1194      * @param string $format Target format
1195      * @param DateTimeZone $tz
1196      */
1197     public function expandTime($date, $format, $tz)
1198     {
1199         $formats = $this->split_date_time($format);
1200         if(isset($formats[1])) {
1201             $now = clone $this->getNow();
1202             $now->setTimezone($tz);
1203             return $this->merge_date_time($now->format($formats[0]), $date);
1204         }
1205         return $date;
1206     }
1207
1208     /**
1209          * Get midnight (start of the day) in local time format
1210          *
1211          * @return Time string
1212          */
1213         function get_default_midnight()
1214         {
1215         return $this->_get_midnight($this->get_time_format());
1216         }
1217
1218         /**
1219          * Get the name of the timezone for the user
1220          * @param User $user User, default - current user
1221          * @return string
1222          */
1223         public static function userTimezone(User $user = null)
1224         {
1225             $user = self::getInstance()->_getUser($user);
1226             if(empty($user)) {
1227                 return '';
1228             }
1229             $tz = self::getInstance()->_getUserTZ($user);
1230             if($tz) {
1231                 return $tz->getName();
1232             }
1233             return '';
1234         }
1235
1236         /**
1237          * Guess the timezone for the current user
1238          * @return string
1239          */
1240         public static function guessTimezone($userOffset = 0)
1241         {
1242             if(!is_numeric($userOffset)) {
1243                     return '';
1244             }
1245             $defaultZones= array(
1246                 'America/Anchorage', 'America/Los_Angeles', 'America/Phoenix', 'America/Chicago',
1247                 'America/New_York', 'America/Argentina/Buenos_Aires', 'America/Montevideo',
1248                 'Europe/London', 'Europe/Amsterdam', 'Europe/Athens', 'Europe/Moscow',
1249                 'Asia/Tbilisi', 'Asia/Omsk', 'Asia/Jakarta', 'Asia/Hong_Kong',
1250                 'Asia/Tokyo', 'Pacific/Guam', 'Australia/Sydney', 'Australia/Perth',
1251             );
1252
1253             $now = new DateTime();
1254             $tzlist = timezone_identifiers_list();
1255             if($userOffset == 0) {
1256              $gmtOffset = date('Z');
1257                  $nowtz = date('e');
1258                  if(in_array($nowtz, $tzlist)) {
1259                  array_unshift($defaultZones, $nowtz);
1260                  } else {
1261                      $nowtz = timezone_name_from_abbr(date('T'), $gmtOffset, date('I'));
1262                      if(in_array($nowtz, $tzlist)) {
1263                          array_unshift($defaultZones, $nowtz);
1264                      }
1265                  }
1266         } else {
1267             $gmtOffset = $userOffset * 60;
1268         }
1269         foreach($defaultZones as $zoneName) {
1270                 $tz = new DateTimeZone($zoneName);
1271                 if($tz->getOffset($now) == $gmtOffset) {
1272                 return $tz->getName();
1273                 }
1274             }
1275         // try all zones
1276             foreach($tzlist as $zoneName) {
1277                 $tz = new DateTimeZone($zoneName);
1278                 if($tz->getOffset($now) == $gmtOffset) {
1279                 return $tz->getName();
1280                 }
1281             }
1282             return null;
1283         }
1284
1285         /**
1286          * Get the description of the user timezone for specific date
1287          * Like: PST(+08:00)
1288          * We need the date because it can be DST or non-DST
1289          * Note it's different from TZ name in tzName() that relates to current date
1290          * @param User $user User, default - current user
1291          * @return string
1292          */
1293         public static function userTimezoneSuffix(DateTime $date, User $user = null)
1294         {
1295             $user = self::getInstance()->_getUser($user);
1296             if(empty($user)) {
1297                 return '';
1298             }
1299             self::getInstance()->tzUser($date, $user);
1300             return $date->format('T(P)');
1301         }
1302
1303         /**
1304          * Get display name for a certain timezone
1305          * Note: it uses current date for GMT offset, so it may be not suitable for displaying generic dates
1306          * @param string|DateTimeZone $name TZ name
1307          * @return string
1308          */
1309         public static function tzName($name)
1310         {
1311             if(empty($name)) {
1312                 return '';
1313             }
1314             if($name instanceof DateTimeZone) {
1315                 $tz = $name;
1316             } else {
1317             $tz = timezone_open($name);
1318             }
1319         if(!$tz) {
1320             return "???";
1321         }
1322         $now = new DateTime("now", $tz);
1323         $off = $now->getOffset();
1324         $translated = translate('timezone_dom','',$name);
1325         if(is_string($translated) && !empty($translated)) {
1326             $name = $translated;
1327         }
1328         return sprintf("%s (GMT%+2d:%02d)%s", str_replace('_',' ', $name), $off/3600, (abs($off)/60)%60, "");//$now->format('I')==1?"(+DST)":"");
1329         }
1330
1331
1332         public static function _sortTz($a, $b)
1333         {
1334             if($a[0] == $b[0]) {
1335             return strcmp($a[1], $b[1]);
1336             } else {
1337                 return $a[0]<$b[0]?-1:1;
1338             }
1339         }
1340
1341         /**
1342          * Get list of all timezones in the system
1343          * @return array
1344          */
1345         public static function getTimezoneList()
1346         {
1347         $now = new DateTime();
1348         $res_zones = $zones = array();
1349             foreach(timezone_identifiers_list() as $zoneName) {
1350             $tz = new DateTimeZone($zoneName);
1351                 $zones[$zoneName] = array($tz->getOffset($now), self::tzName($zoneName));
1352             }
1353             uasort($zones, array('TimeDate', '_sortTz'));
1354             foreach($zones as $name => $zonedef) {
1355                 $res_zones[$name] = $zonedef[1];
1356             }
1357             return $res_zones;
1358         }
1359
1360         /**
1361          * Print timestamp in RFC2616 format:
1362          * @return string
1363          */
1364         public static function httpTime($ts = null)
1365         {
1366             if($ts === null) {
1367                 $ts = time();
1368             }
1369             return gmdate(self::RFC2616_FORMAT, $ts);
1370         }
1371
1372         /**
1373          * Create datetime object from calendar array
1374          * @param array $time
1375          * @return SugarDateTime
1376          */
1377         public function fromTimeArray($time)
1378         {
1379                 if (! isset( $time) || count($time) == 0 )
1380                 {
1381                         return $this->nowDb();
1382                 }
1383                 elseif ( isset( $time['ts']))
1384                 {
1385                         return $this->fromTimestamp($time['ts']);
1386                 }
1387                 elseif ( isset( $time['date_str']))
1388                 {
1389                     return $this->fromDb($time['date_str']);
1390                 }
1391                 else
1392                 {
1393                 $hour = 0;
1394                 $min = 0;
1395                 $sec = 0;
1396                 $now = $this->getNow(true);
1397                 $day = $now->day;
1398                 $month = $now->month;
1399                 $year = $now->year;
1400                     if (isset($time['sec']))
1401                         {
1402                                 $sec = $time['sec'];
1403                         }
1404                         if (isset($time['min']))
1405                         {
1406                                 $min = $time['min'];
1407                         }
1408                         if (isset($time['hour']))
1409                         {
1410                                 $hour = $time['hour'];
1411                         }
1412                         if (isset($time['day']))
1413                         {
1414                                 $day = $time['day'];
1415                         }
1416                         if (isset($time['month']))
1417                         {
1418                                 $month = $time['month'];
1419                         }
1420                         if (isset($time['year']) && $time['year'] >= 1970)
1421                         {
1422                                 $year = $time['year'];
1423                         }
1424                         return $now->setDate($year, $month, $day)->setTime($hour, $min, $sec)->setTimeZone(self::$gmtTimezone);
1425                 }
1426         return null;
1427         }
1428
1429         /**
1430          * Returns the date portion of a datetime string
1431          *
1432          * @param string $datetime
1433          * @return string
1434          */
1435         public function getDatePart($datetime)
1436         {
1437             list($date, $time) = $this->split_date_time($datetime);
1438             return $date;
1439         }
1440
1441         /**
1442          * Returns the time portion of a datetime string
1443          *
1444          * @param string $datetime
1445          * @return string
1446          */
1447         public function getTimePart($datetime)
1448         {
1449             list($date, $time) = $this->split_date_time($datetime);
1450             return $time;
1451         }
1452
1453     /**
1454      * Returns the offset from user's timezone to GMT
1455      * @param User $user
1456      * @param DateTime $time When the offset is taken, default is now
1457      * @return int Offset in minutes
1458      */
1459     public function getUserUTCOffset(User $user = null, DateTime $time = null)
1460     {
1461         if(empty($time)) {
1462             $time = $this->now;
1463         }
1464         return $this->_getUserTZ($user)->getOffset($time) / 60;
1465     }
1466
1467     /**
1468      * Create regexp from datetime format
1469      * @param string $format
1470      * @return string Regular expression string
1471      */
1472     public static function get_regular_expression($format)
1473     {
1474         $newFormat = '';
1475         $regPositions = array();
1476         $ignoreNextChar = false;
1477         $count = 1;
1478         foreach (str_split($format) as $char) {
1479             if (! $ignoreNextChar && isset(self::$format_to_regexp[$char])) {
1480                 $newFormat .= '(' . self::$format_to_regexp[$char] . ')';
1481                 $regPositions[$char] = $count;
1482                 $count ++;
1483             } else {
1484                 $ignoreNextChar = false;
1485                 $newFormat .= $char;
1486
1487             }
1488             if ($char == "\\") {
1489                 $ignoreNextChar = true;
1490             }
1491         }
1492
1493         return array('format' => $newFormat, 'positions' => $regPositions);
1494     }
1495
1496     // format - date expression ('' means now) for start and end of the range
1497     protected $date_expressions = array(
1498         'yesterday' =>    array("-1 day", "-1 day"),
1499         'today' =>        array("", ""),
1500         'tomorrow' =>     array("+1 day", "+1 day"),
1501         'last_7_days' =>  array("-6 days", ""),
1502         'next_7_days' =>  array("", "+6 days"),
1503         'last_30_days' => array("-29 days", ""),
1504         'next_30_days' => array("", "+29 days"),
1505     );
1506
1507     /**
1508      * Parse date template
1509      * @param string $template Date expression
1510      * @param bool $daystart Do we want start or end of the day?
1511      * @param User $user
1512      */
1513     protected function parseFromTemplate($template, $daystart, User $user = null)
1514         {
1515         $now = $this->tzUser($this->getNow(), $user);
1516         if(!empty($template[0])) {
1517             $now->modify($template[0]);
1518         }
1519         if($daystart) {
1520             return $now->get_day_begin();
1521         } else {
1522             return $now->get_day_end();
1523         }
1524         }
1525
1526         /**
1527          * Get month-long range mdiff months from now
1528          */
1529         protected function diffMon($mdiff, User $user)
1530         {
1531         $now = $this->tzUser($this->getNow(), $user);
1532             $now->setDate($now->year, $now->month+$mdiff, 1);
1533             $start = $now->get_day_begin();
1534             $end = $now->setDate($now->year, $now->month, $now->days_in_month)->setTime(23, 59, 59);
1535             return array($start, $end);
1536         }
1537
1538         /**
1539          * Get year-long range ydiff years from now
1540          */
1541         protected function diffYear($ydiff, User $user)
1542         {
1543         $now = $this->tzUser($this->getNow(), $user);
1544             $now->setDate($now->year+$ydiff, 1, 1);
1545             $start = $now->get_day_begin();
1546             $end = $now->setDate($now->year, 12, 31)->setTime(23, 59, 59);
1547             return array($start, $end);
1548         }
1549
1550         /**
1551          * Parse date range expression
1552          * Returns beginning and end of the range as a date
1553          * @param string $range
1554          * @param User $user
1555          * @return array
1556          */
1557         public function parseDateRange($range, User $user = null)
1558         {
1559         if(isset($this->date_expressions[$range])) {
1560             return array($this->parseFromTemplate($this->date_expressions[$range][0], true, $user),
1561                 $this->parseFromTemplate($this->date_expressions[$range][1], false, $user)
1562             );
1563         }
1564             switch($range) {
1565                         case 'next_month':
1566                             return $this->diffMon(1,  $user);
1567                     case 'last_month':
1568                             return $this->diffMon(-1,  $user);
1569                     case 'this_month':
1570                             return $this->diffMon(0,  $user);
1571                 case 'last_year':
1572                             return $this->diffYear(-1,  $user);
1573                 case 'this_year':
1574                             return $this->diffYear(0,  $user);
1575                 case 'next_year':
1576                             return $this->diffYear(1,  $user);
1577                 default:
1578                             return null;
1579             }
1580         }
1581
1582     /********************* OLD functions, should not be used publicly anymore ****************/
1583     /**
1584      * Merge time without am/pm with am/pm string
1585      * @TODO find better way to do this!
1586      * @deprecated for public use
1587      * @param string $date
1588      * @param string $format User time format
1589      * @param string $mer
1590      * @return string
1591      */
1592     function merge_time_meridiem($date, $format, $mer)
1593     {
1594         $date = trim($date);
1595         if (empty($date)) {
1596             return $date;
1597         }
1598         $fakeMerFormat = str_replace(array('a', 'A'), array('@~@', '@~@'), $format);
1599         $noMerFormat = str_replace(array('a', 'A'), array('', ''), $format);
1600         $newDate = $this->swap_formats($date, $noMerFormat, $fakeMerFormat);
1601         return str_replace('@~@', $mer, $newDate);
1602     }
1603
1604     /**
1605      * @deprecated for public use
1606      * Convert date from one format to another
1607      *
1608      * @param string $date
1609      * @param string $from
1610      * @param string $to
1611      * @return string
1612      */
1613     public function swap_formats($date, $from, $to)
1614     {
1615         return $this->_convert($date, $from, self::$gmtTimezone, $to, self::$gmtTimezone);
1616     }
1617
1618     /**
1619      * @deprecated for public use
1620      * handles offset values for Timezones and DST
1621      * @param   $date        string             date/time formatted in user's selected format
1622      * @param   $format      string             destination format value as passed to PHP's date() funtion
1623      * @param   $to                  boolean
1624      * @param   $user        object             user object from which Timezone and DST
1625      * @param   $usetimezone string             timezone name
1626      * values will be derived
1627      * @return   string         date formatted and adjusted for TZ and DST
1628      */
1629     function handle_offset($date, $format, $to = true, $user = null, $usetimezone = null)
1630     {
1631         $tz = empty($usetimezone)?$this->_getUserTZ($user):new DateTimeZone($usetimezone);
1632         $dateobj = new SugarDateTime($date, $to? self::$gmtTimezone : $tz);
1633         $dateobj->setTimezone($to ? $tz: self::$gmtTimezone);
1634         return $dateobj->format($format);
1635 //        return $this->_convert($date, $format, $to ? self::$gmtTimezone : $tz, $format, $to ? $tz : self::$gmtTimezone);
1636     }
1637
1638     /**
1639      * @deprecated for public use
1640      * Get current GMT datetime in DB format
1641      * @return string
1642      */
1643     function get_gmt_db_datetime()
1644     {
1645         return $this->nowDb();
1646     }
1647
1648     /**
1649      * @deprecated for public use
1650      * Get current GMT date in DB format
1651      * @return string
1652      */
1653     function get_gmt_db_date()
1654     {
1655         return $this->nowDbDate();
1656     }
1657
1658     /**
1659      * @deprecated for public use
1660      * this method will take an input $date variable (expecting Y-m-d format)
1661      * and get the GMT equivalent - with an hour-level granularity :
1662      * return the max value of a given locale's
1663      * date+time in GMT metrics (i.e., if in PDT, "2005-01-01 23:59:59" would be
1664      * "2005-01-02 06:59:59" in GMT metrics)
1665      */
1666     function handleOffsetMax($date)
1667     {
1668         $min = new DateTime($date, $this->_getUserTZ());
1669         $min->setTime(0, 0);
1670         $max = new DateTime($date, $this->_getUserTZ());
1671         $max->setTime(23, 59, 59);
1672
1673         $min->setTimezone(self::$gmtTimezone);
1674         $max->setTimezone(self::$gmtTimezone);
1675
1676         $gmtDateTime['date'] = $this->asDbDate($max, false);
1677         $gmtDateTime['time'] = $this->asDbDate($max, false);
1678         $gmtDateTime['min'] = $this->asDb($min);
1679         $gmtDateTime['max'] = $this->asDb($max);
1680
1681         return $gmtDateTime;
1682     }
1683
1684     /**
1685      * @deprecated for public use
1686      * this returns the adjustment for a user against the server time
1687      *
1688      * @return integer number of minutes to adjust a time by to get the appropriate time for the user
1689      */
1690     public function adjustmentForUserTimeZone()
1691     {
1692         $tz = $this->_getUserTZ();
1693         $server_tz = new DateTimeZone(date_default_timezone_get());
1694         if ($tz && $server_tz) {
1695             return ($server_tz->getOffset($this->now) - $tz->getOffset($this->now)) / 60;
1696         }
1697         return 0;
1698     }
1699
1700     /**
1701      * @deprecated for public use
1702          * assumes that olddatetime is in Y-m-d H:i:s format
1703          */
1704     function convert_to_gmt_datetime($olddatetime)
1705     {
1706         if (! empty($olddatetime)) {
1707             return date('Y-m-d H:i:s', strtotime($olddatetime) - date('Z'));
1708         }
1709     }
1710
1711     /**
1712      * @deprecated for public use
1713          * get user timezone info
1714          */
1715     public function getUserTimeZone(User $user = null)
1716     {
1717         $tz = $this->_getUserTZ($user);
1718         return array("gmtOffset" => $tz->getOffset($this->now) / 60);
1719     }
1720
1721     /**
1722      * @deprecated for public use
1723          * get timezone start & end
1724          */
1725     public function getDSTRange($year, $zone)
1726     {
1727         $year_date = SugarDateTime::createFromFormat("Y", $year, self::$gmtTimezone);
1728         $year_end = clone $year_date;
1729         $year_end->setDate((int) $year, 12, 31);
1730         $year_end->setTime(23, 59, 59);
1731         $year_date->setDate((int) $year, 1, 1);
1732         $year_date->setTime(0, 0, 0);
1733         $tz = $this->_getUserTZ();
1734         $transitions = $tz->getTransitions($year_date->getTimestamp(), $year_end->getTimestamp());
1735         $idx = 0;
1736         while (! $transitions[$idx]["isdst"])
1737             $idx ++;
1738         $startdate = new DateTime("@" . $transitions[$idx]["ts"], self::$gmtTimezone);
1739         while ($transitions[$idx]["isdst"])
1740             $idx ++;
1741         $enddate = new DateTime("@" . $transitions[$idx]["ts"], self::$gmtTimezone);
1742         return array("start" => $this->asDb($startdate), "end" => $this->asDb($enddate));
1743     }
1744
1745 /****************** GUI stuff that really shouldn't be here, will be moved ************/
1746     /**
1747      * Get Javascript variables setup for user date format validation
1748      * @deprecated moved to SugarView
1749      * @return string JS code
1750      */
1751     function get_javascript_validation()
1752     {
1753         return SugarView::getJavascriptValidation();
1754     }
1755
1756     /**
1757      * AMPMMenu
1758      * This method renders a <select> HTML form element based on the
1759      * user's time format preferences, with give date's value highlighted.
1760      *
1761      * If user's prefs have no AM/PM string, returns empty string.
1762      *
1763      * @todo There is hardcoded HTML in here that does not allow for localization
1764      * of the AM/PM am/pm Strings in this drop down menu.  Also, perhaps
1765      * change to the substr_count function calls to strpos
1766      * TODO: Remove after full switch to fields
1767      * @deprecated
1768      * @param string $prefix Prefix for SELECT
1769      * @param string $date Date in display format
1770      * @param string $attrs Additional attributes for SELECT
1771      * @return string SELECT HTML
1772      */
1773     function AMPMMenu($prefix, $date, $attrs = '')
1774     {
1775         $tf = $this->get_time_format();
1776         $am = strpbrk($tf, 'aA');
1777         if ($am == false) {
1778             return '';
1779         }
1780         $selected = array("am" => "", "pm" => "", "AM" => "", "PM" => "");
1781         if (preg_match('/([ap]m)/i', $date, $match)) {
1782             $selected[$match[1]] = " selected";
1783         }
1784
1785         $menu = "<select name='" . $prefix . "meridiem' " . $attrs . ">";
1786         if ($am{0} == 'a') {
1787             $menu .= "<option value='am'{$selected["am"]}>am";
1788             $menu .= "<option value='pm'{$selected["pm"]}>pm";
1789         } else {
1790             $menu .= "<option value='AM'{$selected["AM"]}>AM";
1791             $menu .= "<option value='PM'{$selected["PM"]}>PM";
1792         }
1793
1794         return $menu . '</select>';
1795     }
1796
1797     /**
1798      * TODO: Remove after full switch to fields
1799      */
1800     function get_user_date_format()
1801     {
1802         return str_replace(array('Y', 'm', 'd'), array('yyyy', 'mm', 'dd'), $this->get_date_format());
1803     }
1804
1805     /**
1806      * TODO: Remove after full switch to fields
1807      * @deprecated
1808      * @return string
1809      */
1810     function get_user_time_format()
1811     {
1812         global $sugar_config;
1813         $time_pref = $this->get_time_format();
1814
1815         if (! empty($time_pref) && ! empty($sugar_config['time_formats'][$time_pref])) {
1816             return $sugar_config['time_formats'][$time_pref];
1817         }
1818
1819         return '23:00'; //default
1820     }
1821
1822 }