【问题标题】:get startdate and enddate for current quarter php获取当前季度php的开始日期和结束日期
【发布时间】:2014-02-06 18:50:20
【问题描述】:

我正在尝试按季度设置开始日期和结束日期。

例如,我正在开发一个报告系统,我需要报告第 1 季度、第 2 季度、第 3 季度和第 4 季度的数据。

第一季度 - 1 月 - 3 月

第二季度 - 4 月 - 6 月

第三季度 - 七月 - 九月

第四季度 - 10 月 - 12 月

例如,我有一些本月和上月的案例,如下所示。

   case 'this_month':
      $start_date = date(DATE_FORMAT, mktime(0, 0, 0, date("m"), 1, date("Y")));
      $end_date = date(DATE_FORMAT, mktime(0, 0, 0, date("m"), date("d"), date("Y")));
    break;
    case 'last_month':
      $start_date = date(DATE_FORMAT, mktime(0, 0, 0, date("m") - 1, 1, date("Y")));
      $end_date = date(DATE_FORMAT, mktime(0, 0, 0, date("m"), 0, date("Y")));
    break;

但现在我需要为本季度和上一季度添加案例,但我不确定如何实际执行此操作,因此它反映了正确的季度范围。

有什么想法吗?

【问题讨论】:

  • 由于找不到任何流畅的解决方案,我刚刚提交了一个 PHP 功能请求,以原生支持 new \DateTime('first day of last quarter'):bugs.php.net/bug.php?id=76622 等实时格式。

标签: php codeigniter date


【解决方案1】:
/**
* Compute the start and end date of some fixed o relative quarter in a specific year.
* @param mixed $quarter  Integer from 1 to 4 or relative string value:
*                        'this', 'current', 'previous', 'first' or 'last'.
*                        'this' is equivalent to 'current'. Any other value
*                        will be ignored and instead current quarter will be used.
*                        Default value 'current'. Particulary, 'previous' value
*                        only make sense with current year so if you use it with
*                        other year like: get_dates_of_quarter('previous', 1990)
*                        the year will be ignored and instead the current year
*                        will be used.
* @param int $year       Year of the quarter. Any wrong value will be ignored and
*                        instead the current year will be used.
*                        Default value null (current year).
* @param string $format  String to format returned dates
* @return array          Array with two elements (keys): start and end date.
*/
public static function get_dates_of_quarter($quarter = 'current', $year = null, $format = null)
{
    if ( !is_int($year) ) {        
       $year = (new DateTime)->format('Y');
    }
    $current_quarter = ceil((new DateTime)->format('n') / 3);
    switch (  strtolower($quarter) ) {
    case 'this':
    case 'current':
       $quarter = ceil((new DateTime)->format('n') / 3);
       break;

    case 'previous':
       $year = (new DateTime)->format('Y');
       if ($current_quarter == 1) {
          $quarter = 4;
          $year--;
        } else {
          $quarter =  $current_quarter - 1;
        }
        break;

    case 'first':
        $quarter = 1;
        break;

    case 'last':
        $quarter = 4;
        break;

    default:
        $quarter = (!is_int($quarter) || $quarter < 1 || $quarter > 4) ? $current_quarter : $quarter;
        break;
    }
    if ( $quarter === 'this' ) {
        $quarter = ceil((new DateTime)->format('n') / 3);
    }
    $start = new DateTime($year.'-'.(3*$quarter-2).'-1 00:00:00');
    $end = new DateTime($year.'-'.(3*$quarter).'-'.($quarter == 1 || $quarter == 4 ? 31 : 30) .' 23:59:59');

    return array(
        'start' => $format ? $start->format($format) : $start,
        'end' => $format ? $end->format($format) : $end,
    );
}

我开发了这个函数来以任何方式处理季度:相对(this、previous、first、last)和固定。

例子:

get_dates_of_quarter();
//return current quarter start and end dates

get_dates_of_quarter(2);
//return 2nd quarter start and end dates of current year

get_dates_of_quarter('first', 2010, 'Y-m-d');
//return start='2010-01-01' and end='2014-03-31'

