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