@@ -878,6 +878,8 @@ public static function DAYS360($startDate = 0, $endDate = 0, $method = false)
878
878
*
879
879
* Excel Function:
880
880
* YEARFRAC(startDate,endDate[,method])
881
+ * See https://lists.oasis-open.org/archives/office-formula/200806/msg00039.html
882
+ * for description of algorithm used in Excel
881
883
*
882
884
* @category Date/Time Functions
883
885
*
@@ -906,6 +908,11 @@ public static function YEARFRAC($startDate = 0, $endDate = 0, $method = 0)
906
908
if (is_string ($ endDate = self ::getDateValue ($ endDate ))) {
907
909
return Functions::VALUE ();
908
910
}
911
+ if ($ startDate > $ endDate ) {
912
+ $ temp = $ startDate ;
913
+ $ startDate = $ endDate ;
914
+ $ endDate = $ temp ;
915
+ }
909
916
910
917
if (((is_numeric ($ method )) && (!is_string ($ method ))) || ($ method == '' )) {
911
918
switch ($ method ) {
@@ -916,46 +923,43 @@ public static function YEARFRAC($startDate = 0, $endDate = 0, $method = 0)
916
923
$ startYear = self ::YEAR ($ startDate );
917
924
$ endYear = self ::YEAR ($ endDate );
918
925
$ years = $ endYear - $ startYear + 1 ;
919
- $ leapDays = 0 ;
926
+ $ startMonth = self ::MONTHOFYEAR ($ startDate );
927
+ $ startDay = self ::DAYOFMONTH ($ startDate );
928
+ $ endMonth = self ::MONTHOFYEAR ($ endDate );
929
+ $ endDay = self ::DAYOFMONTH ($ endDate );
930
+ $ startMonthDay = 100 * $ startMonth + $ startDay ;
931
+ $ endMonthDay = 100 * $ endMonth + $ endDay ;
920
932
if ($ years == 1 ) {
921
933
if (self ::isLeapYear ($ endYear )) {
922
- $ startMonth = self ::MONTHOFYEAR ($ startDate );
923
- $ endMonth = self ::MONTHOFYEAR ($ endDate );
924
- $ endDay = self ::DAYOFMONTH ($ endDate );
925
- if (($ startMonth < 3 ) ||
926
- (($ endMonth * 100 + $ endDay ) >= (2 * 100 + 29 ))) {
927
- $ leapDays += 1 ;
928
- }
934
+ $ tmpCalcAnnualBasis = 366 ;
935
+ } else {
936
+ $ tmpCalcAnnualBasis = 365 ;
929
937
}
930
- } else {
931
- for ($ year = $ startYear ; $ year <= $ endYear ; ++$ year ) {
932
- if ($ year == $ startYear ) {
933
- $ startMonth = self ::MONTHOFYEAR ($ startDate );
934
- $ startDay = self ::DAYOFMONTH ($ startDate );
935
- if ($ startMonth < 3 ) {
936
- $ leapDays += (self ::isLeapYear ($ year )) ? 1 : 0 ;
937
- }
938
- } elseif ($ year == $ endYear ) {
939
- $ endMonth = self ::MONTHOFYEAR ($ endDate );
940
- $ endDay = self ::DAYOFMONTH ($ endDate );
941
- if (($ endMonth * 100 + $ endDay ) >= (2 * 100 + 29 )) {
942
- $ leapDays += (self ::isLeapYear ($ year )) ? 1 : 0 ;
943
- }
938
+ } elseif ($ years == 2 && $ startMonthDay >= $ endMonthDay ) {
939
+ if (self ::isLeapYear ($ startYear )) {
940
+ if ($ startMonthDay <= 229 ) {
941
+ $ tmpCalcAnnualBasis = 366 ;
944
942
} else {
945
- $ leapDays += ( self :: isLeapYear ( $ year )) ? 1 : 0 ;
943
+ $ tmpCalcAnnualBasis = 365 ;
946
944
}
947
- }
948
- if ($ years == 2 ) {
949
- if (($ leapDays == 0 ) && (self ::isLeapYear ($ startYear )) && ($ days > 365 )) {
950
- $ leapDays = 1 ;
951
- } elseif ($ days < 366 ) {
952
- $ years = 1 ;
945
+ } elseif (self ::isLeapYear ($ endYear )) {
946
+ if ($ endMonthDay >= 229 ) {
947
+ $ tmpCalcAnnualBasis = 366 ;
948
+ } else {
949
+ $ tmpCalcAnnualBasis = 365 ;
953
950
}
951
+ } else {
952
+ $ tmpCalcAnnualBasis = 365 ;
954
953
}
955
- $ leapDays /= $ years ;
954
+ } else {
955
+ $ tmpCalcAnnualBasis = 0 ;
956
+ for ($ year = $ startYear ; $ year <= $ endYear ; ++$ year ) {
957
+ $ tmpCalcAnnualBasis += self ::isLeapYear ($ year ) ? 366 : 365 ;
958
+ }
959
+ $ tmpCalcAnnualBasis /= $ years ;
956
960
}
957
961
958
- return $ days / ( 365 + $ leapDays ) ;
962
+ return $ days / $ tmpCalcAnnualBasis ;
959
963
case 2 :
960
964
return self ::DATEDIF ($ startDate , $ endDate ) / 360 ;
961
965
case 3 :
@@ -1273,6 +1277,36 @@ public static function WEEKDAY($dateValue = 1, $style = 1)
1273
1277
return $ DoW ;
1274
1278
}
1275
1279
1280
+ const STARTWEEK_SUNDAY = 1 ;
1281
+ const STARTWEEK_MONDAY = 2 ;
1282
+ const STARTWEEK_MONDAY_ALT = 11 ;
1283
+ const STARTWEEK_TUESDAY = 12 ;
1284
+ const STARTWEEK_WEDNESDAY = 13 ;
1285
+ const STARTWEEK_THURSDAY = 14 ;
1286
+ const STARTWEEK_FRIDAY = 15 ;
1287
+ const STARTWEEK_SATURDAY = 16 ;
1288
+ const STARTWEEK_SUNDAY_ALT = 17 ;
1289
+ const DOW_SUNDAY = 1 ;
1290
+ const DOW_MONDAY = 2 ;
1291
+ const DOW_TUESDAY = 3 ;
1292
+ const DOW_WEDNESDAY = 4 ;
1293
+ const DOW_THURSDAY = 5 ;
1294
+ const DOW_FRIDAY = 6 ;
1295
+ const DOW_SATURDAY = 7 ;
1296
+ const STARTWEEK_MONDAY_ISO = 21 ;
1297
+ const METHODARR = [
1298
+ self ::STARTWEEK_SUNDAY => self ::DOW_SUNDAY ,
1299
+ self ::DOW_MONDAY ,
1300
+ self ::STARTWEEK_MONDAY_ALT => self ::DOW_MONDAY ,
1301
+ self ::DOW_TUESDAY ,
1302
+ self ::DOW_WEDNESDAY ,
1303
+ self ::DOW_THURSDAY ,
1304
+ self ::DOW_FRIDAY ,
1305
+ self ::DOW_SATURDAY ,
1306
+ self ::DOW_SUNDAY ,
1307
+ self ::STARTWEEK_MONDAY_ISO => self ::STARTWEEK_MONDAY_ISO ,
1308
+ ];
1309
+
1276
1310
/**
1277
1311
* WEEKNUM.
1278
1312
*
@@ -1291,41 +1325,51 @@ public static function WEEKDAY($dateValue = 1, $style = 1)
1291
1325
* @param int $method Week begins on Sunday or Monday
1292
1326
* 1 or omitted Week begins on Sunday.
1293
1327
* 2 Week begins on Monday.
1328
+ * 11 Week begins on Monday.
1329
+ * 12 Week begins on Tuesday.
1330
+ * 13 Week begins on Wednesday.
1331
+ * 14 Week begins on Thursday.
1332
+ * 15 Week begins on Friday.
1333
+ * 16 Week begins on Saturday.
1334
+ * 17 Week begins on Sunday.
1335
+ * 21 ISO (Jan. 4 is week 1, begins on Monday).
1294
1336
*
1295
1337
* @return int|string Week Number
1296
1338
*/
1297
- public static function WEEKNUM ($ dateValue = 1 , $ method = 1 )
1339
+ public static function WEEKNUM ($ dateValue = 1 , $ method = self :: STARTWEEK_SUNDAY )
1298
1340
{
1299
1341
$ dateValue = Functions::flattenSingleValue ($ dateValue );
1300
1342
$ method = Functions::flattenSingleValue ($ method );
1301
1343
1302
1344
if (!is_numeric ($ method )) {
1303
1345
return Functions::VALUE ();
1304
- } elseif (($ method < 1 ) || ($ method > 2 )) {
1305
- return Functions::NAN ();
1306
1346
}
1307
- $ method = floor ($ method );
1347
+ $ method = (int ) $ method ;
1348
+ if (!array_key_exists ($ method , self ::METHODARR )) {
1349
+ return Functions::NaN ();
1350
+ }
1351
+ $ method = self ::METHODARR [$ method ];
1308
1352
1309
- if ($ dateValue === null ) {
1310
- $ dateValue = 1 ;
1311
- } elseif (is_string ($ dateValue = self ::getDateValue ($ dateValue ))) {
1353
+ $ dateValue = self ::getDateValue ($ dateValue );
1354
+ if (is_string ($ dateValue )) {
1312
1355
return Functions::VALUE ();
1313
- } elseif ($ dateValue < 0.0 ) {
1356
+ }
1357
+ if ($ dateValue < 0.0 ) {
1314
1358
return Functions::NAN ();
1315
1359
}
1316
1360
1317
1361
// Execute function
1318
1362
$ PHPDateObject = Date::excelToDateTimeObject ($ dateValue );
1363
+ if ($ method == self ::STARTWEEK_MONDAY_ISO ) {
1364
+ return (int ) $ PHPDateObject ->format ('W ' );
1365
+ }
1319
1366
$ dayOfYear = $ PHPDateObject ->format ('z ' );
1320
1367
$ PHPDateObject ->modify ('- ' . $ dayOfYear . ' days ' );
1321
1368
$ firstDayOfFirstWeek = $ PHPDateObject ->format ('w ' );
1322
1369
$ daysInFirstWeek = (6 - $ firstDayOfFirstWeek + $ method ) % 7 ;
1323
- $ interval = $ dayOfYear - $ daysInFirstWeek ;
1324
- $ weekOfYear = floor ($ interval / 7 ) + 1 ;
1325
-
1326
- if ($ daysInFirstWeek ) {
1327
- ++$ weekOfYear ;
1328
- }
1370
+ $ daysInFirstWeek += 7 * !$ daysInFirstWeek ;
1371
+ $ endFirstWeek = $ daysInFirstWeek - 1 ;
1372
+ $ weekOfYear = floor (($ dayOfYear - $ endFirstWeek + 13 ) / 7 );
1329
1373
1330
1374
return (int ) $ weekOfYear ;
1331
1375
}
0 commit comments