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

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

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

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

для примера, давайте вызовем

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

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

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

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

12 ответов


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

обратите внимание:$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);
}

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


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

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

чтобы получить последнюю пятницу месяца, найдите первую пятницу следующего месяца и вычтите 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);
печать
Friday, 03-Apr-09 00:00:00 CEST | Friday, 24-Apr-09 00:00:00 CEST

встроенные функции времени 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'));

нет необходимости в вычислениях или циклах - это очень легко сделать с помощью 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("Y-m-01", $tsInMonth ) );
$NextMonthTS = strtotime( date("Y-m-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("Y-m-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("Y-m-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.

echo date('Y-m-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('Y-m-d');  # 2015-11-06
echo $last_friday_of_this_month->format('Y-m-d');  # 2015-11-27

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

function get_last_friday_of_month($inDate) {
    $inDate = date('Y-m-24', strtotime($inDate));
    $last_friday = date('Y-m-d',strtotime($inDate.' next friday'));
    $next_friday = date('Y-m-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');

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

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