Passed
Branch develop (1f83e2)
by
unknown
30:06
created

getGMTEasterDatetime()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 4
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 5
rs 10
1
<?php
2
/* Copyright (C) 2004-2011 Laurent Destailleur  <[email protected]>
3
 * Copyright (C) 2005-2011 Regis Houssin        <[email protected]>
4
 * Copyright (C) 2011-2015 Juanjo Menent        <[email protected]>
5
 * Copyright (C) 2017      Ferran Marcet        <[email protected]>
6
 * Copyright (C) 2018      Charlene Benke       <[email protected]>
7
*
8
 * This program is free software; you can redistribute it and/or modify
9
 * it under the terms of the GNU General Public License as published by
10
 * the Free Software Foundation; either version 3 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20
 * or see https://www.gnu.org/
21
 */
22
23
/**
24
 *  \file		htdocs/core/lib/date.lib.php
25
 *  \brief		Set of function to manipulate dates
26
 */
27
28
29
/**
30
 *  Return an array with timezone values
31
 *
32
 *  @return     array   Array with timezone values
33
 */
34
function get_tz_array()
35
{
36
	$tzarray = array(
37
		-11=>"Pacific/Midway",
38
		-10=>"Pacific/Fakaofo",
39
		-9=>"America/Anchorage",
40
		-8=>"America/Los_Angeles",
41
		-7=>"America/Dawson_Creek",
42
		-6=>"America/Chicago",
43
		-5=>"America/Bogota",
44
		-4=>"America/Anguilla",
45
		-3=>"America/Araguaina",
46
		-2=>"America/Noronha",
47
		-1=>"Atlantic/Azores",
48
		0=>"Africa/Abidjan",
49
		1=>"Europe/Paris",
50
		2=>"Europe/Helsinki",
51
		3=>"Europe/Moscow",
52
		4=>"Asia/Dubai",
53
		5=>"Asia/Karachi",
54
		6=>"Indian/Chagos",
55
		7=>"Asia/Jakarta",
56
		8=>"Asia/Hong_Kong",
57
		9=>"Asia/Tokyo",
58
		10=>"Australia/Sydney",
59
		11=>"Pacific/Noumea",
60
		12=>"Pacific/Auckland",
61
		13=>"Pacific/Enderbury"
62
	);
63
	return $tzarray;
64
}
65
66
67
/**
68
 * Return server timezone string
69
 *
70
 * @return string			PHP server timezone string ('Europe/Paris')
71
 */
72
function getServerTimeZoneString()
73
{
74
	return @date_default_timezone_get();
75
}
76
77
/**
78
 * Return server timezone int.
79
 *
80
 * @param	string	$refgmtdate		Reference period for timezone (timezone differs on winter and summer. May be 'now', 'winter' or 'summer')
81
 * @return 	float					An offset in hour (+1 for Europe/Paris on winter and +2 for Europe/Paris on summer). Note some countries use half and even quarter hours.
82
 */
83
function getServerTimeZoneInt($refgmtdate = 'now')
84
{
85
	if (method_exists('DateTimeZone', 'getOffset'))
86
	{
87
		// Method 1 (include daylight)
88
		$gmtnow = dol_now('gmt'); $yearref = dol_print_date($gmtnow, '%Y'); $monthref = dol_print_date($gmtnow, '%m'); $dayref = dol_print_date($gmtnow, '%d');
89
		if ($refgmtdate == 'now') $newrefgmtdate = $yearref.'-'.$monthref.'-'.$dayref;
90
		elseif ($refgmtdate == 'summer') $newrefgmtdate = $yearref.'-08-01';
91
		else $newrefgmtdate = $yearref.'-01-01';
92
		$newrefgmtdate .= 'T00:00:00+00:00';
93
		$localtz = new DateTimeZone(getServerTimeZoneString());
94
		$localdt = new DateTime($newrefgmtdate, $localtz);
95
		$tmp = -1 * $localtz->getOffset($localdt);
96
		//print $refgmtdate.'='.$tmp;
97
	} else {
98
		$tmp = 0;
99
		dol_print_error('', 'PHP version must be 5.3+');
100
	}
101
	$tz = round(($tmp < 0 ? 1 : -1) * abs($tmp / 3600));
102
	return $tz;
103
}
104
105
106
/**
107
 *  Add a delay to a date
108
 *
109
 *  @param      int			$time               Date timestamp (or string with format YYYY-MM-DD)
110
 *  @param      int			$duration_value     Value of delay to add
111
 *  @param      int			$duration_unit      Unit of added delay (d, m, y, w, h, i)
112
 *  @return     int      			        	New timestamp
113
 */
114
function dol_time_plus_duree($time, $duration_value, $duration_unit)
115
{
116
	global $conf;
117
118
	if ($duration_value == 0)  return $time;
119
	if ($duration_unit == 'i') return $time + (60 * $duration_value);
120
	if ($duration_unit == 'h') return $time + (3600 * $duration_value);
121
	if ($duration_unit == 'w') return $time + (3600 * 24 * 7 * $duration_value);
122
123
	$deltastring = 'P';
124
125
	if ($duration_value > 0) { $deltastring .= abs($duration_value); $sub = false; }
126
	if ($duration_value < 0) { $deltastring .= abs($duration_value); $sub = true; }
127
	if ($duration_unit == 'd') { $deltastring .= "D"; }
128
	if ($duration_unit == 'm') { $deltastring .= "M"; }
129
	if ($duration_unit == 'y') { $deltastring .= "Y"; }
130
131
	$date = new DateTime();
132
	if (!empty($conf->global->MAIN_DATE_IN_MEMORY_ARE_GMT)) $date->setTimezone(new DateTimeZone('UTC'));
133
	$date->setTimestamp($time);
134
	$interval = new DateInterval($deltastring);
135
136
	if ($sub) $date->sub($interval);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $sub does not seem to be defined for all execution paths leading up to this point.
Loading history...
137
	else $date->add($interval);
138
139
	return $date->getTimestamp();
140
}
141
142
143
/**
144
 * Convert hours and minutes into seconds
145
 *
146
 * @param      int		$iHours     	Hours
147
 * @param      int		$iMinutes   	Minutes
148
 * @param      int		$iSeconds   	Seconds
149
 * @return     int						Time into seconds
150
 * @see convertSecondToTime()
151
 */