get_dates_of_quarter('current', 2009, 'Y-m-d');
//Supposing today is '2014-08-22' (3rd quarter), this will return
//3rd quarter but of year 2009.
//return start='2009-07-01' and end='2009-09-30'

get_dates_of_quarter('previous');
//Supposing today is '2014-02-18' (1st quarter), this will return
//return start='2013-10-01' and end='2013-12-31'

等待这个帮助某人;)

【讨论】:

  • @Delmo 请检查 2 月是否有 28 天。它给出了错误的值
【解决方案2】:

简单代码:

$current_quarter = ceil(date('n') / 3);
$first_date = date('Y-m-d', strtotime(date('Y') . '-' . (($current_quarter * 3) - 2) . '-1'));
$last_date = date('Y-m-t', strtotime(date('Y') . '-' . (($current_quarter * 3)) . '-1'));

【讨论】:

  • 这也是错误的——“最后日期”不是一个月的第一天……应该是那个月的最后一天……
  • @Scott 你可能没见过'Y-m-t'的“t”,答案是正确的!
【解决方案3】:

怎么样:

$offset = (date('n')%3)-1; // modulo ftw
$start = new DateTime("first day of -$offset month midnight");
$offset = 3-(date('n')%3); // modulo ftw again
$end = new DateTime("last day of +$offset month midnight");

