Получить первую или последнюю пятницу в месяц

Я пытаюсь написать функцию календаря, подобную этой

function get_date($month, $year, $week, $day, $direction) { .... } 

$week – целое число (1, 2, 3 …), $ day – день (Sun, Mon, …) или номер, в зависимости от того, что проще. Направление немного запутанно, потому что оно делает другой расчет.

Например, давайте позвоним

get_date(5, 2009, 1, 'Sun', 'forward');

Он использует значение по умолчанию и получает первое воскресенье мая, т.е. 2009-05-03. Если мы позвоним

get_date(5, 2009, 2, 'Sun', 'backward');

, он возвращает второе последнее воскресенье мая, т.е. 2009-05-24.

Возможно, это можно сделать быстрее …
Это было очень интересно для кода.

Обратите внимание, что $direction – 1 для форварда и -1 для отставания, чтобы облегчить работу 🙂
Кроме того, $day начинается со значения 1 для понедельника и заканчивается в 7 для воскресенья.

 function get_date($month, $year, $week, $day, $direction) { if($direction > 0) $startday = 1; else $startday = date('t', mktime(0, 0, 0, $month, 1, $year)); $start = mktime(0, 0, 0, $month, $startday, $year); $weekday = date('N', $start); if($direction * $day >= $direction * $weekday) $offset = -$direction * 7; else $offset = 0; $offset += $direction * ($week * 7) + ($day - $weekday); return mktime(0, 0, 0, $month, $startday + $offset, $year); } 

Я тестировал его с помощью нескольких примеров и, кажется, всегда работал, обязательно дважды проверьте его;)

Язык-агностическая версия:

Чтобы получить первый конкретный день месяца, начните с первого дня месяца: yyyy-mm-01. Используйте любую функцию, чтобы дать число, соответствующее дню недели. Вычтите это число с того дня, которое вы ищете; например, если первый день месяца – среда (2), и вы ищете пятницу (4), вычтите 2 из 4, оставив 2. Если ответ отрицательный, добавьте 7. Наконец добавьте это к первому из месяц; для моего примера, первая пятница будет третьей.

Чтобы получить последнюю пятницу месяца, найдите первую пятницу следующего месяца и вычтите 7 дней.

strtotime () может вам помочь. например

 <?php $tsFirst = strtotime('2009-04-00 next friday'); $tsLast = strtotime('2009-05-01 last friday'); echo date(DATE_RFC850, $tsFirst), " | ", date(DATE_RFC850, $tsLast); 

печать

  Пятница, 03-апр-09 00:00:00 CEST |  Пятница, 24-апр-09 00:00:00 CEST 

Нет необходимости в вычислениях или циклах – это очень легко сделать с strtotime ():

Найдите N-го или последнего вхождения определенного дня определенного месяца:

 ///////////////////////////////////////////////////////////////// // Quick Code ///////////////////////////////////////////////////////////////// // Convenience mapping. $Names = array( 0=>"Sun", 1=>"Mon", 2=>"Tue", 3=>"Wed", 4=>"Thu", 5=>"Fri", 6=>"Sat" ); // Specify what we want // In this example, the Second Monday of Next March $tsInMonth = strtotime('March'); $Day = 1; $Ord = 2; // The actual calculations $ThisMonthTS = strtotime( date("Ym-01", $tsInMonth ) ); $NextMonthTS = strtotime( date("Ym-01", strtotime("next month", $tsInMonth) ) ); $DateOfInterest = (-1 == $Ord) ? strtotime( "last ".$Names[$Day], $NextMonthTS ) : strtotime( $Names[$Day]." + ".($Ord-1)." weeks", $ThisMonthTS ); ///////////////////////////////////////////////////////////////// // Explanation ///////////////////////////////////////////////////////////////// // Specify the month of which we are interested. // You can use any timestamp inside that month, I'm using strtotime for convenience. $tsInMonth = strtotime('March'); // The day of interest, ie: Friday. // It can be 0=Sunday through 6=Saturday (Like 'w' from date()). $Day = 5; // The occurrence of this day in which we are interested. // It can be 1, 2, 3, 4 for the first, second, third, and fourth occurrence of the day in question in the month in question. // You can also use -1 to fine the LAST occurrence. That will return the fifth occurrence if there is one, else the 4th. $Ord = 3; //////////////////////////////////////////////////////////////// // We now have all the specific values we need. // The example values above specify the 3rd friday of next march //////////////////////////////////////////////////////////////// // We need the day name that corresponds with our day number to pass to strtotime(). // This isn't really necessary = we could just specify the string in the first place, but for date calcs, you are more likely to have the day number than the string itself, so this is convenient. $Names = array( 0=>"Sun", 1=>"Mon", 2=>"Tue", 3=>"Wed", 4=>"Thu", 5=>"Fri", 6=>"Sat" ); // Calculate the timestamp at midnight of the first of the month in question. // Remember $tsInMonth is any date in that month. $ThisMonthTS = strtotime( date("Ym-01", $tsInMonth ) ); // Calculate the timestamp at midnight of the first of the FOLLOWING month. // This will be used if we specify -1 for last occurrence. $NextMonthTS = strtotime( date("Ym-01", strtotime("next month", $tsInMonth) ) ); // Now we just format the values a bit and pass them to strtotime(). // To find the 1,2,3,4th occurrence, we work from the first of the month forward. // For the last (-1) occurence,work we work back from the first occurrence of the following month. $DateOfInterest = (-1 == $Ord) ? strtotime( "last ".$Names[$Day], $NextMonthTS ) : // The last occurrence of the day in this month. Calculated as "last dayname" from the first of next month, which will be the last one in this month. strtotime( $Names[$Day]." + ".($Ord-1)." weeks", $ThisMonthTS ); // From the first of this month, move to "next dayname" which will be the first occurrence, and then move ahead a week for as many additional occurrences as you need. 