152
function convertTime2Seconds($iHours = 0, $iMinutes = 0, $iSeconds = 0)
153
{
154
	$iResult = ($iHours * 3600) + ($iMinutes * 60) + $iSeconds;
155
	return $iResult;
156
}
157
158
159
/**	  	Return, in clear text, value of a number of seconds in days, hours and minutes.
160
 *      Can be used to show a duration.
161
 *
162
 *    	@param      int		$iSecond		Number of seconds
163
 *    	@param      string	$format		    Output format ('all': total delay days hour:min like "2 days 12:30",
164
 *                                          - 'allwithouthour': total delay days without hour part like "2 days",
165
 *                                          - 'allhourmin': total delay with format hours:min like "60:30",
166
 *                                          - 'allhourminsec': total delay with format hours:min:sec like "60:30:10",
167
 *                                          - 'allhour': total delay hours without min/sec like "60:30",
168
 *                                          - 'fullhour': total delay hour decimal like "60.5" for 60:30,
169
 *                                          - 'hour': only hours part "12",
170
 *                                          - 'min': only minutes part "30",
171
 *                                          - 'sec': only seconds part,
172
 *                                          - 'month': only month part,
173
 *                                          - 'year': only year part);
174
 *      @param      int		$lengthOfDay    Length of day (default 86400 seconds for 1 day, 28800 for 8 hour)
175
 *      @param      int		$lengthOfWeek   Length of week (default 7)
176
 *    	@return     string		 		 	Formated text of duration
177
 * 	                                		Example: 0 return 00:00, 3600 return 1:00, 86400 return 1d, 90000 return 1 Day 01:00
178
 *      @see convertTime2Seconds()
179
 */
180
function convertSecondToTime($iSecond, $format = 'all', $lengthOfDay = 86400, $lengthOfWeek = 7)
181
{
182
	global $langs;
183
184
	if (empty($lengthOfDay))  $lengthOfDay = 86400; // 1 day = 24 hours
185
	if (empty($lengthOfWeek)) $lengthOfWeek = 7; // 1 week = 7 days
186
187
	if ($format == 'all' || $format == 'allwithouthour' || $format == 'allhour' || $format == 'allhourmin' || $format == 'allhourminsec')
188
	{
189
		if ((int) $iSecond === 0) return '0'; // This is to avoid having 0 return a 12:00 AM for en_US
190
191
		$sTime = '';
192
		$sDay = 0;
193
		$sWeek = 0;
194
195
		if ($iSecond >= $lengthOfDay)
196
		{
197
			for ($i = $iSecond; $i >= $lengthOfDay; $i -= $lengthOfDay)
198
			{
199
				$sDay++;
200
				$iSecond -= $lengthOfDay;
201
			}
202
			$dayTranslate = $langs->trans("Day");
203
			if ($iSecond >= ($lengthOfDay * 2)) $dayTranslate = $langs->trans("Days");
204
		}
205
206
		if ($lengthOfWeek < 7)
207
		{
208
			if ($sDay)
209
			{
210
				if ($sDay >= $lengthOfWeek)
211
				{
212
					$sWeek = (int) (($sDay - $sDay % $lengthOfWeek) / $lengthOfWeek);
213
					$sDay = $sDay % $lengthOfWeek;
214
					$weekTranslate = $langs->trans("DurationWeek");
215
					if ($sWeek >= 2) $weekTranslate = $langs->trans("DurationWeeks");
216
					$sTime .= $sWeek.' '.$weekTranslate.' ';
217
				}
218
			}
219
		}
220
		if ($sDay > 0)
221
		{
222
			$dayTranslate = $langs->trans("Day");
223
			if ($sDay > 1) $dayTranslate = $langs->trans("Days");
224
			$sTime .= $sDay.' '.$dayTranslate.' ';
225
		}
226
227
		if ($format == 'all')
228
		{
229
			if ($iSecond || empty($sDay))
230
			{
231
				$sTime .= dol_print_date($iSecond, 'hourduration', true);
232
			}
233
		} elseif ($format == 'allhourminsec')
234
		{
235
			return sprintf("%02d", ($sWeek * $lengthOfWeek * 24 + $sDay * 24 + (int) floor($iSecond / 3600))).':'.sprintf("%02d", ((int) floor(($iSecond % 3600) / 60))).':'.sprintf("%02d", ((int) ($iSecond % 60)));
236
		} elseif ($format == 'allhourmin')
237
		{
238
			return sprintf("%02d", ($sWeek * $lengthOfWeek * 24 + $sDay * 24 + (int) floor($iSecond / 3600))).':'.sprintf("%02d", ((int) floor(($iSecond % 3600) / 60)));
239
		} elseif ($format == 'allhour')
240
		{
241
			return sprintf("%02d", ($sWeek * $lengthOfWeek * 24 + $sDay * 24 + (int) floor($iSecond / 3600)));
242
		}
243
	} elseif ($format == 'hour')	// only hour part
244
	{
245
		$sTime = dol_print_date($iSecond, '%H', true);
246
	} elseif ($format == 'fullhour')
247
	{
248
		if (!empty($iSecond)) {
249
			$iSecond = $iSecond / 3600;
250
		} else {
251
			$iSecond = 0;
252
		}
253
		$sTime = $iSecond;
254
	} elseif ($format == 'min')	// only min part
255
	{
256
		$sTime = dol_print_date($iSecond, '%M', true);
257
	} elseif ($format == 'sec')	// only sec part
258
	{
259
		$sTime = dol_print_date($iSecond, '%S', true);
260
	} elseif ($format == 'month')	// only month part
261
	{
262
		$sTime = dol_print_date($iSecond, '%m', true);
263
	} elseif ($format == 'year')	// only year part
264
	{
265
		$sTime = dol_print_date($iSecond, '%Y', true);
266
	}
267
	return trim($sTime);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $sTime does not seem to be defined for all execution paths leading up to this point.
Loading history...
268
}
269
270
271
/**
272
 * Generate a SQL string to make a filter into a range (for second of date until last second of date)
273
 *
274
 * @param      string	$datefield			Name of SQL field where apply sql date filter
275
 * @param      int		$day_date			Day date
276
 * @param      int		$month_date			Month date
277
 * @param      int		$year_date			Year date
278
 * @param	   int      $excludefirstand	Exclude first and
279
 * @return     string	$sqldate			String with SQL filter
280
 */
