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