Files
tms/includes/func.php
TMS eb299c9131 Initial commit: Kimai TMS
Kimai time management system (PHP 5.6 / MySQL) with custom extensions:
- Budget tracking (ki_budget)
- Change request form (ki_changerequest)
- Invoice generation (ki_invoice)
- Expense tracking (ki_expenses)
- Flexi time (ki_flexitime)
- Export, admin panel, timesheets, tasks, summary

Database: ~900K time entries, 2,000+ users, 439 projects.
Config: includes/autoconf.php — localhost/kimai/kimai
2026-06-18 21:20:26 +00:00

954 lines
26 KiB
PHP

<?php
/**
* This file is part of
* Kimai - Open Source Time Tracking // http://www.kimai.org
* (c) 2006-2009 Kimai-Development-Team
*
* Kimai is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; Version 3, 29 June 2007
*
* Kimai is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Kimai; If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Functions defined here are not directly accessing the database.
*/
// Require database layer functions
if (isset($kga['server_conn']))
require("db_layer_".$kga['server_conn'].".php");
/**
* Prepare all values of the array so it's save to put them into an sql query.
* The conversion to utf8 is done here as well, if configured.
*
* @param array $data Array which values are being prepared.
* @return array The same array, except all values are being escaped correctly.
*/
function clean_data($data) {
global $kga;
foreach ($data as $key => $value) {
if ($key != "pw") {
$return[$key] = urldecode(strip_tags($data[$key]));
$return[$key] = str_replace('"','_',$data[$key]);
$return[$key] = str_replace("'",'_',$data[$key]);
$return[$key] = str_replace('\\','',$data[$key]);
} else {
$return[$key] = $data[$key];
}
if ($kga['utf8']) $return[$key] = utf8_decode($return[$key]);
}
return $return;
}
/**
* Kill the current users session by redirecting him to the logout page.
*/
function kickUser() {
die("<script type='text/javascript'>window.location.href = '../index.php?a=logout';</script>");
}
/**
* returns formatted time string -> h:mm
* input: number of seconds
*
* @param integer $sek seconds to extract the time from
* @return string
* @author th
* @deprecated use formatDuration instead
*/
function intervallApos($sek) {
if (is_array($sek)) {
$arr = array();
foreach ($sek as $key=>$value)
{
$arr[$key] = intervallApos($value);
}
return $arr;
}
else
return sprintf('%d:%02d', $sek / 3600, $sek / 60 % 60);
}
/**
* returns formatted time string -> h:mm:ss
* input: number of seconds
*
* @param integer $sek seconds to extract the time from
* @return string
* @author th
* @deprecated use formatDuration instead
*/
function intervallColon($sek) {
return sprintf('%d:%02d:%02d', $sek / 3600, $sek / 60 % 60, $sek % 60);
}
/**
* Format a duration given in seconds according to the global setting. Either
* seconds are shown or not.
*
* @param integer|array one value in seconds or an array of values in seconds
* @return integer|array depending on the $sek param which contains the formatted duration
* @author sl
*/
function formatDuration($sek) {
global $kga;
if (is_array($sek)) {
// Convert all values of the array.
$arr = array();
foreach ($sek as $key=>$value)
{
$arr[$key] = formatDuration($value);
}
return $arr;
}
else {
// Format accordingly.
if ($kga['conf']['durationWithSeconds'] == 0)
return sprintf('%d:%02d', $sek / 3600, $sek / 60 % 60);
else
return sprintf('%d:%02d:%02d', $sek / 3600, $sek / 60 % 60, $sek % 60);
}
}
/**
* Format a currency or an array of currencies accordingly.
*
* @param integer|array one value or an array of decimal numbers
* @return integer|array formatted string(s)
* @author sl
*/
function formatCurrency($number,$htmlNoWrap = true) {
global $kga;
if (is_array($number)) {
// Convert all values of the array.
$arr = array();
foreach ($number as $key=>$value)
{
$arr[$key] = formatCurrency($value);
}
return $arr;
}
else {
$value = str_replace(".", $kga['conf']['decimalSeparator'], sprintf("%01.2f",$number) );
if ($kga['conf']['currency_first'])
$value = $kga['currency_sign']." ".$value;
else
$value = $value." ".$kga['currency_sign'];
if ($htmlNoWrap)
return "<span style=\"white-space: nowrap;\">$value</span>";
else
return $value;
}
}
/**
* Format the annotations and only include data which the user wants to see.
* The array which is passed to the method will be modified.
*
* @param $ann array the annotation array (userid => (time, costs) )
*/
function formatAnnotations(&$ann) {
$type = usr_get_preference('ui.sublistAnnotations');
$userIds = array_keys($ann);
if ($type == null)
$type = 0;
switch ($type) {
case 0:
// just time
foreach ($userIds as $userId) {
$ann[$userId] = formatDuration($ann[$userId]['time']);
}
break;
case 1:
// just costs
foreach ($userIds as $userId) {
$ann[$userId] = formatCurrency($ann[$userId]['costs']);
}
break;
case 2:
default:
// both
foreach ($userIds as $userId) {
$time = formatDuration($ann[$userId]['time']);
$costs = formatCurrency($ann[$userId]['costs']);
$ann[$userId] = "<span style=\"white-space: nowrap;\">$time |</span> $costs";
}
}
}
/**
* returns hours, minutes and seconds as array
* input: number of seconds
*
* @param integer $sek seconds to extract the time from
* @return array
* @author th
*/
function hourminsec($sek) {
$i['h'] = $sek / 3600 % 24;
$i['i'] = $sek / 60 % 60;
$i['s'] = $sek % 60;
return $i;
}
/**
* returns array of subdirectorys - needed for skin selector
*
* @param string $dir Directory to list subdirectorys from
* @return array
* @author unknown
*/
function ls($dir){
$arr_subfolders = array();
$i=0;
$handle = opendir($dir);
while (false !== ($readdir = readdir($handle))) {
if ($readdir != '.' && $readdir != '..' && substr($readdir,0,1) != '.' && is_dir("${dir}/${readdir}") ) {
$arr_subfolders[$i] = $readdir;
$i++;
}
}
return isset($arr_subfolders)?$arr_subfolders:false;
closedir($handle);
}
/**
* returns array of language files
*
* @param none
* @return array
* @author unknown/th
*/
function langs(){
$arr_files = array();
$arr_files[] = "";
$handle = opendir(WEBROOT.'/language/');
while (false !== ($readdir = readdir($handle))) {
if ($readdir != '.' && $readdir != '..' && substr($readdir,0,1) != '.'
&& endsWith($readdir,'.php') ) {
$arr_files[] = str_replace(".php", "", $readdir);
}
}
closedir($handle);
sort($arr_files);
return $arr_files;
}
/**
* Get a list of available time zones. This is directly taken from PHP.
*
* @return array of timezone names
*/
function timezoneList() {
return DateTimeZone::listIdentifiers();
}
/**
* Returns array for smarty's html_options funtion.
*
* <pre>
* returns:
* [0] -> pct/evt names
* [1] -> values as IDs
* </pre>
*
* @param string either 'pct', 'evt', 'knd', 'grp'
* @return array
* @author th, sl, kp
*/
function makeSelectBox($subject,$user,$selection=null){
global $kga;
$sel = array();
$sel[0] = array();
$sel[1] = array();
switch ($subject) {
case 'pct':
$arr_pct = get_arr_pct($user);
$i=0;
foreach ($arr_pct as $pct) {
if ($pct['pct_visible']) {
if ($kga['conf']['flip_pct_display']) {
$sel[0][$i] = $pct['knd_name'] . ": " . $pct['pct_name'];
if ($kga['conf']['pct_comment_flag']) {
$sel[0][$i] .= "(" . $pct['pct_comment'] .")" ;
}
} else {
$sel[0][$i] = $pct['pct_name'] . " (" . $pct['knd_name'] . ")";
if ($kga['conf']['pct_comment_flag']) {
$sel[0][$i] .= "(" . $pct['pct_comment'] .")";
}
}
$sel[1][$i] = $pct['pct_ID'];
$i++;
}
}
break;
case 'evt':
$arr_evt = get_arr_evt($user);
$i=0;
foreach ($arr_evt as $evt) {
if ($evt['evt_visible']) {
$sel[0][$i] = $evt['evt_name'];
$sel[1][$i] = $evt['evt_ID'];
$i++;
}
}
break;
case 'tpl':
$arr_tpl = get_arr_tpl($user);
$i=0;
foreach ($arr_tpl as $tpl) {
$sel[0][$i] = $tpl['tpl_name'];
$sel[1][$i] = $tpl['tpl_ID'];
$i++;
}
break;
case 'knd':
$arr_knd = get_arr_knd($user);
$i=0;
$selectionFound = false;
foreach ($arr_knd as $knd) {
if ($knd['knd_visible']) {
$sel[0][$i] = $knd['knd_name'];
$sel[1][$i] = $knd['knd_ID'];
$i++;
if ($selection == $knd['knd_ID'])
$selectionFound = true;
}
}
if ($selection != null && !$selectionFound) {
$data = knd_get_data($selection);
$sel[0][$i] = $data['knd_name'];
$sel[1][$i] = $data['knd_ID'];
}
break;
case 'grp':
$arr_grp = get_arr_grp();
$i=0;
foreach ($arr_grp as $grp) {
if (!$grp['grp_trash']) {
$sel[0][$i] = $grp['grp_name'];
$sel[1][$i] = $grp['grp_ID'];
$i++;
}
}
break;
default:
// TODO leave default options empty ???
break;
}
return $sel;
}
/**
* returns list of projects and their time summary within zef_entry timespace as array
*
* OLD VERSION THAT MERGES TWO QUERYS - bad bad stuff ...
* TODO: [tom] revise with join query!
*
* @param integer $group ID of group in table grp
* @param integer $user ID of user in table usr
* @param integer $in start time in unix seconds
* @param integer $out end time in unix seconds
* @global array $kga kimai-global-array
* @return array
* @author th
*/
function get_arr_pct_with_time($group,$user,$in,$out) {
global $kga;
//TODO: [tom] Functions results with 1 query
$arr_pcts = get_arr_pct($group);
$arr_time = get_arr_time_pct($user,$in,$out);
//TODO END
$arr = array();
$i=0;
foreach ($arr_pcts as $pct) {
$arr[$i]['pct_ID'] = $pct['pct_ID'];
$arr[$i]['knd_ID'] = $pct['knd_ID'];
$arr[$i]['pct_name'] = $pct['pct_name'];
$arr[$i]['pct_comment'] = $pct['pct_comment'];
$arr[$i]['knd_name'] = $pct['knd_name'];
$arr[$i]['pct_visible'] = $pct['pct_visible'];
$arr[$i]['zeit'] = @formatDuration($arr_time[$pct['pct_ID']]);
$i++;
}
return $arr;
}
/**
* returns a random code with given length
*
* @global integer $length length of the code
* @return array
* @author th
*/
function random_code($length) {
$code = "";
$string="ABCDEFGHIJKLMNPQRSTUVWXYZabcdefghijklmnpqrstuvwxyz0123456789";
mt_srand((double)microtime()*1000000);
for ($i=1; $i <= $length; $i++) {
$code .= substr($string, mt_rand(0,strlen($string)-1), 1);
}
return $code;
}
/**
* returns a random number with X digits
*
* @global integer $length digit count of number
* @return array
* @author th
*/
function random_number($length) {
$number = "";
$string="0123456789";
mt_srand((double)microtime()*1000000);
for ($i=1; $i <= $length; $i++) {
$number .= substr($string, mt_rand(0,strlen($string)-1), 1);
}
return $number;
}
/**
* checks if the database structure needs to be updated for new Kimai version.
* if yes the function redirects to /admin/updater.php
*
* @param string $path path to admin dir relative to the document that calls this function (usually "." or "..")
* @global array $kga kimai-global-array
* @return array
* @author th
*/
function checkDBversion($path) {
global $kga;
// check for versions before 0.7.13r96
$installedVersion = get_DBversion();
$checkVersion = $installedVersion[0];
$checkVersion = "$checkVersion";
if ($checkVersion != $kga['version']) {
header("Location: $path/updater.php");
exit;
}
// the check for revision is much simpler ...
if ( (int)$installedVersion[1] < (int)$kga['revision']) {
header("Location: $path/updater.php");
exit;
}
}
/**
* returns browser name
*
* returns: "Opera", "msie", "Safari", "Mozilla" or "?"
*
* TODO: [togi] check if this still is really needed anywhere
* Smarty has a browser value by itself!!!
*
*
* @return string
* @author th
*/
function get_agent() {
@$agent=$_SERVER["HTTP_USER_AGENT"];
if(strpos($agent,"opera") !== false) $browser = "Opera";
else if(strpos($agent,"msie") !== false) $browser = "msie";
else if(strpos($agent,"Safari") !== false) $browser = "Safari";
else if(strpos($agent,"mozilla") !== false) $browser = "Mozilla";
else $browser = "?";
return $browser;
}
/**
* writes errors during install or update to the logfile stored in temporary
*
* @param string $value message
* @param string $path relative path to temporary directory
* @param boolean $success
* @author th
*/
function logfile($value) {
$value = str_replace("\n", "", $value);
$value = str_replace(" ", " ", $value);
$value = str_replace(" ", " ", $value);
$logdatei=fopen(WEBROOT."temporary/logfile.txt","a");
fputs($logdatei, date("[d.m.Y H:i:s] ",time()) . $value ."\n");
fclose($logdatei);
}
/**
* preprocess shortcut for date entries
*
* allowed shortcut formats are shown in the dialogue for edit timesheet entries (click the "?")
*
* @param string $date shortcut date
* @return string
* @author th
*/
function expand_date_shortcut($date) {
$date = str_replace(" ","",$date);
// empty string can't be a time value
if (strlen($date)==0)
return false;
// get the parts
$parts = preg_split("/\./",$date);
if (count($parts) == 0 || count($parts) > 3)
return false;
// check day
if (strlen($parts[0]) == 1)
$parts[0] = "0".$parts[0];
// check month
if (!isset($parts[1]))
$parts[1] = date("m");
else if (strlen($parts[1]) == 1)
$parts[1] = "0".$parts[1];
// check year
if (!isset($parts[2]))
$parts[2] = date("Y");
else if (strlen($parts[2]) == 2) {
if ($parts[2] > 70)
$parts[2] = "19".$parts[2];
else {
if ($parts[2] < 10)
$parts[2] = "200".$parts[2];
else
$parts[2] = "20".$parts[2];
}
}
$return = implode(".",$parts);
if (!preg_match("/([0-9]{1,2})\.([0-9]{1,2})\.([0-9]{2,4})/",$return)) $return = false;
return $return;
}
/**
* preprocess shortcut for time entries
*
* allowed shortcut formats are shown in the dialogue for edit timesheet entries (click the "?")
*
* @param string $date shortcut time
* @return string
* @author th
*/
function expand_time_shortcut($time) {
$time = str_replace(" ","",$time);
// empty string can't be a time value
if (strlen($time)==0)
return false;
// get the parts
$parts = preg_split("/:|\./",$time);
for ($i=0;$i<count($parts);$i++) {
switch (strlen($parts[$i])) {
case 0:
return false;
case 1:
$parts[$i] = "0".$parts[$i];
}
}
// fill unsued parts (eg. 12:00 given but 12:00:00 is needed)
while (count($parts) < 3) {
$parts[] = "00";
}
$return = implode(":",$parts);
$regex23 = '([0-1][0-9])|(2[0-3])'; // regular expression for hours
$regex59 = '([0-5][0-9])'; // regular expression for minutes and seconds
if (!preg_match("/^($regex23):($regex59):($regex59)$/",$return)) $return = false;
return $return;
}
/**
* check if a parset string matches with the following time-formatting: 20.08.2008-19:00:00
* returns true if string is ok
*
* @param string $timestring
* @return boolean
* @author th
*/
function check_time_format($timestring) {
if (!preg_match("/([0-9]{1,2})\.([0-9]{1,2})\.([0-9]{2,4})-([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2})/",$timestring)) {
return false; // WRONG format
} else {
$ok = 1;
$hours = substr($timestring,11,2);
$minutes = substr($timestring,14,2);
$seconds = substr($timestring,17,2);
if ((int)$hours>=24) $ok=0;
if ((int)$minutes>=60) $ok=0;
if ((int)$seconds>=60) $ok=0;
logfile("timecheck: ".$ok);
$day = substr($timestring,0,2);
$month = substr($timestring,3,2);
$year = substr($timestring,6,4);
if (!checkdate( (int)$month, (int)$day, (int)$year) ) $ok=0;
logfile("time/datecheck: ".$ok);
if ($ok) {
return true;
} else {
return false;
}
}
}
function convert_time_strings($in,$out) {
$explode_in = explode("-",$in);
$explode_out = explode("-",$out);
$date_in = explode(".",$explode_in[0]);
$date_out = explode(".",$explode_out[0]);
$time_in = explode(":",$explode_in[1]);
$time_out = explode(":",$explode_out[1]);
$time['in'] = mktime($time_in[0], $time_in[1], $time_in[2], $date_in[1], $date_in[0], $date_in[2]);
$time['out'] = mktime($time_out[0],$time_out[1],$time_out[2],$date_out[1],$date_out[0],$date_out[2]);
$time['diff'] = (int)$time['out']-(int)$time['in'];
return $time;
}
/**
* returns last day (..30..31) of given month
*
* @param integer $month
* @param integer $year
* @return integer number of day
*
* @author http://lutrov.com/blog/php-last-day-of-the-month-calculation/
*/
function lastday($month = '', $year = '') {
if (empty($month)) {
$month = date('m');
}
if (empty($year)) {
$year = date('Y');
}
$result = strtotime("{$year}-{$month}-01");
$result = strtotime('-1 second', strtotime('+1 month', $result));
return date('d', $result);
}
/**
* read a cookie or return a default value, if cookie is not set
*
* @param string $cookie_name
* @param mixed $default the value, which will be returned, when the cookie is not set
* @return mixed
*
* @author rvock
*/
function get_cookie($cookie_name, $default=null) {
return isset($_COOKIE[$cookie_name]) ? $_COOKIE[$cookie_name] : $default;
}
/**
* check if there are 0s (erroneuos entries) in the zef data while editing and prevent overwriting old data in this case
*
* @param int $id the id of the entry to be edited
* @param array $zef_data
* @return boolean the return value of the actual editing function
*
* @author Oleg
*/
function check_zef_data($id, $zef_data) {
if (($zef_data['in'] == 0) && ($zef_data['out'] == 0) && ($zef_data['diff'] == 0)) {
$zef_final_data['zef_pctID'] = $zef_data['pct_ID'];
$zef_final_data['zef_evtID'] = $zef_data['evt_ID'];
$zef_final_data['zef_location'] = $zef_data['zlocation'];
$zef_final_data['zef_division'] = $zef_data['division'];
$zef_final_data['zef_trackingnr'] = $zef_data['trackingnr'];
$zef_final_data['zef_comment'] = $zef_data['comment'];
$zef_final_data['zef_comment_type'] = $zef_data['comment_type'];
$zef_final_data['zef_rate'] = $zef_data['rate'];
$zef_final_data['zef_cleared'] = $zef_data['cleared'];
return zef_edit_record($id,$zef_final_data);
} else {
$zef_final_data['zef_pctID'] = $zef_data['pct_ID'];
$zef_final_data['zef_evtID'] = $zef_data['evt_ID'];
$zef_final_data['zef_location'] = $zef_data['zlocation'];
$zef_final_data['zef_division'] = $zef_data['division'];
$zef_final_data['zef_trackingnr'] = $zef_data['trackingnr'];
$zef_final_data['zef_comment'] = $zef_data['comment'];
$zef_final_data['zef_comment_type'] = $zef_data['comment_type'];
$zef_final_data['zef_in'] = $zef_data['in'];
$zef_final_data['zef_out'] = $zef_data['out'];
$zef_final_data['zef_time'] = $zef_data['diff'];
$zef_final_data['zef_rate'] = $zef_data['rate'];
$zef_final_data['zef_cleared'] = $zef_data['cleared'];
return zef_edit_record($id,$zef_final_data);
}
}
// http://www.alfasky.com/?p=20
// This little function will help you truncate a string to a specified
// length when copying data to a place where you can only store or display
// a limited number of characters, then it will append “…” to it showing
// that some characters were removed from the original entry.
function addEllipsis($string, $length, $end='…')
{
if (strlen($string) > $length)
{
$length -= strlen($end); // $length = $length - strlen($end);
$string = substr($string, 0, $length);
$string .= $end; // $string = $string . $end;
}
return $string;
}
// based on http://wiki.jumba.com.au/wiki/PHP_Generate_random_password
function createPassword($length) {
$chars = "234567890abcdefghijkmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
$i = 0;
$password = "";
while ($i <= $length) {
$password .= $chars{mt_rand(0,strlen($chars)-1)};
$i++;
}
return $password;
}
function write_config_file($database,$hostname,$username,$password,$db_layer,$db_type,$prefix,$lang,$salt) {
$file=fopen(realpath(dirname(__FILE__)).'/autoconf.php','w');
if (!$file) return false;
$config=<<<EOD
<?php
/**
* This file is part of
* Kimai - Open Source Time Tracking // http://www.kimai.org
* (c) 2006-2009 Kimai-Development-Team
*
* Kimai is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; Version 3, 29 June 2007
*
* Kimai is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Kimai; If not, see <http://www.gnu.org/licenses/>.
*/
// This file was automatically generated by the installer
\$server_hostname = "$hostname";
\$server_database = "$database";
\$server_username = "$username";
\$server_password = "$password";
\$server_conn = "$db_layer";
\$server_type = "$db_type";
\$server_prefix = "$prefix";
\$language = "$lang";
\$password_salt = "$salt";
?>
EOD;
fputs($file, $config);
fclose($file);
return true;
}
/**
* Check if the new time values are better than the old once in the array.
*
* @param $bestTime (called by reference)
* Array containing the, until now, best time data
* @param $newStart suggestion for a better start time
* @param $newEnd suggestion for a better end time
* @param $realStart the real start time
* @param $realEnd the real end time
*/
function roundTimespanCheckIfBetter(&$bestTime,$newStart,$newEnd,$realStart,$realEnd) {
$realDuration = $realEnd-$realStart;
$newDuration = $newEnd-$newStart;
if (abs($realDuration-$newDuration) > abs($realDuration - $bestTime['duration'])) {
// new times are definitely worse, as the timespan is furher away from the real duration
return;
}
// still, this might be closer to the real time
if (abs($realStart-$newStart)+abs($realEnd-$newEnd) >= $bestTime['totalDeviation']) {
// it is not
return;
}
// new time is better, update array
$bestTime['start'] = $newStart;
$bestTime['end'] = $newEnd;
$bestTime['duration'] = $newEnd-$newStart;
$bestTime['totalDeviation'] = abs($realStart-$newStart)+abs($realEnd-$newEnd);
}
/**
* Find a beginning and end time whose timespan is as close to
* the real timepsan as possible while being a multiple of $steps (in minutes).
*
* e.g.: 16:07:31 - 17:15:16 is "rounded" to 16:00:00 - 17:15:00
* with steps set to 15
*
*@param $start the beginning of the timespan
*@param $end the end of the timespan
*@param $steps the steps in minutes (has to divide an hour, e.g. 5 is valid while 7 is not)
*
*/
function roundTimespan($start,$end,$steps) {
// calculate how long a steps is (e.g. 15 second steps are 900 seconds long)
$stepWidth=$steps*60;
if ($steps == 0) {
$bestTime = array();
$bestTime['start'] = $start;
$bestTime['end'] = $end;
return $bestTime;
}
// calculate how many seconds we are over the previous full step
$startSecondsOver = $start%$stepWidth;
$endSecondsOver = $end%$stepWidth;
// calculate earlier and later times of full step width
$earlierStart = $start-$startSecondsOver;
$earlierEnd = $end-$endSecondsOver;
$laterStart = $start+($stepWidth-$startSecondsOver);
$laterEnd = $end+($stepWidth-$endSecondsOver);
// assuming the earlier start end end time are the best (likely not always true)
$bestTime = array();
$bestTime['start'] = $earlierStart;
$bestTime['end'] = $earlierEnd;
$bestTime['duration'] = $earlierEnd-$earlierStart;
$bestTime['totalDeviation'] = abs($start-$earlierStart)+abs($end-$earlierEnd);
// check for better start and end times
roundTimespanCheckIfBetter($bestTime,$earlierStart,$laterEnd,$start,$end);
roundTimespanCheckIfBetter($bestTime,$laterStart,$earlierEnd,$start,$end);
roundTimespanCheckIfBetter($bestTime,$laterStart,$laterEnd,$start,$end);
return $bestTime;
}
/**
* get in and out unix seconds of specific user
*
* <pre>
* returns:
* [0] -> in
* [1] -> out
* </pre>
*
* @param string $user ID of user
* @global array $kga kimai-global-array
* @return array
* @author th
*/
// checked
function get_timespace() {
global $kga, $conn;
$timespace = array(null,null);
if (isset($kga['usr'])) {
$timespace[0] = $kga['usr']['timespace_in'];
$timespace[1] = $kga['usr']['timespace_out'];
}
/* database has no entries? */
$mon = date("n"); $day = date("j"); $Y = date("Y");
if (!$timespace[0]) {
$timespace[0] = mktime(0,0,0,$mon,1,$Y);
}
if (!$timespace[1]) {
$timespace[1] = mktime(23,59,59,$mon,$day,$Y);
}
return $timespace;
}
function endsWith($haystack,$needle) {
return strcmp(substr($haystack, strlen($haystack)-strlen($needle)),$needle)===0;
}
?>