281
function dolSqlDateFilter($datefield, $day_date, $month_date, $year_date, $excludefirstand = 0)
282
{
283
	global $db;
284
	$sqldate = "";
285
	if ($month_date > 0) {
286
		if ($year_date > 0 && empty($day_date)) {
287
			$sqldate .= ($excludefirstand ? "" : " AND ").$datefield." BETWEEN '".$db->idate(dol_get_first_day($year_date, $month_date, false));
288
			$sqldate .= "' AND '".$db->idate(dol_get_last_day($year_date, $month_date, false))."'";
289
		} elseif ($year_date > 0 && !empty($day_date)) {
290
			$sqldate .= ($excludefirstand ? "" : " AND ").$datefield." BETWEEN '".$db->idate(dol_mktime(0, 0, 0, $month_date, $day_date, $year_date));
291
			$sqldate .= "' AND '".$db->idate(dol_mktime(23, 59, 59, $month_date, $day_date, $year_date))."'";
292
		} else $sqldate .= ($excludefirstand ? "" : " AND ")." date_format( ".$datefield.", '%c') = '".$db->escape($month_date)."'";
293
	} elseif ($year_date > 0) {
294
		$sqldate .= ($excludefirstand ? "" : " AND ").$datefield." BETWEEN '".$db->idate(dol_get_first_day($year_date, 1, false));
295
		$sqldate .= "' AND '".$db->idate(dol_get_last_day($year_date, 12, false))."'";
296
	}
297
	return $sqldate;
298
}
299
300
/**
301
 *	Convert a string date into a GM Timestamps date
302
 *	Warning: YYYY-MM-DDTHH:MM:SS+02:00 (RFC3339) is not supported. If parameter gm is 1, we will use no TZ, if not we will use TZ of server, not the one inside string.
303
 *
304
 *	@param	string	$string		Date in a string
305
 *				     	        YYYYMMDD
306
 *	                 			YYYYMMDDHHMMSS
307
 *								YYYYMMDDTHHMMSSZ
308
 *								YYYY-MM-DDTHH:MM:SSZ (RFC3339)
309
 *		                		DD/MM/YY or DD/MM/YYYY (deprecated)
310
 *		                		DD/MM/YY HH:MM:SS or DD/MM/YYYY HH:MM:SS (deprecated)
311
 *  @param	int		$gm         1 =Input date is GM date,
312
 *                              0 =Input date is local date using PHP server timezone
313
 *  @return	int					Date as a timestamp
314
 *		                		19700101020000 -> 7200 with gm=1
315
 *								19700101000000 -> 0 with gm=1
316
 *
317
 *  @see    dol_print_date(), dol_mktime(), dol_getdate()
318
 */
319
function dol_stringtotime($string, $gm = 1)
320
{
321
	$reg = array();
322
	// Convert date with format DD/MM/YYY HH:MM:SS. This part of code should not be used.
323
	if (preg_match('/^([0-9]+)\/([0-9]+)\/([0-9]+)\s?([0-9]+)?:?([0-9]+)?:?([0-9]+)?/i', $string, $reg))
324
	{
325
		dol_syslog("dol_stringtotime call to function with deprecated parameter format", LOG_WARNING);
326
		// Date est au format 'DD/MM/YY' ou 'DD/MM/YY HH:MM:SS'
327
		// Date est au format 'DD/MM/YYYY' ou 'DD/MM/YYYY HH:MM:SS'
328
		$sday = $reg[1];
329
		$smonth = $reg[2];
330
		$syear = $reg[3];
331
		$shour = $reg[4];
332
		$smin = $reg[5];
333
		$ssec = $reg[6];
334
		if ($syear < 50) $syear += 1900;
335
		if ($syear >= 50 && $syear < 100) $syear += 2000;
336
		$string = sprintf("%04d%02d%02d%02d%02d%02d", $syear, $smonth, $sday, $shour, $smin, $ssec);
337
	} elseif (
338
		   preg_match('/^([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})Z$/i', $string, $reg)	// Convert date with format YYYY-MM-DDTHH:MM:SSZ (RFC3339)
339
		|| preg_match('/^([0-9]{4})-([0-9]{2})-([0-9]{2}) ([0-9]{2}):([0-9]{2}):([0-9]{2})$/i', $string, $reg)	// Convert date with format YYYY-MM-DD HH:MM:SS
340
   		|| preg_match('/^([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2})([0-9]{2})([0-9]{2})Z$/i', $string, $reg)		// Convert date with format YYYYMMDDTHHMMSSZ
341
	)
342
	{
343
		$syear = $reg[1];
344
		$smonth = $reg[2];
345
		$sday = $reg[3];
346
		$shour = $reg[4];
347
		$smin = $reg[5];
348
		$ssec = $reg[6];
349
		$string = sprintf("%04d%02d%02d%02d%02d%02d", $syear, $smonth, $sday, $shour, $smin, $ssec);
350
	}
351
352
	$string = preg_replace('/([^0-9])/i', '', $string);
353
	$tmp = $string.'000000';
354
	$date = dol_mktime(substr($tmp, 8, 2), substr($tmp, 10, 2), substr($tmp, 12, 2), substr($tmp, 4, 2), substr($tmp, 6, 2), substr($tmp, 0, 4), ($gm ? 1 : 0));
355
	return $date;
356
}
357
358
359
/** Return previous day
360
 *
361
 *  @param      int			$day     	Day
362
 *  @param      int			$month   	Month
363
 *  @param      int			$year    	Year
364
 *  @return     array   				Previous year,month,day
365
 */
