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