]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - include/TimeDate.php
Release 6.1.4
[Github/sugarcrm.git] / include / TimeDate.php
1 <?php
2 if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
3 /*********************************************************************************
4  * SugarCRM is a customer relationship management program developed by
5  * SugarCRM, Inc. Copyright (C) 2004-2011 SugarCRM Inc.
6  * 
7  * This program is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU Affero General Public License version 3 as published by the
9  * Free Software Foundation with the addition of the following permission added
10  * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
11  * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
12  * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
13  * 
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16  * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
17  * details.
18  * 
19  * You should have received a copy of the GNU Affero General Public License along with
20  * this program; if not, see http://www.gnu.org/licenses or write to the Free
21  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22  * 02110-1301 USA.
23  * 
24  * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
25  * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
26  * 
27  * The interactive user interfaces in modified source and object code versions
28  * of this program must display Appropriate Legal Notices, as required under
29  * Section 5 of the GNU Affero General Public License version 3.
30  * 
31  * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
32  * these Appropriate Legal Notices must retain the display of the "Powered by
33  * SugarCRM" logo. If the display of the logo is not reasonably feasible for
34  * technical reasons, the Appropriate Legal Notices must display the words
35  * "Powered by SugarCRM".
36  ********************************************************************************/
37
38 require_once('include/timezone/timezones.php');
39
40 /**
41  * Date/time conversion class
42  *
43  * DB format - Y-m-d H:i:s (2009-12-31 23:46:12), in GMT
44  * Display format - user-defined, in user-supplied timezone
45  *
46  */
47 class TimeDate {
48         /**
49          * DB date format
50          * @var string
51          */
52         var $dbDayFormat = 'Y-m-d';
53         /**
54          * DB time format
55          * @var string
56          */
57         var $dbTimeFormat = 'H:i:s';
58         /**
59          * DB date & time formats
60          * For convenience
61          * @var string
62          */
63         var $dbDateTimeFormat = 'Y-m-d H:i:s';
64
65         protected $supported_strings = array(
66                 'a' => '[ap]m',
67                 'A' => '[AP]M',
68                 'd' => '[0-9]{1,2}',
69                 'h' => '[0-9]{1,2}',
70                 'H' => '[0-9]{1,2}',
71                 'i' => '[0-9]{1,2}',
72                 'm' => '[0-9]{1,2}',
73                 'Y' => '[0-9]{4}',
74                 's' => '[0-9]{1,2}'
75         );
76
77         /**
78          * Map the tokens passed into this as a "format" string to
79          * PHP's internal date() format string values.
80          *
81          * @var array
82          * @access private
83          * @see build_format()
84          */
85         private $time_token_map = array(
86                 'a' => 'a', // meridiem: am or pm
87                 'A' => 'A', // meridiem: AM or PM
88                 'd' => 'd', // days: 1 through 31
89                 'h' => 'h', // hours: 01 through 12
90                 'H' => 'H', // hours: 00 through 23
91                 'i' => 'i', // minutes: 00 through 59
92                 'm' => 'm', // month: 1 - 12
93                 'Y' => 'Y', // year: four digits
94                 's' => 's', // seconds
95         );
96
97     /**
98      * The current timezone information for the current user
99      * @var array
100      */
101     private $current_user_timezone = null;
102
103     /**
104      * The current user timezone adjustment
105      * @var int
106      */
107     private $current_user_adjustment = null;
108     /**
109      * The target TZ for current user timezone adjustment
110      * @var array
111      */
112     private $current_user_adjustment_tz = null;
113
114     /**
115      * Default midnight string in user format
116      * @var string
117      */
118     private $default_midnight = null;
119
120     /**
121      * For testability - disallow using cache
122      * @var bool
123      */
124     public $allow_cache = true;
125
126     /**
127      * Constructor
128      */
129     public function __construct()
130     {
131         // avoids a potential E_STRICT warning when using any date function
132         if ( function_exists('date_default_timezone_set') && function_exists('date_default_timezone_get'))
133             date_default_timezone_set(@date_default_timezone_get());
134     }
135
136         /**
137          * Returns the current users timezone info or another user;
138          *
139          * @param User $user user object for which you want to display, null for current user
140          * @return Array of timezone info
141          */
142         public function getUserTimeZone($user = null){
143                 global $timezones, $current_user;
144                 $usertimezone = array();
145                 if(empty($user) || (!empty($user->id) && $user->id == $current_user->id)) {
146                         if(isset($this->current_user_timezone)) return $this->current_user_timezone; // current user, return saved timezone info
147                         $user = $current_user;
148                 }
149
150                 if(isset($user))
151                 {
152                         if($usertimezone = $user->getPreference('timezone')) {
153                                         if(empty($timezones[$usertimezone])) {
154                                                 $GLOBALS['log']->fatal('TIMEZONE:NOT DEFINED-'. $usertimezone);
155                                                 $usertimezone = array();
156                                         } else {
157                                                 $usertimezone = $timezones[$usertimezone];
158                                         }
159                         }
160                 }
161
162                 if(!empty($user->id) && $user->id == $current_user->id) $this->current_user_timezone = $usertimezone; // save current_user
163                 return $usertimezone;
164         }
165
166         /**
167          * @deprecated for public use
168          * function adjustmentForUserTimeZone()
169          * this returns the adjustment for a user against the server time
170          *
171          * @param array $timezone_to_adjust pass in a timezone to adjust for
172          * @return integer number of minutes to adjust a time by to get the appropriate time for the user
173          */
174         public function adjustmentForUserTimeZone($timezone_to_adjust = null){
175                 if(isset($this->current_user_adjustment) && $this->current_user_adjustment_tz == $timezone_to_adjust){
176                         return $this->current_user_adjustment;
177                 }
178
179                 $adjustment = 0;
180                 $this->current_user_adjustment_tz = $timezone_to_adjust;
181
182                 if(empty($timezone_to_adjust)) {
183                         $timezone = $this->getUserTimeZone();
184                 } else {
185                         $timezone = $timezone_to_adjust;
186                 }
187                 if(empty($timezone)) {
188                         return $adjustment;
189                 }
190
191                 $server_offset = date('Z')/60;
192                 $server_in_ds = date('I');
193                 $user_in_ds = $this->inDST(date('Y-m-d H:i:s'), $timezone);
194                 $user_offset = $timezone['gmtOffset'] ;
195
196                 //compensate for ds for user
197                 if($user_in_ds) {
198                         $user_offset += 60;
199                 }
200
201                 //either both + or -
202                 $adjustment += $server_offset - $user_offset;
203                 if(empty($timezone_to_adjust)) $this->current_user_adjustment = $adjustment; // save current_user adj
204
205                 return $adjustment;
206         }
207
208         /**
209      * @deprecated for public use
210          * function getWeekDayName($indexOfDay)
211          * Returns a days name
212          *
213          * @param INT(WEEKDAY INDEX) $indexOfDay
214          * @return STRING representing the given weekday
215          */
216         function getWeekDayName($indexOfDay){
217                 static $dow = array ( 'sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday' );
218                 return $dow[$indexOfDay];
219         }
220         /**
221      * @deprecated for public use
222          * function getMonthName($indexMonth)
223          * Returns a Months Name
224          *
225          * @param INT(MONTH INDEX) $indexMonth
226          * @return STRING representation of the month
227          */
228         function getMonthName($indexMonth){
229                 static $months = array ( 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' );
230                 return $months[$indexMonth];
231         }
232
233         /**
234      * @deprecated for public use
235          * function getDateFromRules($year, $startMonth, $startDate, $weekday, $startTime )
236          * Converts the rules for a timezones dst into a string representation of a date for the given year
237          *
238          * @param STRING(YEAR) $year
239          * @param INT(MONTH INDEX) $startMonth
240          * @param INT(DATE INDEX) $startDate
241          * @param INT(WEEKDAY INDEX) $weekday
242          * @param INT(START TIME IN SECONDS) $startTime
243          * @return unknown
244          */
245         function getDateFromRules($year, $startMonth, $startDate, $weekday, $startTime ){
246                 if($weekday < 0)return date( 'Y-m-d H:i:s', strtotime("$year-$startMonth-$startDate") + $startTime);
247                 $dayname = self::getWeekDayName($weekday);
248                 if($startDate > 0)$startMonth--;
249                 $month = self::getMonthName($startMonth);
250                 $startWeek = floor($startDate/7);
251                 //echo "$startWeek week $dayname - $month 1, $year<br>";
252                 return date( 'Y-m-d H:i:s', strtotime( "$startWeek week $dayname", strtotime( "$month 1, $year" ) ) + $startTime );
253
254         }
255
256         /**
257          * @deprecated for public use
258          *      function getDSTRange($year, $zone)
259          *
260          * returns the start and end date for dst for a given timezone and year or false if that zone doesn't support dst
261          *
262          * @param STRING(Year e.g. 2005) $year
263          * @param ARRAY (TIME ZONE INFO) $zone
264          * @return ARRAY OF DATE REPRESENTING THE START AND END OF DST or FALSE if the zone doesn't support dst
265          */
266         function getDSTRange($year, $zone){
267                 $range = array();
268                 if(empty($zone['dstOffset'])){
269                         return false;
270                 }
271
272                 $range['start'] = $this->getDateFromRules($year, $zone['dstMonth'], $zone['dstStartday'], $zone['dstWeekday'],  $zone['dstStartTimeSec']);
273                 $range['end'] = $this->getDateFromRules($year, $zone['stdMonth'], $zone['stdStartday'], $zone['stdWeekday'],  $zone['stdStartTimeSec']);
274                 return $range;
275         }
276
277      /**
278       * @deprecated for public use
279       *
280       * Is the date in DST or not
281       * @param $date
282       * @param $zone
283       */
284         function inDST($date, $zone){
285                 $datetime = explode(' ', $date);
286                 $dateSplit = explode('-', $datetime[0]);
287                 if(empty($dateSplit[2]))return false;
288                 $dstRange = $this->getDSTRange($dateSplit[0], $zone);
289                 if(!$dstRange){
290                         return false;
291                 }
292                 $datestamp = strtotime($date);
293                 $startstamp = strtotime($dstRange['start']);
294                 $endstamp = strtotime($dstRange['end']);
295                 if((($datestamp >= $startstamp  || $datestamp < $endstamp) && $startstamp > $endstamp)
296                         || ($datestamp >= $startstamp && $datestamp < $endstamp)
297                 ){
298                         return true;
299                 }
300                 return false;
301         }
302
303         /**
304          * Create regexp from datetime format
305          * @param string $format
306          * @return string Regular expression string
307          */
308         function get_regular_expression($format) {
309                 $newFormat = '';
310                 $regPositions = array();
311                 $ignoreNextChar = false;
312                 $count = 1;
313                 $format_characters = str_split($format, 1);
314                 foreach ($format_characters as $char) {
315                         if (!$ignoreNextChar && isset($this->supported_strings[$char])) {
316                                 $newFormat.= '('.$this->supported_strings[$char].')';
317                                 $regPositions[$char] = $count;
318                                 $count++;
319                         } else {
320                                 $ignoreNextChar = false;
321
322                                 $newFormat.= $char;
323
324                         }
325                         if ($char == "\\") {
326                                 $ignoreNextChar = true;
327                         }
328                 }
329
330                 return array('format'=>$newFormat, 'positions'=>$regPositions);
331
332         }
333
334         /**
335          * Verify if the date string conforms to a format
336          *
337          * @param string $date
338          * @param string $format Format to check
339          * @param string $toformat
340          * @return bool Is the date ok?
341          */
342         public function check_matching_format($date, $format, $toformat = '') {
343                 $regs = array();
344                 $startreg = $this->get_regular_expression($format);
345                 if (!empty($toformat)) {
346                         $otherreg = $this->get_regular_expression($toformat);
347                         //if the other format has the same regular expression then maybe it is shifting month and day position or something similar let it go for formating
348                         if ($startreg['format'] == $otherreg['format']) {
349                                 return false;
350                         }
351                 }
352
353                  preg_match('@'.$startreg['format'].'@', $date, $regs);
354                 if (empty($regs)) {
355                         return false;
356                 }
357                 return true;
358         }
359
360         /**
361          * @deprecated for public use
362          * Build a true PHP format string from a user supplied format string
363          *
364          * @param string $format
365          * @return string
366          * @access private
367          * @see $time_token_map
368          */
369         function build_format($format)
370         {
371                 $format = str_split($format, 1);
372                 $return = '';
373                 foreach ($format as $char) {
374                         $return .= (isset($this->time_token_map[$char])) ?
375                                 $this->time_token_map[$char] :
376                                 $char;
377                 }
378                 return $return;
379         }
380
381     /**
382      * Convert date from one format to another
383      *
384      * @param string $date
385      * @param string $from
386      * @param string $to
387      * @return string
388      */
389         function swap_formats($date, $startFormat, $endFormat) {
390                 $startreg = $this->get_regular_expression($startFormat);
391                 preg_match('@'.$startreg['format'].'@', $date, $regs);
392                 $newDate = $endFormat;
393                 //handle 12 to 24 hour conversion
394                 if (isset($startreg['positions']['h']) && !isset($startreg['positions']['H']) && !empty($regs[$startreg['positions']['h']]) && $regs[$startreg['positions']['h']] !== '' && strpos($endFormat, 'H') > -1) {
395                         $startreg['positions']['H'] = sizeof($startreg['positions']) + 1;
396                         $regs[$startreg['positions']['H']] = $regs[$startreg['positions']['h']];
397                         if ((isset($startreg['positions']['A']) && isset($regs[$startreg['positions']['A']]) && $regs[$startreg['positions']['A']] == 'PM') || (isset($startreg['positions']['a']) && isset($regs[$startreg['positions']['a']]) && $regs[$startreg['positions']['a']] == 'pm')) {
398                                 if ($regs[$startreg['positions']['h']] != 12) {
399                                         $regs[$startreg['positions']['H']] = $regs[$startreg['positions']['h']] + 12;
400                                 }
401                         }
402                         if ((isset($startreg['positions']['A']) && isset($regs[$startreg['positions']['A']])&& $regs[$startreg['positions']['A']] == 'AM') || (isset($startreg['positions']['a']) && isset($regs[$startreg['positions']['a']]) && $regs[$startreg['positions']['a']] == 'am')) {
403                                 if ($regs[$startreg['positions']['h']] == 12) {
404                                         $regs[$startreg['positions']['H']] = 0;
405                                 }
406                         }
407                 }
408                 if (!empty($startreg['positions']['H']) && !empty($regs[$startreg['positions']['H']]) && !isset($startreg['positions']['h']) && strpos($endFormat, 'h') > -1) {
409                         $startreg['positions']['h'] = sizeof($startreg['positions']) + 1;
410                         $regs[$startreg['positions']['h']] = $regs[$startreg['positions']['H']];
411                         if (!isset($startreg['positions']['A'])) {
412                                 $startreg['positions']['A'] = sizeof($startreg['positions']) + 1;
413                                 $regs[$startreg['positions']['A']] = 'AM';
414                         }
415                         if (!isset($startreg['positions']['a'])) {
416                                 $startreg['positions']['a'] = sizeof($startreg['positions']) + 1;
417                                 $regs[$startreg['positions']['a']] = 'am';
418                         }
419                         if ($regs[$startreg['positions']['H']] > 11) {
420                                 $regs[$startreg['positions']['h']] = $regs[$startreg['positions']['H']] - 12;
421                                 if ($regs[$startreg['positions']['h']] == 0) {
422                                         $regs[$startreg['positions']['h']] = 12;
423                                 }
424                                 $regs[$startreg['positions']['a']] = 'pm';
425                                 $regs[$startreg['positions']['A']] = 'PM';
426                         }
427                         if ($regs[$startreg['positions']['H']] == 0) {
428                                 $regs[$startreg['positions']['h']] = 12;
429                                 $regs[$startreg['positions']['a']] = 'am';
430                                 $regs[$startreg['positions']['A']] = 'AM';
431                         }
432                 }
433                 if (!empty($startreg['positions']['h'])) {
434                         if (!isset($regs[$startreg['positions']['h']])) {
435                                 $regs[$startreg['positions']['h']] = '12';
436                         } else if (strlen($regs[$startreg['positions']['h']]) < 2)
437                                 $regs[$startreg['positions']['h']] = '0'.$regs[$startreg['positions']['h']];
438                 }
439                 if (!empty($startreg['positions']['H'])) {
440                         // if no hour is set or it is equal to 0, set it explicitly to "00"
441                         if (empty($regs[$startreg['positions']['H']])) {
442                                 $regs[$startreg['positions']['H']] = '00';
443                         } else if (strlen($regs[$startreg['positions']['H']]) < 2)
444                                 $regs[$startreg['positions']['H']] = '0'.$regs[$startreg['positions']['H']];
445                 }
446                 if (!empty($startreg['positions']['d'])) {
447                         if (!isset($regs[$startreg['positions']['d']])) {
448                                 $regs[$startreg['positions']['d']] = '01';
449                         } else if (strlen($regs[$startreg['positions']['d']]) < 2)
450                                 $regs[$startreg['positions']['d']] = '0'.$regs[$startreg['positions']['d']];
451                 }
452                 if (!empty($startreg['positions']['i'])) {
453                         // if no minute is set or it is equal to 0, set it explicitly to "00"
454                         if (empty($regs[$startreg['positions']['i']])) {
455                                 $regs[$startreg['positions']['i']] = '00';
456                         } else if (strlen($regs[$startreg['positions']['i']]) < 2)
457                                 $regs[$startreg['positions']['i']] = '0'.$regs[$startreg['positions']['i']];
458                 } else {
459                         $startreg['positions']['i'] = count($startreg['positions']) + 1;
460                         $regs[$startreg['positions']['i']] = '00';
461
462                 }
463                 if (!empty($startreg['positions']['m'])) {
464                         if (!isset($regs[$startreg['positions']['m']])) {
465                                 $regs[$startreg['positions']['m']] = '01';
466                         } elseif(strlen($regs[$startreg['positions']['m']]) < 2)
467                                 $regs[$startreg['positions']['m']] = '0'.$regs[$startreg['positions']['m']];
468                 }
469                 if (!empty($startreg['positions']['Y'])) {
470                         if (!isset($regs[$startreg['positions']['Y']])) {
471                                 $regs[$startreg['positions']['Y']] = '2000';
472                         }
473                 }
474                 if (!empty($startreg['positions']['s'])) {
475                         if (!isset($regs[$startreg['positions']['s']])) {
476                                 $regs[$startreg['positions']['s']] = '00';
477                         } else if (strlen($regs[$startreg['positions']['s']]) < 2)
478                                 $regs[$startreg['positions']['s']] = '0'.$regs[$startreg['positions']['s']];
479                 } else {
480                         $startreg['positions']['s'] = sizeof($startreg['positions']) + 1;
481                         $regs[$startreg['positions']['s']] = '00';
482                 }
483                 foreach($startreg['positions'] as $key=>$val) {
484                         if (isset($regs[$val])) {
485                                 $newDate = str_replace($key, $regs[$val], $newDate);
486                         }
487                 }
488                 return $newDate;
489
490         }
491         /**
492          * Converts DB time string to local time string
493          *
494          * TZ conversion depends on offset parameter
495          *
496          * @param string $date Time in DB format
497          * @param bool $meridiem
498          * @param bool $offset Perform TZ conversion?
499          * @return string Time in user-defined format
500          */
501         function to_display_time($date, $meridiem = true, $offset = true) {
502                 $date = trim($date);
503                 if (empty($date)) {
504                         return $date;
505                 }
506                 if ($offset) {
507                         $date = $this->handle_offset($date, $this->get_db_date_time_format(), true);
508                 }
509                 return $this->to_display($date, $this->dbTimeFormat, $this->get_time_format($meridiem));
510         }
511
512         /**
513          * Converts DB date string to local date string
514          *
515          * TZ conversion depens on offset parameter
516          *
517          * @param string $date Date in DB format
518          * @param bool $use_offset Perform TZ conversion?
519          * @return string Date in user-defined format
520          */
521         function to_display_date($date, $use_offset = true) {
522                 $date = trim($date);
523                 if (empty($date)) {
524                         return $date;
525                 }
526                 if ($use_offset)
527                          $date = $this->handle_offset($date, $this->get_db_date_time_format(), true);
528
529                 return $this->to_display($date, $this->dbDayFormat, $this->get_date_format());
530         }
531
532         /**
533          * Convert DB datetime to local datetime
534          *
535          * TZ conversion is controlled by $offset
536          *
537          * @param string $date Original date in DB format
538          * @param bool $meridiem
539          * @param bool $offset Perform TZ conversion?
540          * @param User $user User owning the conversion formats
541          * @return string Date in display format
542          */
543         function to_display_date_time($date, $meridiem = true, $offset = true, $user = null) {
544                 $date = trim($date);
545
546                 if (empty($date)) {
547                         return $date;
548                 }
549
550                 if($this->allow_cache) {
551                         $args = array(
552                             'time' => $date,
553                             'meridiem' => $meridiem,
554                             'offset' => $offset,
555                             'user' => is_null($user)?null:$user->id,
556                         );
557
558                         // todo use __METHOD__ once PHP5 minimum verison is required
559                         $cache_key = md5('TimeDate::to_display_date_time_' . serialize($args));
560                         $cached_value = sugar_cache_retrieve($cache_key);
561                         if (!is_null($cached_value)) {
562                             return $cached_value;
563                         }
564                 }
565
566                 if ($offset) {
567                         $date = $this->handle_offset($date, $this->get_db_date_time_format(), true, $user);
568                 }
569
570                 $return_value = $this->to_display($date, $this->get_db_date_time_format(), $this->get_date_time_format($meridiem, $user));
571
572                 if($this->allow_cache) {
573                         sugar_cache_put($cache_key, $return_value);
574                 }
575                 return $return_value;
576         }
577
578         /**
579          * Convert date from format to format
580          *
581          * No TZ conversion is performed!
582          *
583          * @param string $date
584          * @param string $fromformat Source format
585          * @param string $toformat Target format
586          * @return string Converted date
587          */
588         function to_display($date, $fromformat, $toformat) {
589                 $date = trim($date);
590                 if (empty($date)) {
591                         return $date;
592                 }
593                 return $this->swap_formats($date, $fromformat, $toformat);
594         }
595
596         /**
597          * Convert date from local datetime to GMT-based DB datetime
598          *
599          * Includes TZ conversion.
600          *
601          * @param string $date
602          * @return string Datetime in DB format
603          */
604         public function to_db($date) {
605                 $date = trim($date);
606                 if (empty($date)) {
607                         return $date;
608                 }
609                 if (strlen($date) <= 10) {
610                         $date = $this->merge_date_time($date, $this->get_default_midnight());
611                 }
612
613                 $date = $this->swap_formats($date, $this->get_date_time_format(), $this->get_db_date_time_format());
614                 return $this->handle_offset($date, $this->get_db_date_time_format(), false, $GLOBALS['current_user']);
615         }
616
617
618         /*
619          * @todo This should return the raw text to be included within a <script> tag.
620          *         Having this display it's own <script> keeps it from being able to be embedded
621          *         in another Javascript file to allow for better caching
622          */
623         /**
624          * Get Javascript variables setup for user date format validation
625          * @todo: move to separate utility class?
626          *
627          * @return string JS code
628          */
629         function get_javascript_validation() {
630                 $cal_date_format = $this->get_cal_date_format();
631                 $timereg = $this->get_regular_expression($this->get_time_format());
632                 $datereg = $this->get_regular_expression($this->get_date_format());
633                 $date_pos = '';
634                 foreach($datereg['positions'] as $type=>$pos) {
635                         if (empty($date_pos)) {
636                                 $date_pos.= "'$type': $pos";
637                         } else {
638                                 $date_pos.= ",'$type': $pos";
639                         }
640
641                 }
642                 $date_pos = '{'.$date_pos.'}';
643                 if (preg_match('/\)([^\d])\(/', $timereg['format'], $match)) {
644                         $time_separator = $match[1];
645                 } else {
646                         $time_separator = ":";
647                 }
648                 $hour_offset = $this->get_hour_offset() * 60 * 60;
649
650         // Add in the number formatting styles here as well, we have been handling this with individual modules.
651         require_once('modules/Currencies/Currency.php');
652         list($num_grp_sep, $dec_sep) = get_number_seperators();
653
654                 $the_script = "<script type=\"text/javascript\">\n"
655                         ."\tvar time_reg_format = '".$timereg['format']."';\n"
656                         ."\tvar date_reg_format = '".$datereg['format']."';\n"
657                         ."\tvar date_reg_positions = $date_pos;\n"
658                         ."\tvar time_separator = '$time_separator';\n"
659                         ."\tvar cal_date_format = '$cal_date_format';\n"
660                         ."\tvar time_offset = $hour_offset;\n"
661             ."\tvar num_grp_sep = '$num_grp_sep';\n"
662             ."\tvar dec_sep = '$dec_sep';\n"
663                         ."</script>";
664
665                 return $the_script;
666
667         }
668
669         /**
670          * Convert local datetime to DB date
671          *
672          * TZ conversion depends on $use_offset. If false, only format conversion is performed.
673          *
674          * @param string $date Local date
675          * @param bool $use_offset Should time and TZ be taken into account?
676          * @return string Date in DB format
677          */
678         public function to_db_date($date, $use_offset = true) {
679                 $date = trim($date);
680                 if (empty($date)) {
681                         return $date;
682                 }
683         if (!$use_offset && preg_match('/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/',$date) ) {
684             // Already in system format
685             return $date;
686         }
687                 if ($use_offset) {
688                         $date = $this->to_db($date);
689                         $date = $this->swap_formats($date, $this->dbDayFormat, $this->dbDayFormat);
690                 } else {
691                         $date = $this->swap_formats($date, $this->get_date_format(), $this->dbDayFormat);
692                 }
693
694                 return $date;
695         }
696
697         /**
698          * Convert local datetime to DB time
699          *
700          * TZ conversion depends on $use_offset. If false, only format conversion is performed.
701          *
702          * @param string $date Local date
703          * @param bool $use_offset Should time and TZ be taken into account?
704          * @return string Time in DB format
705          */
706         public function to_db_time($date, $use_offset = true) {
707                 $date = trim($date);
708                 if (empty($date)) {
709                         return $date;
710                 }
711                 if ($use_offset){
712                         $date =$this->to_db($date, $use_offset);
713                         $date = $this->swap_formats($date, $this->get_db_date_time_format(), $this->dbTimeFormat);
714                 }else{
715                         $date = $this->swap_formats($date, $this->get_time_format(), $this->dbTimeFormat);
716                 }
717                 return $date;
718
719
720         }
721
722         /**
723          * Takes a Date & Time value in local format and converts them to DB format
724          * No TZ conversion!
725          *
726          * @param string $date
727          * @param string $time
728          * @return array Date & time in DB format
729          **/
730         public function to_db_date_time($date, $time) {
731                 global $current_user;
732                 if(is_object($current_user)) {
733                         $timeFormat = $current_user->getUserDateTimePreferences();
734                 } else {
735                         $timeFormat['date'] = $this->dbDayFormat;
736                         $timeFormat['time'] = $this->dbTimeFormat;
737                 }
738                 $dt = '';
739                 $newDate = '';
740                 $retDateTime = array();
741
742                 // concat: ('.' breaks strtotime())
743                 $time = str_replace('.',':',$time);
744                 $dt = $date.' '.$time;
745         if ( preg_match('/^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}$/',$dt) ) {
746             // Already in system time format
747             return array($date, $time);
748         }
749                 $newDate = $this->swap_formats($dt, $timeFormat['date'].' '.$timeFormat['time'] , $this->dbDateTimeFormat);
750                 return $this->split_date_time($newDate);
751         }
752
753         /**
754      * @deprecated for public use
755          * Get DST offset between user & event
756          * @param $user_in_dst
757          * @param $event_in_dst
758          */
759         function getUserEventOffset($user_in_dst, $event_in_dst){
760                 if($user_in_dst && !$event_in_dst ){
761                         return -3600;
762                 }
763                 if(!$user_in_dst && $event_in_dst ){
764                         return 3600;
765                 }
766                 return 0;
767         }
768
769 /**************************************************************
770 U       S       E       Time    GMT     Delta Server Client     U/E     Delta Server GMT
771 USER IN LA and server in NY
772 D       D       D       12      19      -3      0       -4
773 D       D       S       12      20      -3      -1      -4
774 D       S       D       12      19      -2      0       -5
775 D       S       S       12      20      -2      -1      -5
776 S       D       D       12      19      -4      1       -4
777 S       D       S       12      20      -4      0       -4
778 S       S       D       12      19      -3      1       -5
779 S       S       S       12      20      -3      0       -5
780
781
782 User in LA and server in gmt there are no DST for server
783 D       S       D       12      19      -7      0       0
784 D       S       S       12      20      -7      -1      0
785
786 S       S       D       12      19      -8      1       0
787 S       S       S       12      20      -8      0       0
788
789 ***************************************************************/
790
791         /**
792          * handles offset values for Timezones and DST
793          * @param       $date        string             date/time formatted in user's selected format
794          * @param       $format      string             destination format value as passed to PHP's date() funtion
795          * @param       $to                  boolean
796          * @param       $user        object             user object from which Timezone and DST
797      * @param   $usetimezone string             timezone name as it appears in timezones.php values will be derived
798          * @return       string         date formatted and adjusted for TZ and DST
799          */
800         function handle_offset($date, $format, $to = true, $user = null, $usetimezone = null) {
801                 global $sugar_config;
802                 $date = trim($date);
803                 // Samir Gandhi
804                 // This has been commented out because it is going through the wrong code path
805                 // Email module was broken and thats why its commented
806                 //if($this->use_old_gmt()){
807                         //return $this->handle_offset_depricated($date, $format, $to);
808                 //}
809                 if (empty($date)) {
810                         return $date;
811                 }
812                 if($this->allow_cache) {
813                         $args = array(
814                             'date' => $date,
815                             'format' => $format,
816                             'to' => $to,
817                             'user' => is_null($user)?null:$user->id,
818                             'usetimezone' => $usetimezone,
819                         );
820                         $cache_key = md5('TimeDate::handle_offset_' . serialize($args));
821                         $cached_result = sugar_cache_retrieve($cache_key);
822                         if (!is_null($cached_result)) {
823                             return $cached_result;
824                         }
825                 }
826                 if (strtotime($date) == -1) {
827                         return $date;
828                 }
829                 $deltaServerGMT = date('Z');
830
831                 if ( !empty($usetimezone) )
832                         $timezone = $GLOBALS['timezones'][$usetimezone];
833                 else
834                         $timezone = $this->getUserTimeZone($user);
835                 $deltaServerUser = $this->get_hour_offset($to, $timezone);
836                 $event_in_ds = $this->inDST($date,$timezone );
837                 $user_in_ds = $this->inDST(date('Y-m-d H:i:s'),$timezone );
838                 $server_in_ds = date('I');
839                 $ue = $this->getUserEventOffset($user_in_ds, $event_in_ds);
840                 $zone = 1;
841                 if (!$to) {
842                         $zone = -1;
843                 }
844                 $result = date($format, strtotime($date) + $deltaServerUser * 3600 + ($ue + $deltaServerGMT) * $zone);
845                 if($this->allow_cache) {
846                         sugar_cache_put($cache_key, $result);
847                 }
848                 return $result;
849         }
850
851         /**
852      * @deprecated for public use
853          */
854         function use_old_gmt()
855         {
856                 if(isset($_SESSION['GMTO'])){
857                         return $_SESSION['GMTO'];
858                 }
859                 $db = DBManagerFactory::getInstance();
860                 $fix_name = 'DST Fix';
861                 $result =$db->query("Select * from  versions  where  name='$fix_name'");
862                 $valid = $db->fetchByAssoc($result);
863                 if($valid){
864                         $_SESSION['GMTO'] = false;
865                 }else{
866                         $_SESSION['GMTO'] = true;
867                 }
868                 return $_SESSION['GMTO'];
869         }
870
871         /**
872      * @deprecated for public use
873          *This function is depricated don't use it. It is only for backwards compatibility until the admin runs the upgrade script
874          *
875          * @param unknown_type $date
876          * @param unknown_type $format
877          * @param unknown_type $to
878          * @return unknown
879          */
880         private function handle_offset_depricated($date, $format, $to = true)
881         {
882                 $date = trim($date);
883                 if (empty($date)) {
884                         return $date;
885                 }
886                 if (strtotime($date) == -1) {
887                         return $date;
888                 }
889                 $zone = date('Z');
890                 if (!$to) {
891                         $zone *= -1;
892                 }
893                 return date($format, strtotime($date) + $this->get_hour_offset($to) * 60 * 60 + $zone);
894         }
895
896         /**
897          *      this method will take an input $date variable (expecting Y-m-d format)
898          *      and get the GMT equivalent - with an hour-level granularity :
899          *      return the max value of a given locale's
900          *      date+time in GMT metrics (i.e., if in PDT, "2005-01-01 23:59:59" would be
901          *      "2005-01-02 06:59:59" in GMT metrics)
902          */
903         function handleOffsetMax($date, $format = '', $to = true)
904         {
905                 global $current_user;
906                 $gmtDateTime = array($date); // for errors
907                 /* check for bad date formatting */
908                 $date = trim($date);
909
910                 if (empty($date)) {
911                         return $gmtDateTime;
912                 }
913
914                 if (strtotime($date) == -1) {
915                         return $gmtDateTime;
916                 }
917
918                 /*      cn: passed $date var will be a "MAX" value, which we need to return
919                         as a GMT date/time pair to provide for hour-level granularity */
920                 /* this ridiculousness b/c PHP returns current time when passing "today"
921                         or "tomorrow" as strtotime() args */
922                 $dateNoTime = date('Y-m-d', strtotime($date));
923
924                 /* handle timezone and daylight savings */
925                 $dateWithTimeMin = $dateNoTime.' 00:00:00';
926                 $dateWithTimeMax = $dateNoTime.' 23:59:59';
927
928                 $offsetDateMin = $this->handle_offset($dateWithTimeMin, $this->dbDateTimeFormat, false);
929                 $offsetDateMax = $this->handle_offset($dateWithTimeMax, $this->dbDateTimeFormat, false);
930
931
932                 $exOffsetDateMax = $this->split_date_time($offsetDateMax);
933                 $gmtDateTime['date'] = $exOffsetDateMax[0];
934                 $gmtDateTime['time'] = $exOffsetDateMax[1];
935                 $gmtDateTime['min'] = $offsetDateMin;
936                 $gmtDateTime['max'] = $offsetDateMax;
937
938                 return $gmtDateTime;
939         }
940
941
942         /**
943          * Get current GMT datetime in DB format
944          * @return string
945          */
946         function get_gmt_db_datetime() {
947                 return gmdate($this->get_db_date_time_format());
948         }
949
950         /**
951          * Get current GMT date in DB format
952          * @return string
953          */
954         function get_gmt_db_date() {
955                 return gmdate($this->get_db_date_format());
956         }
957
958         /*
959          * @deprecated for public use
960          * Convert time in strtotime format into Y-m-d H:i:s format
961          */
962         function convert_to_gmt_datetime($olddatetime) {
963                 if (!empty($olddatetime)) {
964                         return date('Y-m-d H:i:s', strtotime($olddatetime) - date('Z'));
965                 }
966         }
967
968         /**
969          * makes one datetime string from date string and time string
970          *
971          * @param string $date
972          * @param string $time
973          * @return string Datetime string
974          */
975         function merge_date_time($date, $time) {
976                 return $date.' '.$time;
977         }
978
979         /**
980          * Merge time without am/pm with am/pm string
981          *
982          * @param string $date
983          * @param string $format User time format
984          * @param string $mer
985          * @return string
986          */
987         function merge_time_meridiem($date, $format, $mer) {
988                 $date = trim($date);
989                 if (empty($date)) {
990                         return $date;
991                 }
992                 $fakeMerFormat = str_replace(array('a', 'A'), array('!@!', '!@!'), $format);
993                 $noMerFormat = str_replace(array('a', 'A'), array('', ''), $format);
994                 $newDate = $this->swap_formats($date, $noMerFormat, $fakeMerFormat);
995                 return str_replace('!@!', $mer, $newDate);
996         }
997         
998         /**
999          * Returns the time portion of a datetime string
1000          *
1001          * @param string $datetime
1002          * @return string
1003          */
1004         public function getTimePart($datetime)
1005         {
1006             return trim(array_pop($this->split_date_time($datetime)));
1007         }
1008         
1009         /**
1010          * Returns the date portion of a datetime string
1011          *
1012          * @param string $datetime
1013          * @return string
1014          */
1015         public function getDatePart($datetime)
1016         {
1017             return trim(array_shift($this->split_date_time($datetime)));
1018         }
1019
1020         /**
1021          * @deprecated for public use
1022          * AMPMMenu
1023          * This method renders a <select> HTML form element based on the
1024          * user's time format preferences, with give date's value highlighted.
1025          *
1026          * If user's prefs have no AM/PM string, returns empty string.
1027          *
1028          * @todo: There is hardcoded HTML in here that does not allow for localization
1029          * of the AM/PM am/pm Strings in this drop down menu.  Also, perhaps
1030          * change to the substr_count function calls to strpos
1031          * @todo: move to separate utility class?
1032          *
1033          * @param string $prefix Prefix for SELECT
1034          * @param string $date Date in display format
1035          * @param string $attrs Additional attributes for SELECT
1036          * @return string SELECT HTML
1037          */
1038         function AMPMMenu($prefix, $date, $attrs = '') {
1039
1040                 if (substr_count($this->get_time_format(), 'a') == 0 && substr_count($this->get_time_format(), 'A') == 0) {
1041                         return '';
1042                 }
1043                 $menu = "<select name='".$prefix."meridiem' ".$attrs.">";
1044
1045                 if (strpos($this->get_time_format(), 'a') > -1) {
1046
1047                         if (substr_count($date, 'am') > 0)
1048                                 $menu.= "<option value='am' selected>am";
1049                         else
1050                                 $menu.= "<option value='am'>am";
1051                         if (substr_count($date, 'pm') > 0)
1052                                 $menu.= "<option value='pm' selected>pm";
1053                         else
1054                                 $menu.= "<option value='pm'>pm";
1055
1056                 } else {
1057
1058                         if (substr_count($date, 'AM') > 0)
1059                                 $menu.= "<option value='AM' selected>AM";
1060                         else
1061                                 $menu.= "<option value='AM'>AM";
1062                         if (substr_count($date, 'PM') > 0) {
1063                                 $menu.= "<option value='PM' selected>PM";
1064                         } else
1065                                 $menu.= "<option value='PM'>PM";
1066
1067                 }
1068
1069                 return $menu.'</select>';
1070         }
1071
1072         /**
1073      * @deprecated for public use
1074          * Get timezone offset in hours between server and timezone
1075          *
1076          * @param bool $to To this timezone or from this timezone?
1077          * @param
1078          * @return float
1079          */
1080         function get_hour_offset($to = true, $timezone = null) {
1081                 $timeDelta = $this->adjustmentForUserTimeZone($timezone) /60.0;
1082                 if ($to) {
1083                         return -1.0 * $timeDelta;
1084                 }
1085                 return 1.0 * $timeDelta;
1086         }
1087
1088         /**
1089          * Get user-defined time format
1090          *
1091          * @param bool $meridiem Should we allow AM/PM?
1092          * @param User $user
1093          * @return string
1094          */
1095         function get_time_format($meridiem = true, $user = null) {
1096                 global $current_user;
1097                 global $sugar_config;
1098
1099                 if(empty($user)) $user = $current_user;
1100
1101                 if ($user instanceof User && $user->getPreference('timef')) {
1102                         $timeFormat = $user->getPreference('timef');
1103                 } else {
1104                         $timeFormat = $sugar_config['default_time_format'];
1105                 }
1106                 if (!$meridiem) {
1107                         $timeFormat = str_replace(array('a', 'A'), array('', ''), $timeFormat);
1108                 }
1109                 return $timeFormat;
1110         }
1111
1112         /**
1113          * Get user-defined date format
1114          *
1115          * @param User $user
1116          * @return string
1117          */
1118         public function get_date_format($user = null) {
1119                 global $current_user;
1120                 global $sugar_config;
1121
1122                 if(empty($user)) $user = $current_user;
1123
1124                 if ($user instanceof User && $user->getPreference('datef')) {
1125                         return $user->getPreference('datef');
1126                 }
1127                 if(!empty($sugar_config['default_date_format'])){
1128                         return $sugar_config['default_date_format'];
1129                 }else{
1130                         return '';
1131                 }
1132         }
1133
1134         /**
1135          * Get user-defined datetime format
1136          *
1137          * @param bool $meridiem Should we allow AM/PM?
1138          * @param User $user
1139          * @return string
1140          */
1141         public function get_date_time_format($meridiem = true, $user = null) {
1142                 return $this->merge_date_time($this->get_date_format($user), $this->get_time_format($meridiem, $user));
1143         }
1144
1145         public function get_db_date_time_format() {
1146                 return $this->merge_date_time($this->dbDayFormat, $this->dbTimeFormat);
1147         }
1148
1149         public function get_db_date_format()
1150         {
1151                 return $this->dbDayFormat;
1152         }
1153
1154         public function get_db_time_format()
1155         {
1156                 return $this->dbTimeFormat;
1157         }
1158
1159         /**
1160          * Get user date format it strftime terms
1161          */
1162         function get_cal_date_format() {
1163                 return str_replace(
1164                         array('Y',  'm',  'd'),
1165                         array('%Y', '%m', '%d'),
1166                         $this->get_date_format());
1167         }
1168
1169         /**
1170          * Get user time format it strftime terms
1171          */
1172         function get_cal_time_format() {
1173                 return str_replace(
1174                         array('a',  'A',  'h',  'H',  'i',  's'),
1175                         array('%P', '%p', '%I', '%H', '%M', '%S'),
1176                         $this->get_time_format());
1177         }
1178
1179         /**
1180          * Get user date & time format it strftime terms
1181          */
1182         function get_cal_date_time_format() {
1183                 return $this->merge_date_time($this->get_cal_date_format(), $this->get_cal_time_format());
1184         }
1185
1186         /**
1187          * Return current date format for user display
1188          *
1189          * Displays format as an example for the user (i.e. something like dd/mm/yyyy)
1190          */
1191         function get_user_date_format() {
1192                 return str_replace(
1193                         array('Y',    'm',  'd'),
1194                         array('yyyy', 'mm', 'dd'),
1195                         $this->get_date_format());
1196         }
1197
1198         /**
1199          * Return current time format for user display (e.g. as example under select box)
1200          * FIXME: should we be using current format as get_user_date_format() or config?
1201          */
1202         function get_user_time_format() {
1203                 global $sugar_config;
1204                 $time_pref = $this->get_time_format();
1205
1206                 if(!empty($time_pref) && !empty($sugar_config['time_formats'][$time_pref])) {
1207                    return $sugar_config['time_formats'][$time_pref];
1208                 }
1209
1210                 return '23:00'; //default
1211                 /*
1212                 // Commented out by Collin (doesn't seem to work properly)
1213                 return $this->to_display_time('23:00:00', true, false);
1214                 */
1215         }
1216
1217         /**
1218          * @deprecated for public use
1219          * @todo: move to separate utility class?
1220          */
1221         function get_microtime_string() {
1222                 return sugar_microtime();
1223         }
1224
1225         /**
1226          * Get midnight (start of the day) in local time format
1227          *
1228          * @param bool $refresh Should cached value be discarded?
1229          * @return Time string
1230          */
1231         function get_default_midnight($refresh = false)
1232         {
1233                 if (is_null($this->default_midnight) || !$this->allow_cache || $refresh) {
1234                         $time_mapping = array(
1235                                 'H' => '00',
1236                                 'h' => '12',
1237                                 'i' => '00',
1238                                 's' => '00',
1239                                 'a' => 'am',
1240                                 'A' => 'AM',
1241                         );
1242                         $this->default_midnight = str_replace(
1243                                 array_keys($time_mapping),
1244                                 $time_mapping,
1245                                 $this->get_time_format()
1246                         );
1247                 }
1248                 return $this->default_midnight;
1249         }
1250
1251         /*
1252          * Splits datetime string into date & time parts
1253          * @param string $date Datetime string
1254          * @return array date,time
1255          */
1256         protected function split_date_time($date)
1257         {
1258                 return explode(' ', $date);
1259         }
1260
1261    /**
1262      * Returns start and end of a certain local date in GMT
1263      * Example: for May 19 in PDT start would be 2010-05-19 07:00:00, end would be 2010-05-20 06:59:59
1264      * @param string $date Date in user format
1265      * @return array Start & end date in start, end
1266      */
1267     public function getDayStartEndGMT($date)
1268     {
1269                 $datetime = $this->split_date_time($date);
1270
1271                 $dates = $this->handleOffsetMax($this->to_db_date($datetime[0], false));
1272
1273         $result['start'] = $dates['min'];
1274         $result['end'] = $dates['max'];
1275
1276         return $result;
1277     }
1278
1279 }