【讨论】:

  • 惊呆了,有人为上面的那一大段代码写了一个衬里;)"first day of -" . ((date('n')%3)-1) . " month midnight"
  • 今天试过了 - 2019 年 3 月 9 日 - 但不起作用 - 第一个偏移量是 -1,字符串是--1...我得到了 19 年 4 月 1 日作为开始- 应该是 1/1/19 - 下面的 KLOZ 看起来不错
  • start 在一个季度的最后几个月将不正确,因为 %3 将给出 0。您的偏移量 ((date('n')%3-1) 将给出 jan = 1-1 = 0,feb = 2-1 = 1 ,march = 0-1 = -1 等。相反,您可以使用(date('n')-1)%3,这将给出 jan = 0%3 = 0, feb = 1%3 = 1, mar = 2%3 = 2, apr = 3%3 = 0 等此外,在过去几个月中,您的偏移量 (3-(date('n')%3)) 将是 +3,这太高了:jan = 3-1 = 2, feb = 3-2 = 1, mar = 3-0 = 3 , apr = 3-1 = 2。您可以通过重新应用模数来解决此问题:(3-(date('n'))%3)%3, jan = 2%3 = 2, feb = 1%3 = 1, mar = 3%3 = 0, apr = 2 %3 = 2。
  • 第一个偏移量应该是:$offset = (date('n')-1)%3; 第二个应该是:$offset = (3-(date('n'))%3)%3;
【解决方案4】:

检查this quarter

 case 'this_quarter':

          $current_month = date('m');
          $current_year = date('Y');
          if($current_month>=1 && $current_month<=3)
          {
            $start_date = strtotime('1-January-'.$current_year);  // timestamp or 1-Januray 12:00:00 AM
            $end_date = strtotime('1-April-'.$current_year);  // timestamp or 1-April 12:00:00 AM means end of 31 March
          }
          else  if($current_month>=4 && $current_month<=6)
          {
            $start_date = strtotime('1-April-'.$current_year);  // timestamp or 1-April 12:00:00 AM
            $end_date = strtotime('1-July-'.$current_year);  // timestamp or 1-July 12:00:00 AM means end of 30 June
          }
          else  if($current_month>=7 && $current_month<=9)
          {
            $start_date = strtotime('1-July-'.$current_year);  // timestamp or 1-July 12:00:00 AM
            $end_date = strtotime('1-October-'.$current_year);  // timestamp or 1-October 12:00:00 AM means end of 30 September
          }
          else  if($current_month>=10 && $current_month<=12)
          {
            $start_date = strtotime('1-October-'.$current_year);  // timestamp or 1-October 12:00:00 AM
            $end_date = strtotime('1-January-'.($current_year+1));  // timestamp or 1-January Next year 12:00:00 AM means end of 31 December this year
          }



        break;

更新:2 对于last quarter

case 'last_quarter':

          $current_month = date('m');
          $current_year = date('Y');

          if($current_month>=1 && $current_month<=3)
          {
            $start_date = strtotime('1-October-'.($current_year-1));  // timestamp or 1-October Last Year 12:00:00 AM
            $end_date = strtotime('1-January-'.$current_year);  // // timestamp or 1-January  12:00:00 AM means end of 31 December Last year
          } 
          else if($current_month>=4 && $current_month<=6)
          {
            $start_date = strtotime('1-January-'.$current_year);  // timestamp or 1-Januray 12:00:00 AM
            $end_date = strtotime('1-April-'.$current_year);  // timestamp or 1-April 12:00:00 AM means end of 31 March
          }
          else  if($current_month>=7 && $current_month<=9)
          {
            $start_date = strtotime('1-April-'.$current_year);  // timestamp or 1-April 12:00:00 AM
            $end_date = strtotime('1-July-'.$current_year);  // timestamp or 1-July 12:00:00 AM means end of 30 June
          }
          else  if($current_month>=10 && $current_month<=12)
          {
            $start_date = strtotime('1-July-'.$current_year);  // timestamp or 1-July 12:00:00 AM
            $end_date = strtotime('1-October-'.$current_year);  // timestamp or 1-October 12:00:00 AM means end of 30 September
          }



        break;

【讨论】:

    【解决方案5】:

    IMO 有些答案太复杂了

    public function getStartOfQuarter()
    {
         return date(sprintf('Y-%s-01', floor((date('n') - 1) / 3) * 3 + 1));
    }
    
    public function getEndOfQuarter()
    {
        return date(sprintf('Y-%s-t', floor((date('n') + 2) / 3) * 3));
    }
    

    【讨论】:

    • 越野车?为time() = 1473937773 提供2016-9-012016-9-30
    • 已修复。我有一个单元测试: 1. 错误; 2. 甚至没有运行。
    • 第一个 return 语句应该在分号之前有两个额外的右括号。
    • 这将返回 2021-12-30 作为本季度的最后一天。 12 月有 31 天..
    【解决方案6】:

    只是想指出SynaTree 的解决方案不适用于每季度最后 3 个月。

    这是使用 DateTime 的修改后的解决方案。

    $now = new DateTimeImmutable();
    $offset = ($now->format('n') - 1) % 3;
    $start = $now->modify("first day of -{$offset} month midnight");
    $endExcluded = $start->modify("+3 month");
    $endIncluded = $start->modify("+3 month -1 second");
    

    $endExcluded 适用于 DatePeriod 循环,其中当时间为 00:00:00 时不包括结束日期。

    【讨论】:

      【解决方案7】:

      一个通用的版本是:

          $date = new DateTime(/* you may insert a date here, else its now */);
          //$date->modify('-3 months'); // you may want last quarter or any other modifcation
          $quarter = ceil($date->format('n')/3);
      
          $start = new DateTime();
          $start->setDate($date->format('Y'), (($quarter*3)-2), 1)->setTime(0, 0, 0, 0);
      
          $end = new DateTime();
          $end->setDate($date->format('Y'), ($quarter*3), 1);
          $end->setDate($date->format('Y'), ($quarter*3), $end->format('t'))->setTime(0, 0, 0, 0);
      
          echo $start->format('Y-m-d');
          echo $end->format('Y-m-d');
      

      这既简单又有效。它让您选择输入日期并通过修改进行相对适应。

      【讨论】:

        【解决方案8】:

        尝试使用DateTime函数。对于您的示例,如下所示:

        case 'this_month':
            $start_date = new DateTime('first day of this month');
            $end_date = new DateTime('last day of this month');
        break;
        case 'last_month':
            $start_date = new DateTime('first day of next month');
            $end_date = new DateTime('last day of next month');
        break;
        
        echo $start_date->format(DATE_FORMAT);
        echo $end_date->format(DATE_FORMAT);
        

        如果您想获取季度的第一天和最后一天,请尝试使用:

         $start_date = new DateTime('first day of January');
         $end_date = new DateTime('last day of March');
        
         echo $start_date->format(DATE_FORMAT);
         echo $end_date->format(DATE_FORMAT);
        

        或者使用函数strtotime。 strtotime 示例:

        $quarter_start = strtotime('first day of January');
        $quarter_end = strtotime('last day of March');
        
        echo date(DATE_FORMAT, $quarter_start);
        echo date(DATE_FORMAT, $quarter_end);
        

        【讨论】:

        • 时间变化,一月和三月不再有效
        【解决方案9】:

        我认为这可能要简单得多。

        function get_this_quarter() {
            $current_month = date('m');
            $current_quarter_start = ceil($current_month/4)*3+1; // get the starting month of the current quarter
            $start_date = date("Y-m-d H:i:s", mktime(0, 0, 0, $current_quarter_start, 1, date('Y') ));
            $end_date = date("Y-m-d H:i:s", mktime(0, 0, 0, $current_quarter_start+3, 1, date('Y') ));
            // by adding or subtracting from $current_quarter_start within the mktime function you can get any quarter of any year you want.
            return array($start_date, $end_date);
        }
        

        这在没有所有 if 语句的情况下同样有效,并且更加灵活。如代码中的 cmets 所述,您可以轻松修改 $current_quarter_variable 以满足您的需求。

        希望这会有所帮助!

        【讨论】:

        • “结束日期”怎么可能是一个月的第一天?!给定日期“25.03.2016”的季度结束日期怎么可能是“01.07.2016”,而应该是 31.05.2016?这不起作用
        【解决方案10】:

        简单示例:

        define('DATE_FORMAT', 'Y-m-d');
        
        function get_start_and_end_date($case) {
            $start = 'first day of ';
            $end = 'last day of ';
        
            if ($case == 'this_quarter') {
                $case = 'quarter_' . ceil((new DateTime)->format('n') / 3);
            }
        
            switch ($case) {
                case 'prev_month'    : $start .= 'previous month'; $end .= 'previous month'; break;
                default              :
                case 'this_month'    : $start .= 'this month';     $end .= 'this month';     break;
                case 'next_month'    : $start .= 'next month';     $end .= 'next month';     break;
                case 'first_quarter' :
                case 'quarter_1'     : $start .= 'January';        $end .= 'March';          break;
                case 'quarter_2'     : $start .= 'April';          $end .= 'June';           break;
                case 'quarter_3'     : $start .= 'July';           $end .= 'September';      break;
                case 'last_quarter'  :
                case 'quarter_4'     : $start .= 'October';        $end .= 'December';       break;
            }
        
            return [
                'start' => (new DateTime($start))->format(DATE_FORMAT),
                'end' => (new DateTime($end))->format(DATE_FORMAT),
            ];
        }
        

        demo

        【讨论】:

          【解决方案11】:
          function getDates( $passedDate = '' ) {
              if( $passedDate == '' ) {
                  $v = ceil( date( "m" ) / 3 );
                  $y = date( "Y" );
              } else {
                  $v = ceil( $month / 3 );
                  $y = date( "Y", strtotime( $passedDate ) );
              }
              $y = date( "Y" );
              $m = ( $v * 3 ) - 2;
              $date = $y . '-' . $m . '-' . 01;
              $return['begin'] = date( "Y-m-d", strtotime(  $date ) );
              $return['end'] = date( "Y-m-t", strtotime( $return['begin'] . "+ 2 months"  ) );
              $return['version'] = $y . 'Q' . ceil( date( "m" ) / 4 );
              return $return;
          }
          

          默认情况下,此函数将返回当前日期的开始和结束日期以及季度,但是如果您希望它用于特定日期,只需将月份传递给它。

          【讨论】:

            【解决方案12】:

            这是我的解决方案。

            function getCurrentQuarter($timestamp=false){
                if(!$timestamp)$timestamp=time();
                $day = date('n', $timestamp);
                $quarter = ceil($day/3);
                return $quarter;
            }
            
            function getPreviousQuarter($timestamp=false){
                if(!$timestamp)$timestamp=time();
                $quarter = getCurrentQuarter($timestamp) - 1;
                if($quarter < 0){
                    $quarter = 4;
                }
                return $quarter;
            }
            
            function getNextQuarter($timestamp=false){
                if(!$timestamp)$timestamp=time();
                $quarter = getCurrentQuarter($timestamp) + 1;
                if($quarter > 4){
                    $quarter = 0;
                }
                return $quarter;
            }
            
            function getFirstAndLastDayOfQuarter($quarter, $year=false){
                if(!$year)$year=date('Y');
                $startDate = new DateTime($year.'-'.($quarter*3-2).'-1');
                //Get first day of first month of next quarter
                $endMonth = $quarter*3+1;
                if($endMonth>12){
                    $endMonth = 1;
                    $year++;
                }
                $endDate = new DateTime($year.'-'.$endMonth.'-1');
                //Subtract 1 second to get last day of prior month
                $endDate->sub(new DateInterval('PT1S'));
                return array($startDate, $endDate);
            }
            

            【讨论】:

              【解决方案13】:

              您可以使用基本数学简单地做到这一点。

              每个月的数字减去 1 % 3 将告诉您与当前季度抵消了多少个月。

              /**
               * @param DateTime $date
               * @return array
               */
              function getQuarterRangeFromDate(DateTime $date)
              {
              
                  // Clone the date to avoid modifying your date in current context
                  $quarter_start = clone($date);
              
                  // Find the offset of months
                  $months_offset = ($date->format('m') - 1) % 3;
              
              
                  // Modify quarter date
                  $quarter_start->modify(" - " . $months_offset . " month")
                      ->modify("first day of this month");
              
              
                  $quarter_end = clone($quarter_start);
                  $quarter_end->modify("+ 3 month");
              
                  return [$quarter_start, $quarter_end];
              }
              

              【讨论】:

                【解决方案14】:
                $monthsFromStart = (date('n') - 1) % 3; // 0, 1, 2, 0, 1, 2, ...
                $monthsToEnd = 2 - $monthsFromStart; // 2, 1, 0, 2, 1, 0, ...
                
                $startDay = new DateTime("first day of -$monthsFromStart month midnight");
                $endDay = new DateTime("last day of +$monthsToEnd month midnight");
                

                【讨论】:

                  【解决方案15】:

                  我瞥了一眼答案,所有答案都大约是今年的几个季度。 有时,您需要特定日期的季度。 这是我的尝试,详细解释了代码 cmets 中的作用:

                  /** * 获取季度开始/结束日期 * * 可能的用法: * 季度开始日期:季度('2016-04-12')[开始] - 结果:2016-04-01 * 季度结束日期:季度('2016-04-12')[结束] - 结果:2016-06-30 * 季度编号:季度('2016-04-12')[编号] - 结果:2 * * @param string $time 您希望季度开始和结束的日期 - 可接受的格式,至少:'Y-m',还有:'Y-m-d'、'Y-m-d H:i:s' 等。 * @return array [季度开始,季度结束,季度编号] */ 公共功能区($time) { /** * 获取给定日期的季度数 * 我们是天花板浮点值,所以我使用 (int) 强制转换来获得整数(没有小数部分) * 可以使用 round() 而不是强制转换为 (int) */ $quarter = (int)ceil(date("m", strtotime($time)) / 3); $year = date("Y", strtotime($time)); $四分之一= [ 1 => ['01-01', '03-31', 1], 2 => ['04-01', '06-30', 2], 3 => ['07-01', '09-30', 3], 4 => ['10-01', '12-31', 4], ]; $季度开始 = $年。 '-' 。 $季度[$季度][0]; $季度结束 = $年。 '-' 。 $季度[$季度][1]; $quarterNumber = $quarters[$quarter][2]; return ['start' => $quarterStart, 'end' => $quarterEnd, 'number' => $quarterNumber]; }

                  【讨论】:

                    【解决方案16】:

                    我需要一些函数来给我每个季度的日期,这样我就可以获取一些销售信息。如果有人需要,这是代码:

                       private function getQuarterRange($quarter, $year=null) {
                            if ($quarter > 4 || $quarter < 1)
                                return null;
                    
                            if ($year == null)
                                $year = date('Y');
                    
                    
                            $startDate = date('Y-m-d', strtotime($year . '-' . (($quarter * 3) - 2). '-1'));
                            $endDate = date('Y-m-d', strtotime(date('Y-m-d', strtotime($startDate)) . '+3 month - 1 day'));
                            return ['startDate' => $startDate, 'endDate' => $endDate];
                        }
                    

                    只需致电:var_dump($this-&gt;getQuarterRange(4, 2018)); 输出:

                    array(2) {
                      ["startDate"]=>
                      string(10) "2018-10-01"
                      ["endDate"]=>
                      string(10) "2018-12-31"
                    }
                    

                    【讨论】:

                      【解决方案17】:
                      $QuarterMonth = ["1" => ["Y-01-01","Y-03-31"],"2"=>["Y-04-01","Y-06-30"],"3"=>["Y-07-01","Y-09-30"],"4"=>["Y-10-01","Y-12-31"]];
                      $curMonth = date("m", time());
                      
                      $qtr_month_from = date($QuarterMonth[ceil($curMonth/3)][0]);
                      $qtr_month_to = date($QuarterMonth[ceil($curMonth/3)][1]);
                      

                      【讨论】:

                        【解决方案18】:

                        使用下面的代码我们可以得到当前季度的开始日期。

                        $intCurrentMonth               = date( 'n' );
                        $fltCurrentQuarter             = ( $intCurrentMonth - 1 ) / 3;
                        $intCurrentQuarter             = ( int ) $fltCurrentQuarter;
                        $fltCurrentQuarterMantissa     = $fltCurrentQuarter - $intCurrentQuarter;
                        $fltCurrentQuarterMantissa     = ( int ) ( $fltCurrentQuarterMantissa * 100 ) / 100;
                        $intFirstMonthOfCurrentQuarter = $intCurrentMonth - ceil( $fltCurrentQuarterMantissa * 3 );
                        $strFirstDateOfCurrentQuarter  = date( 'm/d/Y', strtotime( $intFirstMonthOfCurrentQuarter . '/01' ) );
                        
                        

                        有很好的优化空间。

                        【讨论】:

                          【解决方案19】:

                          获取当前季度开始/结束日期的更简单方法:

                          $qtr_start = date("Y-".(ceil(date("n")/3))."-01");
                          

                          那么,只要在正确的地方加上 3 个月就可以得到结束:

                          $qtr_end = date("Y-".(ceil(date("n")/3)+3)."-01");
                          

                          只有我的两分钱。

                          【讨论】:

                            【解决方案20】:

                            我不确定这为什么如此困难 - 有几个解决方案非常接近,但未能找到一个季度的第一天和最后一天。季度不会在第一天结束!

                            由上面的 KLOZ 修改:

                                $current_quarter = ceil(date('n') / 3);
                                $first_day_of_this_quarter = date('m/d/Y', strtotime(date('Y').'-'.(($current_quarter*3)-2).'-1'));
                                $last_day_of_this_quarter = date('m/d/Y', strtotime(date('Y').'-'.($current_quarter*3).'-'.(date("t",date('Y').'-'.($current_quarter*3).'-1'))));
                            

                            【讨论】:

                            • KLOZ 的答案对我来说是正确的。但是,此错误: TypeError date(): Argument #2 ($timestamp) must be of type ?int, string given
                            猜你喜欢
                            • 2019-08-07
                            • 1970-01-01
                            • 1970-01-01
                            • 1970-01-01
                            • 1970-01-01
                            • 2021-07-21
                            • 1970-01-01
                            • 1970-01-01
                            • 2014-03-02
                            相关资源
                            最近更新 更多