2009-05-29 4 views
10

Я пытаюсь написать функцию календаря, как этотПолучить первую или последнюю пятницу месяца в

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

$week является целым числом (1, 2, 3 ...), $ день является день (Солнце, Пн, ...) или номер, в зависимости от того, что проще. Направление немного запутанно, потому что оно делает другой расчет.

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

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

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

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

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

+0

Просто ради любопытства .. Для чего вы его используете ?! Это не так просто, но может быть сделано в любом случае. – MartinodF

+0

Я пишу планировщик заданий. Некоторые позиции размещаются ежемесячно, и мне нужно определить, какие даты будут отображаться в календаре. – Zahymaka

+0

Написал это для вас, надеюсь, что он работает так, как вы ожидали! – MartinodF

ответ

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); 
} 

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

+0

О, а также обратите внимание, что понедельник = 1, воскресенье = 7 – MartinodF

2

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

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

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

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

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

20

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

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

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

0

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

8

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

+0

Я уже проверил, он не поддерживает «второй последний» или аналогичный, и это медленнее. – MartinodF

6

Нет необходимости расчетов или петли - это не очень легко сделать с помощью StrToTime():

НАЙДИТЕ-го или последнее вхождение в определенный день конкретного месяц:

///////////////////////////////////////////////////////////////// 
// 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. 
+0

Огромное спасибо – beardedlinuxgeek

+0

@beardedlinuxgeek - Мое удовольствие. – Eli

1
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)

5

Встроенные функции времени 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')); 
4
echo date('Y-m-d',strtotime('last friday')); 
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 
0

Это, кажется, работает идеально каждый раз; он принимает любую датированную дату и возвращает дату последней пятницы месяца, даже в случае 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; 
} 
0

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

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'); 

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

Сообщите мне, если вам нужна дополнительная информация.

Смежные вопросы