366
function dol_get_prev_day($day, $month, $year)
367
{
368
	$time = dol_mktime(12, 0, 0, $month, $day, $year, 1, 0);
369
	$time -= 24 * 60 * 60;
370
	$tmparray = dol_getdate($time, true);
371
	return array('year' => $tmparray['year'], 'month' => $tmparray['mon'], 'day' => $tmparray['mday']);
372
}
373
374
/** Return next day
375
 *
376
 *  @param      int			$day    	Day
377
 *  @param      int			$month  	Month
378
 *  @param      int			$year   	Year
379
 *  @return     array   				Next year,month,day
380
 */
381
function dol_get_next_day($day, $month, $year)
382
{
383
	$time = dol_mktime(12, 0, 0, $month, $day, $year, 1, 0);
384
	$time += 24 * 60 * 60;
385
	$tmparray = dol_getdate($time, true);
386
	return array('year' => $tmparray['year'], 'month' => $tmparray['mon'], 'day' => $tmparray['mday']);
387
}
388
389
/**	Return previous month
390
 *
391
 *	@param		int			$month		Month
392
 *	@param		int			$year		Year
393
 *	@return		array					Previous year,month
394
 */
395
function dol_get_prev_month($month, $year)
396
{
397
	if ($month == 1)
398
	{
399
		$prev_month = 12;
400
		$prev_year  = $year - 1;
401
	} else {
402
		$prev_month = $month - 1;
403
		$prev_year  = $year;
404
	}
405
	return array('year' => $prev_year, 'month' => $prev_month);
406
}
407
408
/**	Return next month
409
 *
410
 *	@param		int			$month		Month
411
 *	@param		int			$year		Year
412
 *	@return		array					Next year,month
413
 */
414
function dol_get_next_month($month, $year)
415
{
416
	if ($month == 12)
417
	{
418
		$next_month = 1;
419
		$next_year  = $year + 1;
420
	} else {
421
		$next_month = $month + 1;
422
		$next_year  = $year;
423
	}
424
	return array('year' => $next_year, 'month' => $next_month);
425
}
426
427
/**	Return previous week
428
 *
429
 *  @param      int			$day     	Day
430
 *  @param      int			$week    	Week
431
 *  @param      int			$month   	Month
432
 *	@param		int			$year		Year
433
 *	@return		array					Previous year,month,day
434
 */
435
function dol_get_prev_week($day, $week, $month, $year)
436
{
437
	$tmparray = dol_get_first_day_week($day, $month, $year);
438
439
	$time = dol_mktime(12, 0, 0, $month, $tmparray['first_day'], $year, 1, 0);
440
	$time -= 24 * 60 * 60 * 7;
441
	$tmparray = dol_getdate($time, true);
442
	return array('year' => $tmparray['year'], 'month' => $tmparray['mon'], 'day' => $tmparray['mday']);
443
}
444
445
/**	Return next week
446
 *
447
 *  @param      int			$day     	Day
448
 *  @param      int			$week    	Week
449
 *  @param      int			$month   	Month
450
 *	@param		int			$year		Year
451
 *	@return		array					Next year,month,day
452
 */
453
function dol_get_next_week($day, $week, $month, $year)
454
{
455
	$tmparray = dol_get_first_day_week($day, $month, $year);
456
457
	$time = dol_mktime(12, 0, 0, $tmparray['first_month'], $tmparray['first_day'], $tmparray['first_year'], 1, 0);
458
	$time += 24 * 60 * 60 * 7;
459
	$tmparray = dol_getdate($time, true);
460
461
	return array('year' => $tmparray['year'], 'month' => $tmparray['mon'], 'day' => $tmparray['mday']);
462
}
463
464
/**	Return GMT time for first day of a month or year
465
 *
466
 *	@param		int			$year		Year
467
 * 	@param		int			$month		Month
468
 * 	@param		mixed		$gm			False or 0 or 'server' = Return date to compare with server TZ, True or 1 to compare with GM date.
469
 *                          			Exemple: dol_get_first_day(1970,1,false) will return -3600 with TZ+1, a dol_print_date on it will return 1970-01-01 00:00:00
470
 *                          			Exemple: dol_get_first_day(1970,1,true) will return 0 whatever is TZ, a dol_print_date on it will return 1970-01-01 00:00:00
471
 *  @return		int						Date for first day, '' if error
472
 */
473
function dol_get_first_day($year, $month = 1, $gm = false)
474
{
475
	if ($year > 9999) return '';
476
	return dol_mktime(0, 0, 0, $month, 1, $year, $gm);
477
}
478
479
480
/**	Return GMT time for last day of a month or year
481
 *
482
 *	@param		int			$year		Year
483
 * 	@param		int			$month		Month
484
 * 	@param		boolean		$gm			False or 0 or 'server' = Return date to compare with server TZ, True or 1 to compare with GM date.
485
 *	@return		int						Date for first day, '' if error
486
 */
487
function dol_get_last_day($year, $month = 12, $gm = false)
488
{
489
	if ($year > 9999) return '';
490
	if ($month == 12)
491
	{
492
		$month = 1;
493
		$year += 1;
494
	} else {
495
		$month += 1;
496
	}
497
498
	// On se deplace au debut du mois suivant, et on retire un jour
499
	$datelim = dol_mktime(23, 59, 59, $month, 1, $year, $gm);
500
	$datelim -= (3600 * 24);
501
502
	return $datelim;
503
}
504
505
/**	Return GMT time for last hour of a given GMT date (it removes hours, min and second part)
506
 *
507
 *	@param		int			$date		Date
508
 *  @return		int						Date for last hour of a given date
509
 */
