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