【问题标题】:Get the First or Last Friday in a Month获取一个月中的第一个或最后一个星期五
【发布时间】:2009-05-29 03:20:37
【问题描述】:

我正在尝试写一个这样的日历函数

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

$week 是一个整数 (1, 2, 3...),$day 是一天 (Sun, Mon, ...) 或数字,以更容易的为准。方向有点混乱,因为它做了不同的计算。

举个例子,让我们调用

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

它使用默认值,并获取 5 月的第一个星期日,即 2009-05-03。如果我们调用

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

,它返回 5 月的倒数第二个星期日,即 2009-05-24。

【问题讨论】:

  • 只是为了好奇..你用它做什么?!这不是那么简单,但无论如何都可以做到。
  • 我正在编写一个作业调度程序。有些职位是每月的,我需要能够确定在日历中显示它们的日期。
  • 为你写下来,希望能如你所愿!
  • 有趣的是,Google 的 AI 在“第三个星期三”(可能还有其他可能性)返回了此内容,因此无需将问题编辑为“一个月中的第 n 个指定日期”

标签: php datetime


【解决方案1】:

与语言无关的版本:

要获取该月的第一天,请从该月的第一天开始:yyyy-mm-01。使用任何可用的函数来给出对应于星期几的数字。从您要查找的日期中减去该数字;例如,如果该月的第一天是星期三 (2),而您正在寻找星期五 (4),则从 4 中减去 2,留下 2。如果答案是否定的,则添加 7。最后将其添加到第一个这个月;以我为例,第一个星期五是第三个。

要获得该月的最后一个星期五,请找到下个月的第一个星期五并减去 7 天。

【讨论】:

    【解决方案2】:

    也许它可以做得更快...
    这对编码非常有趣。

    请注意,$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);
    }
    

    我已经用几个示例对其进行了测试,并且似乎总是可以正常工作,但请务必仔细检查;)

    【讨论】:

    • 哦,还要注意星期一 = 1,星期日 = 7
    【解决方案3】:

    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】:

      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);
      打印
      星期五,2009 年 4 月 3 日 00:00:00 CEST | 2009 年 4 月 24 日星期五 00:00:00 CEST

      【讨论】:

      • 我已经检查过了,它不支持“倒数第二”或类似的,而且速度较慢。
      【解决方案5】:

      无需计算或循环 - 使用 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.
      

      【讨论】:

      • @beardedlinuxgeek - 我的荣幸。
      【解决方案6】:
      echo date('Y-m-d',strtotime('last friday'));
      

      【讨论】:

        【解决方案7】:

        您可以使用mktime 检索当月第一天的unix时间戳:

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

        如果您有某个月的第一天的日期,则可以使用 date 轻松检索该日期的工作日:

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

        从那里可以很容易地向前走,得到你想要的日期。

        【讨论】:

          【解决方案8】:
          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);
          }
          

          这对我有用,并且基于与语言无关的版本:-) 太糟糕了,我需要做那个 delta 的事情(因为如果一个月的最后一天是想要的工作日,我们不需要减去 7)

          【讨论】:

            【解决方案9】:

            只要找出有关月份的第一天和最后一天是什么(即 2009 年 5 月 1 日是星期五,2009 年 5 月 31 日是星期日)我相信大多数 PHP 函数使用星期一 = 0,星期日 = 6,因此Friday=4,所以你知道Sunday (6) - Friday (4) = 2,然后31-2 = 29,这意味着这个月的最后一个星期五是29号。第一个星期五,如果为负数,则加7,如果为0,则月份从星期五开始。

            【讨论】:

              【解决方案10】:

              使用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
              

              【讨论】:

                【解决方案11】:

                这似乎每次都能完美运行;它采用提供的任何日期并返回该月最后一个星期五的日期,即使是该月的 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;
                }
                

                【讨论】:

                  【解决方案12】:

                  以下是最快的解决方案,您可以在所有条件下使用。如果你稍微调整一下,你也可以得到一个星期的数组。

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

                  我希望这会有所帮助。

                  如果您需要任何进一步的信息,请告诉我。 ;)

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 2021-12-04
                    • 1970-01-01
                    • 2023-03-21
                    • 1970-01-01
                    • 2014-07-21
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多