510
function dol_get_last_hour($date)
511
{
512
	$tmparray = dol_getdate($date);
513
	return dol_mktime(23, 59, 59, $tmparray['mon'], $tmparray['mday'], $tmparray['year'], false);
514
}
515
516
/**	Return GMT time for first hour of a given GMT date (it removes hours, min and second part)
517
 *
518
 *	@param		int			$date		Date
519
 *  @return		int						Date for last hour of a given date
520
 */
521
function dol_get_first_hour($date)
522
{
523
	$tmparray = dol_getdate($date);
524
	return dol_mktime(0, 0, 0, $tmparray['mon'], $tmparray['mday'], $tmparray['year'], false);
525
}
526
527
/**	Return first day of week for a date. First day of week may be monday if option MAIN_START_WEEK is 1.
528
 *
529
 *	@param		int		$day		Day
530
 * 	@param		int		$month		Month
531
 *  @param		int		$year		Year
532
 * 	@param		int		$gm			False or 0 or 'server' = Return date to compare with server TZ, True or 1 to compare with GM date.
533
 *	@return		array				year,month,week,first_day,first_month,first_year,prev_day,prev_month,prev_year
534
 */
535
function dol_get_first_day_week($day, $month, $year, $gm = false)
536
{
537
	global $conf;
538
539
	//$day=2; $month=2; $year=2015;
540
	$date = dol_mktime(0, 0, 0, $month, $day, $year, $gm);
541
542
	//Checking conf of start week
543
	$start_week = (isset($conf->global->MAIN_START_WEEK) ? $conf->global->MAIN_START_WEEK : 1);
544
545
	$tmparray = dol_getdate($date, true); // detail of current day
546
547
	//Calculate days = offset from current day
548
	$days = $start_week - $tmparray['wday'];
549
 	if ($days >= 1) $days = 7 - $days;
550
 	$days = abs($days);
551
	$seconds = $days * 24 * 60 * 60;
552
	//print 'start_week='.$start_week.' tmparray[wday]='.$tmparray['wday'].' day offset='.$days.' seconds offset='.$seconds.'<br>';
553
554
	//Get first day of week
555
	$tmpdaytms = date($tmparray[0]) - $seconds; // $tmparray[0] is day of parameters
556
	$tmpday = date("d", $tmpdaytms);
557
558
	//Check first day of week is in same month than current day or not
559
	if ($tmpday > $day)
560
	{
561
		$prev_month = $month - 1;
562
		$prev_year = $year;
563
564
		if ($prev_month == 0)
565
		{
566
			$prev_month = 12;
567
			$prev_year  = $year - 1;
568
		}
569
	} else {
570
		$prev_month = $month;
571
		$prev_year = $year;
572
	}
573
	$tmpmonth = $prev_month;
574
	$tmpyear = $prev_year;
575
576
	//Get first day of next week
577
	$tmptime = dol_mktime(12, 0, 0, $month, $tmpday, $year, 1, 0);
578
	$tmptime -= 24 * 60 * 60 * 7;
579
	$tmparray = dol_getdate($tmptime, true);
580
	$prev_day = $tmparray['mday'];
581
582
	//Check prev day of week is in same month than first day or not
583
	if ($prev_day > $tmpday)
584
	{
585
		$prev_month = $month - 1;
586
		$prev_year = $year;
587
588
		if ($prev_month == 0)
589
		{
590
			$prev_month = 12;
591
			$prev_year  = $year - 1;
592
		}
593
	}
594
595
	$week = date("W", dol_mktime(0, 0, 0, $tmpmonth, $tmpday, $tmpyear, $gm));
596
597
	return array('year' => $year, 'month' => $month, 'week' => $week, 'first_day' => $tmpday, 'first_month' => $tmpmonth, 'first_year' => $tmpyear, 'prev_year' => $prev_year, 'prev_month' => $prev_month, 'prev_day' => $prev_day);
598
}
599
600
/**
601
 *	Return the easte day in GMT time.
602
 *  This function replaces easter_date() that returns a date in local TZ.
603
 *
604
 *	@param	    int			$year     			Year
605
 *	@return   	int								GMT Date
606
 */
607
function getGMTEasterDatetime($year) {
608
	$base = new DateTime("$year-03-21");
609
	$days = easter_days($year);	// Return number of days between 21 march and easter day.
610
	$tmp = $base->add(new DateInterval("P{$days}D"));
611
	return $tmp->getTimestamp();
612
}
613
614
/**
615
 *	Return the number of non working days including saturday and sunday (or not) between 2 dates in timestamp.
616
 *  Dates must be UTC with hour, day, min to 0.
617
 *	Called by function num_open_day()
618
 *
619
 *	@param	    int			$timestampStart     Timestamp de debut
620
 *	@param	    int			$timestampEnd       Timestamp de fin
621
 *  @param      string		$country_code       Country code
622
 *	@param      int			$lastday            Last day is included, 0: no, 1:yes
623
 *  @param		int			$includesaturday	Include saturday as non working day (-1=use setup, 0=no, 1=yes)
624
 *  @param		int			$includesunday		Include sunday as non working day (-1=use setup, 0=no, 1=yes)
625
 *	@return   	int|string						Number of non working days or error message string if error
626
 *  @see num_between_day(), num_open_day()
627
 */