Встроенные функции времени PHP делают это простым.

http://php.net/manual/en/function.strtotime.php

 // Get first Friday of next month. $timestamp = strtotime('first fri of next month'); // Get second to last Friday of the current month. $timestamp = strtotime('last fri of this month -7 days'); // Format a timestamp as a human-meaningful string. $formattedDate = date('F j, Y', strtotime('first wed of last month')); 

Обратите внимание, что мы всегда хотим убедиться, что мы определили правильный часовой пояс для использования с strtotime чтобы PHP понимал, где вычислить временную метку относительно того, в какой часовой зоне машина думает, что она находится.

 date_default_timezone_set('America/New_York'); $formattedDate = date('F j, Y', strtotime('first wed of last month +1 week')); 
 echo date('Ym-d',strtotime('last friday')); 

Вы можете использовать mktime для получения отметки времени unix в первый день месяца:

 $firstOfMonth = mktime(0, 0, 0, $month, 1, $year); 

Когда у вас есть дата первого дня определенного месяца, легко получить день недели для этой даты, используя дату :

 $weekday = date("N", $firstOfMonth); 

Оттуда довольно просто сделать шаг вперед, чтобы получить дату, после которой вы будете.

 function get_date($month, $year, $week, $day) { # $month, $year: current month to search in # $week: 0=1st, 1=2nd, 2=3rd, 3=4th, -1=last # $day: 0=mon, 1=tue, ..., 6=sun $startday=1; $delta=0; if ($week < 0) { $startday = date('t', mktime(0, 0, 0, $month, 1, $year)); # 28..31 $delta=1; } $start = mktime(0, 0, 0, $month, $startday, $year); $dstart = date('w', $start)-1; # last of the month falls on 0=mon,6=sun $offset=$day-$dstart; if ($offset<$delta){$offset+=7;} $newday=$startday+$offset+($week*7); return mktime(0, 0, 0, $month, $newday, $year); } 

Это работает для меня и на основе языковой версии 🙂 Только слишком плохо, мне нужно было сделать эту дельта-вещь (ведь если последний день месяца – желаемый рабочий день, нам не нужно вычитать 7)

Просто узнайте, что такое первый и последний день месяца (например, 1 мая 2009 года – пятница и 31 мая 2009 года – воскресенье). Я считаю, что большинство функций PHP используют понедельник = 0, воскресенье = 6, таким образом, пятница = 4, так что вы знаете, что воскресенье (6) – пятница (4) = 2, затем 31-2 = 29, то есть последняя пятница этого месяца на 29-м. В первую пятницу, если число отрицательное, добавьте 7, если число равно 0, месяц начинается в пятницу.

То же самое можно сделать очень элегантно, используя класс DateTime .

 $time_zone = new DateTimeZone('Europe/Ljubljana'); $first_friday_of_this_month = new DateTime('first Friday of this month', $time_zone); $last_friday_of_this_month = new DateTime('last Friday of this month', $time_zone); echo $first_friday_of_this_month->format('Ym-d'); # 2015-11-06 echo $last_friday_of_this_month->format('Ym-d'); # 2015-11-27 

Кажется, это идеально работает каждый раз; он принимает любую датированную дату и возвращает дату последней пятницы месяца, даже в случае 5 пятниц в месяце.

 function get_last_friday_of_month($inDate) { $inDate = date('Ym-24', strtotime($inDate)); $last_friday = date('Ym-d',strtotime($inDate.' next friday')); $next_friday = date('Ym-d',strtotime($inDate.' next friday')); if(date('m', strtotime($last_friday)) === date('m', strtotime($next_friday))){ $last_friday = $next_friday; }else{ // } return $last_friday; } 

Ниже приведено самое быстрое решение, и вы можете использовать его во всех условиях. Также вы можете получить массив всего дня недели, если вы немного его настроите.

 function findDate($date, $week, $weekday){ # $date is the date we are using to get the month and year which should be a datetime object # $week can be: 0 for first, 1 for second, 2 for third, 3 for fourth and -1 for last # $weekday can be: 1 for Monday, 2 for Tuesday, 3 for Wednesday, 4 for Thursday, 5 for Friday, 6 for Saturday and 7 for Sunday $start = clone $date; $finish = clone $date; $start->modify('first day of this month'); $finish->modify('last day of this month'); $finish->modify('+1 day'); $interval = DateInterval::createFromDateString('1 day'); $period = new DatePeriod($start, $interval, $finish); foreach($period AS $date){ $result[$date->format('N')][] = $date; } if($week == -1) return end($result[$weekday]); else return $result[$weekday][$week]; } $date = DateTime::createFromFormat('d/m/Y', '25/12/2016'); # find the third Wednesday in December 2016 $result = findDate($date, 2, 3); echo $result->format('d/m/Y'); 

Надеюсь, это поможет.

Дайте мне знать, если вам нужна дополнительная информация.