628
function num_public_holiday($timestampStart, $timestampEnd, $country_code = '', $lastday = 0, $includesaturday = -1, $includesunday = -1)
629
{
630
	global $db, $conf, $mysoc;
631
632
	$nbFerie = 0;
633
634
	// Check to ensure we use correct parameters
635
	if ((($timestampEnd - $timestampStart) % 86400) != 0) return 'Error Dates must use same hours and must be GMT dates';
636
637
	if (empty($country_code)) $country_code = $mysoc->country_code;
638
639
	if ($includesaturday < 0) $includesaturday = (isset($conf->global->MAIN_NON_WORKING_DAYS_INCLUDE_SATURDAY) ? $conf->global->MAIN_NON_WORKING_DAYS_INCLUDE_SATURDAY : 1);
640
	if ($includesunday < 0)   $includesunday   = (isset($conf->global->MAIN_NON_WORKING_DAYS_INCLUDE_SUNDAY) ? $conf->global->MAIN_NON_WORKING_DAYS_INCLUDE_SUNDAY : 1);
641
642
	$country_id = dol_getIdFromCode($db, $country_code, 'c_country', 'code', 'rowid');
643
644
	$i = 0;
645
	while ((($lastday == 0 && $timestampStart < $timestampEnd) || ($lastday && $timestampStart <= $timestampEnd))
646
		&& ($i < 50000))		// Loop end when equals (Test on i is a security loop to avoid infinite loop)
647
	{
648
		$ferie = false;
649
		$specialdayrule = array();
650
651
		$jour  = gmdate("d", $timestampStart);
652
		$mois  = gmdate("m", $timestampStart);
653
		$annee = gmdate("Y", $timestampStart);
654
655
		//print "jour=".$jour." month=".$mois." year=".$annee." includesaturday=".$includesaturday." includesunday=".$includesunday."\n";
656
657
		// Loop on public holiday defined into hrm_public_holiday for the day, month and year analyzed
658
		// TODO Execute this request first and store results into an array, then reuse this array.
659
		$sql = "SELECT code, entity, fk_country, dayrule, year, month, day, active";
660
		$sql .= " FROM ".MAIN_DB_PREFIX."c_hrm_public_holiday";
661
		$sql .= " WHERE active = 1 and fk_country IN (0".($country_id > 0 ? ", ".$country_id : 0).")";
662
663
		$resql = $db->query($sql);
664
		if ($resql)
665
		{
666
			$num_rows = $db->num_rows($resql);
667
			$i = 0;
668
			while ($i < $num_rows)
669
			{
670
				$obj = $db->fetch_object($resql);
671
672
				if (!empty($obj->dayrule) && $obj->dayrule != 'date')		// For example 'easter', '...'
673
				{
674
					$specialdayrule[$obj->dayrule] = $obj->dayrule;
675
				} else {
676
					$match = 1;
677
					if (!empty($obj->year) && $obj->year != $annee) $match = 0;
678
					if ($obj->month != $mois) $match = 0;
679
					if ($obj->day != $jour) $match = 0;
680
681
					if ($match) $ferie = true;
682
				}
683
684
				$i++;
685
			}
686
		} else {
687
			dol_syslog($db->lasterror(), LOG_ERR);
688
			return 'Error sql '.$db->lasterror();
689
		}
690
		//var_dump($specialdayrule)."\n";
691
		//print "ferie=".$ferie."\n";
692
693
		if (!$ferie) {
694
			// Special dayrules
695
			if (in_array('easter', $specialdayrule))
696
			{
697
				// Calculation for easter date
698
				$date_paques = getGMTEasterDatetime($annee);
699
				$jour_paques = gmdate("d", $date_paques);
700
				$mois_paques = gmdate("m", $date_paques);
701
				if ($jour_paques == $jour && $mois_paques == $mois) $ferie = true;
702
				// Easter (sunday)
703
			}
704
705
			if (in_array('eastermonday', $specialdayrule))
706
			{
707
				// Calculation for the monday of easter date
708
				$date_paques = getGMTEasterDatetime($annee);
709
				$date_lundi_paques = mktime(
710
					gmdate("H", $date_paques),
711
					gmdate("i", $date_paques),
712
					gmdate("s", $date_paques),
713
					gmdate("m", $date_paques),
714
					gmdate("d", $date_paques) + 1,
715
					gmdate("Y", $date_paques)
716
				);
717
				$jour_lundi_paques = gmdate("d", $date_lundi_paques);
718
				$mois_lundi_paques = gmdate("m", $date_lundi_paques);
719
				if ($jour_lundi_paques == $jour && $mois_lundi_paques == $mois) $ferie = true;
720
				// Easter (monday)
721
			}
722
723
			if (in_array('ascension', $specialdayrule))
724
			{
725
				// Calcul du jour de l'ascension (39 days after easter day)
726
				$date_paques = getGMTEasterDatetime($annee);
727
				$date_ascension = mktime(
728
					gmdate("H", $date_paques),
729
					gmdate("i", $date_paques),
730
					gmdate("s", $date_paques),
731
					gmdate("m", $date_paques),
732
					gmdate("d", $date_paques) + 39,
733
					gmdate("Y", $date_paques)
734
				);
735
				$jour_ascension = gmdate("d", $date_ascension);
736
				$mois_ascension = gmdate("m", $date_ascension);
737
				if ($jour_ascension == $jour && $mois_ascension == $mois) $ferie = true;
738
				// Ascension (thursday)
739
			}
740
741
			if (in_array('pentecote', $specialdayrule))
742
			{
743
				// Calculation of "Pentecote" (49 days after easter day)
744
				$date_paques = getGMTEasterDatetime($annee);
745
				$date_pentecote = mktime(
746
					gmdate("H", $date_paques),
747
					gmdate("i", $date_paques),
748
					gmdate("s", $date_paques),
749
					gmdate("m", $date_paques),
750
					gmdate("d", $date_paques) + 49,
751
					gmdate("Y", $date_paques)
752
				);
753
				$jour_pentecote = gmdate("d", $date_pentecote);
754
				$mois_pentecote = gmdate("m", $date_pentecote);
755
				if ($jour_pentecote == $jour && $mois_pentecote == $mois) $ferie = true;
756
				// "Pentecote" (sunday)
757
			}
758
			if (in_array('pentecotemonday', $specialdayrule))
759
			{
760
				// Calculation of "Pentecote" (49 days after easter day)
761
				$date_paques = getGMTEasterDatetime($annee);
762
				$date_pentecote = mktime(
763
					gmdate("H", $date_paques),
764
					gmdate("i", $date_paques),
765
					gmdate("s", $date_paques),
766
					gmdate("m", $date_paques),
767
					gmdate("d", $date_paques) + 50,
768
					gmdate("Y", $date_paques)
769
					);
770
				$jour_pentecote = gmdate("d", $date_pentecote);
771
				$mois_pentecote = gmdate("m", $date_pentecote);
772
				if ($jour_pentecote == $jour && $mois_pentecote == $mois) $ferie = true;
773
				// "Pentecote" (monday)
774
			}
775
776
			if (in_array('viernessanto', $specialdayrule))
777
			{
778
				// Viernes Santo
779
				$date_paques = getGMTEasterDatetime($annee);
780
				$date_viernes = mktime(
781
					gmdate("H", $date_paques),
782
					gmdate("i", $date_paques),
783
					gmdate("s", $date_paques),
784
					gmdate("m", $date_paques),
785
					gmdate("d", $date_paques) - 2,
786
					gmdate("Y", $date_paques)
787
				);
788
				$jour_viernes = gmdate("d", $date_viernes);
789
				$mois_viernes = gmdate("m", $date_viernes);
790
				if ($jour_viernes == $jour && $mois_viernes == $mois) $ferie = true;
791
				//Viernes Santo
792
			}
793
794
			if (in_array('fronleichnam', $specialdayrule))
795
			{
796
				// Fronleichnam (60 days after easter sunday)
797
				$date_paques = getGMTEasterDatetime($annee);
798
				$date_fronleichnam = mktime(
799
					gmdate("H", $date_paques),
800
					gmdate("i", $date_paques),
801
					gmdate("s", $date_paques),
802
					gmdate("m", $date_paques),
803
					gmdate("d", $date_paques) + 60,
804
					gmdate("Y", $date_paques)
805
					);
806
				$jour_fronleichnam = gmdate("d", $date_fronleichnam);
807
				$mois_fronleichnam = gmdate("m", $date_fronleichnam);
808
				if ($jour_fronleichnam == $jour && $mois_fronleichnam == $mois) $ferie = true;
809
				// Fronleichnam
810
			}
811
		}
812
		//print "ferie=".$ferie."\n";
813
814
		// If we have to include saturday and sunday
815
		if (!$ferie) {
816
			if ($includesaturday || $includesunday)
817
			{
818
				$jour_julien = unixtojd($timestampStart);
819
				$jour_semaine = jddayofweek($jour_julien, 0);
820
				if ($includesaturday)					//Saturday (6) and Sunday (0)
821
				{
822
					if ($jour_semaine == 6) $ferie = true;
823
				}
824
				if ($includesunday)						//Saturday (6) and Sunday (0)
825
				{
826
					if ($jour_semaine == 0) $ferie = true;
827
				}
828
			}
829
		}
830
		//print "ferie=".$ferie."\n";
831
832
		// We increase the counter of non working day
833
		if ($ferie) $nbFerie++;
834
835
		// Increase number of days (on go up into loop)
836
		$timestampStart = dol_time_plus_duree($timestampStart, 1, 'd');
837
		//var_dump($jour.' '.$mois.' '.$annee.' '.$timestampStart);
838
839
		$i++;
840
	}
841
842
	//print "nbFerie=".$nbFerie."\n";
843
	return $nbFerie;
844
}
845
846
/**
847
 *	Function to return number of days between two dates (date must be UTC date !)
848
 *  Example: 2012-01-01 2012-01-02 => 1 if lastday=0, 2 if lastday=1
849
 *
850
 *	@param	   int			$timestampStart     Timestamp start UTC
851
 *	@param	   int			$timestampEnd       Timestamp end UTC
852
 *	@param     int			$lastday            Last day is included, 0: no, 1:yes
853
 *	@return    int								Number of days
854
 *  @seealso num_public_holiday(), num_open_day()
855
 */
856
function num_between_day($timestampStart, $timestampEnd, $lastday = 0)
857
{
858
	if ($timestampStart < $timestampEnd)
859
	{
860
		if ($lastday == 1)
861
		{
862
			$bit = 0;
863
		} else {
864
			$bit = 1;
865
		}
866
		$nbjours = (int) floor(($timestampEnd - $timestampStart) / (60 * 60 * 24)) + 1 - $bit;
867
	}
868
	//print ($timestampEnd - $timestampStart) - $lastday;
869
	return $nbjours;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $nbjours does not seem to be defined for all execution paths leading up to this point.
Loading history...
870
}
871
872
/**
873
 *	Function to return number of working days (and text of units) between two dates (working days)
874
 *
875
 *	@param	   	int			$timestampStart     Timestamp for start date (date must be UTC to avoid calculation errors)
876
 *	@param	   	int			$timestampEnd       Timestamp for end date (date must be UTC to avoid calculation errors)
877
 *	@param     	int			$inhour             0: return number of days, 1: return number of hours
878
 *	@param		int			$lastday            We include last day, 0: no, 1:yes
879
 *  @param		int			$halfday			Tag to define half day when holiday start and end
880
 *  @param      string		$country_code       Country code (company country code if not defined)
881
 *	@return    	int|string						Number of days or hours or string if error
882
 *  @seealso num_between_day(), num_public_holiday()
883
 */
884
function num_open_day($timestampStart, $timestampEnd, $inhour = 0, $lastday = 0, $halfday = 0, $country_code = '')
885
{
886
	global $langs, $mysoc;
887
888
	if (empty($country_code)) $country_code = $mysoc->country_code;
889
890
	dol_syslog('num_open_day timestampStart='.$timestampStart.' timestampEnd='.$timestampEnd.' bit='.$lastday.' country_code='.$country_code);
891
892
	// Check parameters
893
	if (!is_int($timestampStart) && !is_float($timestampStart)) return 'ErrorBadParameter_num_open_day';
894
	if (!is_int($timestampEnd) && !is_float($timestampEnd)) return 'ErrorBadParameter_num_open_day';
895
896
	//print 'num_open_day timestampStart='.$timestampStart.' timestampEnd='.$timestampEnd.' bit='.$lastday;
897
	if ($timestampStart < $timestampEnd)
898
	{
899
		$numdays = num_between_day($timestampStart, $timestampEnd, $lastday);
900
901
		$numholidays = num_public_holiday($timestampStart, $timestampEnd, $country_code, $lastday);
902
		$nbOpenDay = ($numdays - $numholidays);
903
		if ($inhour == 1 && $nbOpenDay <= 3) $nbOpenDay = ($nbOpenDay * 24);
904
		return $nbOpenDay - (($inhour == 1 ? 12 : 0.5) * abs($halfday));
905
	} elseif ($timestampStart == $timestampEnd)
906
	{
907
		$numholidays = 0;
908
		if ($lastday) {
909
			$numholidays = num_public_holiday($timestampStart, $timestampEnd, $country_code, $lastday);
910
			if ($numholidays == 1) return 0;
911
		}
912
913
		$nbOpenDay = $lastday;
914
915
		if ($inhour == 1) $nbOpenDay = ($nbOpenDay * 24);
916
		return $nbOpenDay - (($inhour == 1 ? 12 : 0.5) * abs($halfday));
917
	} else {
918
		return $langs->trans("Error");
919
	}
920
}
921
922
923
924
/**
925
 *	Return array of translated months or selected month.
926
 *  This replace old function monthArrayOrSelected.
927
 *
928
 *	@param	Translate	$outputlangs	Object langs
929
 *  @param	int			$short			0=Return long label, 1=Return short label
930
 *	@return array						Month string or array if selected < 0
931
 */
932
function monthArray($outputlangs, $short = 0)
933
{
934
	$montharray = array(
935
		1  => $outputlangs->trans("Month01"),
936
		2  => $outputlangs->trans("Month02"),
937
		3  => $outputlangs->trans("Month03"),
938
		4  => $outputlangs->trans("Month04"),
939
		5  => $outputlangs->trans("Month05"),
940
		6  => $outputlangs->trans("Month06"),
941
		7  => $outputlangs->trans("Month07"),
942
		8  => $outputlangs->trans("Month08"),
943
		9  => $outputlangs->trans("Month09"),
944
		10 => $outputlangs->trans("Month10"),
945
		11 => $outputlangs->trans("Month11"),
946
		12 => $outputlangs->trans("Month12")
947
	);
948
949
	if (!empty($short))
950
	{
951
		$montharray = array(
952
			1  => $outputlangs->trans("MonthShort01"),
953
			2  => $outputlangs->trans("MonthShort02"),
954
			3  => $outputlangs->trans("MonthShort03"),
955
			4  => $outputlangs->trans("MonthShort04"),
956
			5  => $outputlangs->trans("MonthShort05"),
957
			6  => $outputlangs->trans("MonthShort06"),
958
			7  => $outputlangs->trans("MonthShort07"),
959
			8  => $outputlangs->trans("MonthShort08"),
960
			9  => $outputlangs->trans("MonthShort09"),
961
			10 => $outputlangs->trans("MonthShort10"),
962
			11 => $outputlangs->trans("MonthShort11"),
963
			12 => $outputlangs->trans("MonthShort12")
964
			);
965
	}
966
967
	return $montharray;
968
}
969
/**
970
 *	Return array of week numbers.
971
 *
972
 *	@param	int 		$month			Month number
973
 *  @param	int			$year			Year number
974
 *	@return array						Week numbers
975
 */
976
function getWeekNumbersOfMonth($month, $year)
977
{
978
	$nb_days = cal_days_in_month(CAL_GREGORIAN, $month, $year);
979
	$TWeek = array();
980
	for ($day = 1; $day < $nb_days; $day++) {
981
		$week_number = getWeekNumber($day, $month, $year);
982
		$TWeek[$week_number] = $week_number;
983
	}
984
	return $TWeek;
985
}
986
/**
987
 *	Return array of first day of weeks.
988
 *
989
 *	@param	array 		$TWeek			array of week numbers
990
 *  @param	int			$year			Year number
991
 *	@return array						First day of week
992
 */
993
function getFirstDayOfEachWeek($TWeek, $year)
994
{
995
	$TFirstDayOfWeek = array();
996
	foreach ($TWeek as $weekNb) {
997
		if (in_array('01', $TWeek) && in_array('52', $TWeek) && $weekNb == '01') $year++; //Si on a la 1re semaine et la semaine 52 c'est qu'on change d'année
998
		$TFirstDayOfWeek[$weekNb] = date('d', strtotime($year.'W'.$weekNb));
999
	}
1000
	return $TFirstDayOfWeek;
1001
}
1002
/**
1003
 *	Return array of last day of weeks.
1004
 *
1005
 *	@param	array 		$TWeek			array of week numbers
1006
 *  @param	int			$year			Year number
1007
 *	@return array						Last day of week
1008
 */
1009
function getLastDayOfEachWeek($TWeek, $year)
1010
{
1011
	$TLastDayOfWeek = array();
1012
	foreach ($TWeek as $weekNb) {
1013
		$TLastDayOfWeek[$weekNb] = date('d', strtotime($year.'W'.$weekNb.'+6 days'));
1014
	}
1015
	return $TLastDayOfWeek;
1016
}
1017
/**
1018
 *	Return week number.
1019
 *
1020
 *	@param	int 		$day			Day number
1021
 *	@param	int 		$month			Month number
1022
 *  @param	int			$year			Year number
1023
 *	@return int							Week number
1024
 */
1025
function getWeekNumber($day, $month, $year)
1026
{
1027
	$date = new DateTime($year.'-'.$month.'-'.$day);
1028
	$week = $date->format("W");
1029
	return $week;
1030
}
1031