PHP DateTime :: แก้ไขการเพิ่มและการลบเดือน


102

ฉันทำงานหนักมากDateTime classและเพิ่งพบกับสิ่งที่ฉันคิดว่าเป็นจุดบกพร่องเมื่อเพิ่มเดือน หลังจากการวิจัยเล็กน้อยปรากฏว่ามันไม่ใช่ข้อบกพร่อง แต่กลับได้ผลตามที่ตั้งใจไว้ ตามเอกสารพบที่นี่ :

ตัวอย่าง # 2 ระวังเมื่อบวกหรือลบเดือน

<?php
$date = new DateTime('2000-12-31');

$date->modify('+1 month');
echo $date->format('Y-m-d') . "\n";

$date->modify('+1 month');
echo $date->format('Y-m-d') . "\n";
?>
The above example will output:
2001-01-31
2001-03-03

ใครสามารถพิสูจน์ได้ว่าทำไมสิ่งนี้ถึงไม่ถือว่าเป็นจุดบกพร่อง?

นอกจากนี้ใครมีวิธีแก้ปัญหาที่สวยงามเพื่อแก้ไขปัญหาและทำให้ +1 เดือนทำงานตามที่คาดไว้แทนที่จะเป็นตามที่ตั้งใจไว้หรือไม่?


คาดว่า "2544-01-31" บวก 1 เดือนจะเป็นอย่างไร ... "2544-02-28"? "2544-03-01"?
Artefacto

57
โดยส่วนตัวฉันคาดว่าจะเป็น 2001-02-28
tplaner

เรื่องเดียวกันกับstrtotime() stackoverflow.com/questions/7119777/…
Valentin Despa

2
ใช่มันค่อนข้างเป็นนิสัยที่น่ารำคาญ คุณได้อ่านแบบละเอียดเพื่อให้ทราบว่า P1M คือ 31 วัน ไม่เข้าใจจริงๆว่าทำไมคนถึงปกป้องมันว่าเป็นพฤติกรรมที่ "ถูกต้อง"
Indivision Dev

ดูเหมือนว่าความคิดเห็นที่เป็นที่นิยมคือตรรกะควรปัดเศษลง (เป็น 2/28) แม้ว่า PHP จะปัดเศษขึ้น (เป็น 3/1) ... แม้ว่าฉันจะชอบวิธีของ PHP แต่ Excel ของ Microsoft ก็ลดลงทำให้นักพัฒนาเว็บเทียบเคียงกับผู้ใช้สเปรดชีต ...
Dave Heq

คำตอบ:


107

เหตุใดจึงไม่เกิดข้อผิดพลาด:

พฤติกรรมปัจจุบันถูกต้อง สิ่งต่อไปนี้เกิดขึ้นภายใน:

  1. +1 monthเพิ่มจำนวนเดือน (เดิม 1) ทีละหนึ่ง 2010-02-31นี้จะทำให้วันที่

  2. เดือนที่สอง (กุมภาพันธ์) มีเพียง 28 วันในปี 2010 ดังนั้น PHP จึงแก้ไขสิ่งนี้โดยอัตโนมัติเพียงแค่นับวันต่อจากวันที่ 1 กุมภาพันธ์ จากนั้นคุณจะสิ้นสุดในวันที่ 3 มีนาคม

วิธีรับสิ่งที่คุณต้องการ:

เพื่อให้ได้สิ่งที่คุณต้องการโดย: ตรวจสอบด้วยตนเองในเดือนถัดไป จากนั้นเพิ่มจำนวนวันในเดือนถัดไป

ฉันหวังว่าคุณจะเขียนโค้ดนี้ได้ ฉันแค่ให้สิ่งที่ต้องทำ

วิธี PHP 5.3:

ที่จะได้รับพฤติกรรมที่ถูกต้องคุณสามารถใช้อย่างใดอย่างหนึ่งของการทำงานใหม่ PHP 5.3 first day ofที่เปิดตัวฉันท์เวลาญาติ บทนี้สามารถใช้ร่วมกับnext month, fifth monthหรือ+8 monthsจะไปวันแรกของเดือนที่ระบุ แทนที่จะใช้+1 monthสิ่งที่คุณทำอยู่คุณสามารถใช้รหัสนี้เพื่อรับวันแรกของเดือนถัดไปได้ดังนี้:

<?php
$d = new DateTime( '2010-01-31' );
$d->modify( 'first day of next month' );
echo $d->format( 'F' ), "\n";
?>

Februaryสคริปต์นี้จะถูกต้องเอาท์พุท สิ่งต่อไปนี้เกิดขึ้นเมื่อ PHP ประมวลผลfirst day of next monthบทนี้:

  1. next monthเพิ่มจำนวนเดือน (เดิม 1) ทีละหนึ่ง ทำให้วันที่ 2010-02-31

  2. first day ofตั้งค่าหมายเลขวัน1เป็นวันที่ 2010-02-01


1
สิ่งที่คุณกำลังพูดคือมันเพิ่ม 1 เดือนอย่างแท้จริงโดยไม่สนใจวันที่สมบูรณ์? ดังนั้นฉันคิดว่าคุณอาจประสบปัญหาคล้าย ๆ กันกับ +1 ปีถ้าคุณเพิ่มในปีอธิกสุรทิน?
tplaner

@evolve ใช่วรรณกรรมเพิ่ม 1 เดือน
shamittomar

13
และถ้าคุณลบ 1 เดือนหลังจากเพิ่มแล้วคุณจะได้วันที่ที่แตกต่างกันโดยสิ้นเชิงฉันคิดว่า ดูเหมือนไม่ได้ตั้งใจมาก
Dan Breen

2
ตัวอย่างที่ยอดเยี่ยมเกี่ยวกับการใช้ stanzas ใหม่ใน PHP 5.3 ซึ่งคุณสามารถใช้วันแรกวันสุดท้ายเดือนนี้เดือนถัดไปและเดือนก่อนหน้า
Kim Stacks

6
นี่คือบั๊ก ข้อผิดพลาดร้ายแรง ถ้าฉันต้องการเพิ่ม 31 วันฉันเพิ่ม 31 วัน II ต้องการเพิ่มเดือนควรเพิ่มเดือนไม่ใช่ 31 วัน
low_rents

12

นี่คือโซลูชันขนาดกะทัดรัดอีกวิธีหนึ่งโดยใช้เมธอด DateTime โดยปรับเปลี่ยนวัตถุในตำแหน่งโดยไม่ต้องสร้างโคลน

$dt = new DateTime('2012-01-31');

echo $dt->format('Y-m-d'), PHP_EOL;

$day = $dt->format('j');
$dt->modify('first day of +1 month');
$dt->modify('+' . (min($day, $dt->format('t')) - 1) . ' days');

echo $dt->format('Y-m-d'), PHP_EOL;

มันส่งออก:

2012-01-31
2012-02-29

1
ขอบคุณ. ทางออกที่ดีที่สุดที่มีให้ที่นี่ $dt->modify()->modify()นอกจากนี้คุณยังสามารถร่นรหัสเพื่อ ใช้งานได้ดีเช่นกัน
Alph.Dev

10

สิ่งนี้อาจมีประโยชน์:

echo Date("Y-m-d", strtotime("2013-01-01 +1 Month -1 Day"));
  // 2013-01-31

echo Date("Y-m-d", strtotime("2013-02-01 +1 Month -1 Day"));
  // 2013-02-28

echo Date("Y-m-d", strtotime("2013-03-01 +1 Month -1 Day"));
  // 2013-03-31

echo Date("Y-m-d", strtotime("2013-04-01 +1 Month -1 Day"));
  // 2013-04-30

echo Date("Y-m-d", strtotime("2013-05-01 +1 Month -1 Day"));
  // 2013-05-31

echo Date("Y-m-d", strtotime("2013-06-01 +1 Month -1 Day"));
  // 2013-06-30

echo Date("Y-m-d", strtotime("2013-07-01 +1 Month -1 Day"));
  // 2013-07-31

echo Date("Y-m-d", strtotime("2013-08-01 +1 Month -1 Day"));
  // 2013-08-31

echo Date("Y-m-d", strtotime("2013-09-01 +1 Month -1 Day"));
  // 2013-09-30

echo Date("Y-m-d", strtotime("2013-10-01 +1 Month -1 Day"));
  // 2013-10-31

echo Date("Y-m-d", strtotime("2013-11-01 +1 Month -1 Day"));
  // 2013-11-30

echo Date("Y-m-d", strtotime("2013-12-01 +1 Month -1 Day"));
  // 2013-12-31

2
ไม่ใช่วิธีแก้ปัญหาทั่วไปเนื่องจากใช้ได้กับอินพุตบางอย่างเท่านั้นเช่นวันที่ 1 ของเดือน เช่นการทำเช่นนี้ในวันที่ 30 มกราคมนำไปสู่ความทุกข์
Jens Roland

หรือคุณสามารถทำได้$dateTime->modify('first day of next month')->modify('-1day')
Anthony

6

วิธีแก้ปัญหาของฉัน:

$startDate = new \DateTime( '2015-08-30' );
$endDate = clone $startDate;

$billing_count = '6';
$billing_unit = 'm';

$endDate->add( new \DateInterval( 'P' . $billing_count . strtoupper( $billing_unit ) ) );

if ( intval( $endDate->format( 'n' ) ) > ( intval( $startDate->format( 'n' ) ) + intval( $billing_count ) ) % 12 )
{
    if ( intval( $startDate->format( 'n' ) ) + intval( $billing_count ) != 12 )
    {
        $endDate->modify( 'last day of -1 month' );
    }
}

3
คำสั่ง "โคลน" เป็นวิธีแก้ปัญหาการกำหนดตัวแปรของฉัน ขอบคุณสำหรับสิ่งนี้.
Steph Rose

4

ฉันเห็นด้วยกับความเชื่อมั่นของ OP ว่านี่เป็นเรื่องที่ไม่เข้าใจง่ายและน่าหงุดหงิด แต่ก็กำลังพิจารณาว่ามีความ+1 monthหมายอย่างไรในสถานการณ์ที่เกิดเหตุการณ์นี้ ลองพิจารณาตัวอย่างเหล่านี้:

คุณเริ่มต้นด้วย 2015-01-31 และต้องการเพิ่มเดือน 6 ​​ครั้งเพื่อรับรอบการจัดกำหนดการสำหรับการส่งจดหมายข่าวทางอีเมล ด้วยความคาดหวังเริ่มต้นของ OP สิ่งนี้จะกลับมา:

  • 2015-01-31
  • 2015-02-28
  • 2015-03-31
  • 2015-04-30
  • 2015-05-31
  • 2015-06-30

สังเกตได้ทันทีว่าเราคาดว่า+1 monthจะหมายถึงlast day of monthหรือหรืออีกทางหนึ่งคือเพิ่ม 1 เดือนต่อการวนซ้ำ แต่จะอ้างอิงถึงจุดเริ่มต้นเสมอ แทนที่จะแปลว่าวันนี้เป็น "วันสุดท้ายของเดือน" เราอาจอ่านว่า "วันที่ 31 ของเดือนถัดไปหรือใช้ได้ภายในเดือนนั้น" ซึ่งหมายความว่าเราจะข้ามจากวันที่ 30 เมษายนเป็น 31 พฤษภาคมแทนที่จะเป็นวันที่ 30 พฤษภาคม โปรดทราบว่านี่ไม่ใช่เพราะเป็น "วันสุดท้ายของเดือน" แต่เป็นเพราะเราต้องการให้ "ใกล้เคียงที่สุดกับวันที่เริ่มต้นของเดือน"

สมมติว่าผู้ใช้คนหนึ่งของเราสมัครรับจดหมายข่าวฉบับอื่นเพื่อเริ่มต้นในวันที่ 2015-01-30 วันที่ใช้งานง่าย+1 monthคืออะไร? การตีความหนึ่งครั้งจะเป็น "วันที่ 30 ของเดือนถัดไปหรือใกล้เคียงที่สุด" ซึ่งจะส่งกลับ:

  • 2015-01-30
  • 2015-02-28
  • 2015-03-30
  • 2015-04-30
  • 2015-05-30
  • 2015-06-30

สิ่งนี้จะใช้ได้ดียกเว้นเมื่อผู้ใช้ของเราได้รับจดหมายข่าวทั้งสองฉบับในวันเดียวกัน สมมติว่านี่เป็นปัญหาด้านอุปทานแทนที่จะเป็นด้านอุปสงค์เราไม่กังวลว่าผู้ใช้จะรู้สึกรำคาญกับการรับจดหมายข่าว 2 ฉบับในวันเดียวกัน แต่เซิร์ฟเวอร์อีเมลของเราไม่สามารถจ่ายแบนด์วิดท์ในการส่งเป็นสองเท่าได้ จดหมายข่าวมากมาย ด้วยเหตุนี้เราจึงกลับไปใช้การตีความ "+1 เดือน" อีกแบบว่า "ส่งในวันที่สองถึงวันสุดท้ายของแต่ละเดือน" ซึ่งจะส่งกลับ:

  • 2015-01-30
  • 2015-02-27
  • 2015-03-30
  • 2015-04-29
  • 2015-05-30
  • 2558-06-29

ตอนนี้เราได้หลีกเลี่ยงการทับซ้อนกับชุดแรก แต่เราก็จบลงด้วยวันที่ 29 เมษายนและวันที่ 29 มิถุนายนซึ่งตรงกับสัญชาตญาณดั้งเดิมของเราที่+1 monthควรจะกลับมาm/$d/Yหรือสิ่งที่น่าดึงดูดและเรียบง่ายm/30/Yสำหรับเดือนที่เป็นไปได้ทั้งหมด ตอนนี้ลองพิจารณาการตีความที่สามของการ+1 monthใช้วันที่ทั้งสอง:

31 ม.ค.

  • 2015-01-31
  • 2015-03-03
  • 2015-03-31
  • 2015-05-01
  • 2015-05-31
  • 2015-07-01

30 มกราคม

  • 2015-01-30
  • 2015-03-02
  • 2015-03-30
  • 2015-04-30
  • 2015-05-30
  • 2015-06-30

ข้างต้นมีบางประเด็น เดือนกุมภาพันธ์ถูกข้ามไปซึ่งอาจเป็นปัญหาทั้งสิ้นอุปทาน (พูดว่ามีการจัดสรรแบนด์วิดท์รายเดือนหรือไม่และกุมภาพันธ์จะเสียไปและเดือนมีนาคมจะเพิ่มขึ้นเป็นสองเท่า) และสิ้นความต้องการ (ผู้ใช้รู้สึกว่าถูกโกงจากเดือนกุมภาพันธ์และรับรู้ว่าเดือนมีนาคมพิเศษ เพื่อพยายามแก้ไขข้อผิดพลาด) ในทางกลับกันสังเกตว่าชุดวันที่สองชุด:

  • ไม่ทับซ้อนกัน
  • มักจะตรงกับวันที่เมื่อเดือนนั้นมีวันที่ (ชุดวันที่ 30 มกราคมจึงดูสะอาดตา)
  • คือภายใน 3 วัน (ในกรณีส่วนใหญ่ 1 วัน) ซึ่งอาจถือว่าเป็นวันที่ "ถูกต้อง"
  • ทั้งหมดอย่างน้อย 28 วัน (เดือนจันทรคติ) จากผู้สืบทอดและบรรพบุรุษของพวกเขาดังนั้นจึงกระจายอย่างเท่าเทียมกันมาก

เมื่อพิจารณาจากสองเซ็ตสุดท้ายการย้อนกลับวันที่ใดวันหนึ่งนั้นไม่ใช่เรื่องยากหากอยู่นอกเหนือจากเดือนถัดไปที่แท้จริง (ดังนั้นย้อนกลับไปที่ 28 กุมภาพันธ์และ 30 เมษายนในเซตแรก) และอย่าเสียเวลานอนเลย การทับซ้อนกันเป็นครั้งคราวและความแตกต่างจากรูปแบบ "วันสุดท้ายของเดือน" เทียบกับ "วันที่สองถึงวันสุดท้ายของเดือน" แต่การคาดหวังว่าห้องสมุดจะเลือกระหว่าง "สวยที่สุด / เป็นธรรมชาติ" "การตีความทางคณิตศาสตร์ของ 02/31 และเดือนอื่น ๆ ล้น" และ "เทียบกับวันแรกของเดือนหรือเดือนที่แล้ว" มักจะจบลงด้วยความคาดหวังของใครบางคนและ กำหนดการบางอย่างจำเป็นต้องปรับวันที่ "ผิด" เพื่อหลีกเลี่ยงปัญหาในโลกแห่งความจริงที่การตีความ "ผิด" เกิดขึ้น

ดังนั้นอีกครั้งในขณะที่ฉันคาดหวังว่า+1 monthจะคืนวันที่ซึ่งเป็นจริงในเดือนถัดไป แต่ก็ไม่ง่ายเหมือนสัญชาตญาณและการเลือกตัวเลือกการใช้คณิตศาสตร์มากกว่าความคาดหวังของนักพัฒนาเว็บอาจเป็นทางเลือกที่ปลอดภัย

นี่เป็นทางเลือกอื่นที่ยังคงเป็นปัญหา แต่ฉันคิดว่าได้ผลลัพธ์ที่ดี:

foreach(range(0,5) as $count) {
    $new_date = clone $date;
    $new_date->modify("+$count month");
    $expected_month = $count + 1;
    $actual_month = $new_date->format("m");
    if($expected_month != $actual_month) {
        $new_date = clone $date;
        $new_date->modify("+". ($count - 1) . " month");
        $new_date->modify("+4 weeks");
    }
    
    echo "* " . nl2br($new_date->format("Y-m-d") . PHP_EOL);
}

ไม่ใช่สิ่งที่ดีที่สุด แต่ตรรกะพื้นฐานคือ: หากเพิ่มผลลัพธ์ 1 เดือนในวันที่อื่นที่ไม่ใช่เดือนหน้าที่คาดไว้ให้ทิ้งวันที่นั้นแล้วเพิ่ม 4 สัปดาห์แทน นี่คือผลการทดสอบสองวัน:

31 ม.ค.

  • 2015-01-31
  • 2015-02-28
  • 2015-03-31
  • 2015-04-28
  • 2015-05-31
  • 2015-06-28

30 มกราคม

  • 2015-01-30
  • 2015-02-27
  • 2015-03-30
  • 2015-04-30
  • 2015-05-30
  • 2015-06-30

(รหัสของฉันยุ่งเหยิงและไม่สามารถใช้งานได้ในสถานการณ์หลายปีฉันยินดีต้อนรับทุกคนให้เขียนโซลูชันใหม่ด้วยรหัสที่สวยงามยิ่งขึ้นตราบใดที่หลักฐานอ้างอิงยังคงอยู่ครบถ้วนเช่นถ้า +1 เดือนส่งคืนวันที่ขี้ขลาดให้ใช้ +4 สัปดาห์แทน)


4

ฉันสร้างฟังก์ชันที่ส่งคืน DateInterval เพื่อให้แน่ใจว่าการเพิ่มเดือนจะแสดงเดือนถัดไปและลบวันในหลังจากนั้น

$time = new DateTime('2014-01-31');
echo $time->format('d-m-Y H:i') . '<br/>';

$time->add( add_months(1, $time));

echo $time->format('d-m-Y H:i') . '<br/>';



function add_months( $months, \DateTime $object ) {
    $next = new DateTime($object->format('d-m-Y H:i:s'));
    $next->modify('last day of +'.$months.' month');

    if( $object->format('d') > $next->format('d') ) {
        return $object->diff($next);
    } else {
        return new DateInterval('P'.$months.'M');
    }
}

4

เมื่อรวมกับคำตอบของ shamittomar แล้วอาจเป็นเช่นนี้สำหรับการเพิ่มเดือน "อย่างปลอดภัย":

/**
 * Adds months without jumping over last days of months
 *
 * @param \DateTime $date
 * @param int $monthsToAdd
 * @return \DateTime
 */

public function addMonths($date, $monthsToAdd) {
    $tmpDate = clone $date;
    $tmpDate->modify('first day of +'.(int) $monthsToAdd.' month');

    if($date->format('j') > $tmpDate->format('t')) {
        $daysToAdd = $tmpDate->format('t') - 1;
    }else{
        $daysToAdd = $date->format('j') - 1;
    }

    $tmpDate->modify('+ '. $daysToAdd .' days');


    return $tmpDate;
}

ขอบคุณมาก!!
ตุ๊กแก

2

ฉันพบวิธีที่สั้นกว่าโดยใช้รหัสต่อไปนี้:

                   $datetime = new DateTime("2014-01-31");
                    $month = $datetime->format('n'); //without zeroes
                    $day = $datetime->format('j'); //without zeroes

                    if($day == 31){
                        $datetime->modify('last day of next month');
                    }else if($day == 29 || $day == 30){
                        if($month == 1){
                            $datetime->modify('last day of next month');                                
                        }else{
                            $datetime->modify('+1 month');                                
                        }
                    }else{
                        $datetime->modify('+1 month');
                    }
echo $datetime->format('Y-m-d H:i:s');

1

นี่คือการนำคำตอบของ Juhanaเวอร์ชันปรับปรุงไปใช้ในคำถามที่เกี่ยวข้อง:

<?php
function sameDateNextMonth(DateTime $createdDate, DateTime $currentDate) {
    $addMon = clone $currentDate;
    $addMon->add(new DateInterval("P1M"));

    $nextMon = clone $currentDate;
    $nextMon->modify("last day of next month");

    if ($addMon->format("n") == $nextMon->format("n")) {
        $recurDay = $createdDate->format("j");
        $daysInMon = $addMon->format("t");
        $currentDay = $currentDate->format("j");
        if ($recurDay > $currentDay && $recurDay <= $daysInMon) {
            $addMon->setDate($addMon->format("Y"), $addMon->format("n"), $recurDay);
        }
        return $addMon;
    } else {
        return $nextMon;
    }
}

เวอร์ชันนี้$createdDateอยู่ภายใต้การสันนิษฐานว่าคุณกำลังจัดการกับรอบระยะเวลารายเดือนที่เกิดขึ้นประจำเช่นการสมัครใช้งานที่เริ่มต้นในวันที่ระบุเช่นวันที่ 31 $createdDateวันที่ "เกิดซ้ำบน" มักจะใช้เวลาช้ามากจะไม่เปลี่ยนเป็นค่าที่ต่ำกว่าเนื่องจากจะถูกผลักไปข้างหน้าผ่านเดือนที่มีมูลค่าน้อยกว่า (เช่นดังนั้นวันที่เกิดซ้ำทั้ง 29, 30 หรือ 31 จะไม่ติดค้างในวันที่ 28 หลังจากผ่านไป ถึงเดือนกุมภาพันธ์ที่ไม่ใช่ปีอธิกสุรทิน)

นี่คือรหัสไดรเวอร์สำหรับทดสอบอัลกอริทึม:

$createdDate = new DateTime("2015-03-31");
echo "created date = " . $createdDate->format("Y-m-d") . PHP_EOL;

$next = sameDateNextMonth($createdDate, $createdDate);
echo "   next date = " . $next->format("Y-m-d") . PHP_EOL;

foreach(range(1, 12) as $i) {
    $next = sameDateNextMonth($createdDate, $next);
    echo "   next date = " . $next->format("Y-m-d") . PHP_EOL;
}

ผลลัพธ์ใด:

created date = 2015-03-31
   next date = 2015-04-30
   next date = 2015-05-31
   next date = 2015-06-30
   next date = 2015-07-31
   next date = 2015-08-31
   next date = 2015-09-30
   next date = 2015-10-31
   next date = 2015-11-30
   next date = 2015-12-31
   next date = 2016-01-31
   next date = 2016-02-29
   next date = 2016-03-31
   next date = 2016-04-30

1

นี่คือคำตอบของ Kasihasiเวอร์ชันปรับปรุงในคำถามที่เกี่ยวข้อง สิ่งนี้จะเพิ่มหรือลบจำนวนเดือนโดยพลการเป็นวันที่ได้อย่างถูกต้อง

public static function addMonths($monthToAdd, $date) {
    $d1 = new DateTime($date);

    $year = $d1->format('Y');
    $month = $d1->format('n');
    $day = $d1->format('d');

    if ($monthToAdd > 0) {
        $year += floor($monthToAdd/12);
    } else {
        $year += ceil($monthToAdd/12);
    }
    $monthToAdd = $monthToAdd%12;
    $month += $monthToAdd;
    if($month > 12) {
        $year ++;
        $month -= 12;
    } elseif ($month < 1 ) {
        $year --;
        $month += 12;
    }

    if(!checkdate($month, $day, $year)) {
        $d2 = DateTime::createFromFormat('Y-n-j', $year.'-'.$month.'-1');
        $d2->modify('last day of');
    }else {
        $d2 = DateTime::createFromFormat('Y-n-d', $year.'-'.$month.'-'.$day);
    }
    return $d2->format('Y-m-d');
}

ตัวอย่างเช่น:

addMonths(-25, '2017-03-31')

จะส่งออก:

'2015-02-28'

0

หากคุณต้องการหลีกเลี่ยงการข้ามเดือนคุณสามารถดำเนินการบางอย่างเช่นนี้เพื่อให้วันที่ออกและเรียกใช้วนรอบในเดือนถัดไปโดยลดวันที่ลงทีละวันและตรวจสอบอีกครั้งจนกว่าจะถึงวันที่ที่ถูกต้องโดยที่ $ start_calculated เป็นสตริงที่ถูกต้องสำหรับ strtotime (เช่น mysql datetime หรือ "now") ซึ่งจะพบว่าสิ้นเดือนอยู่ที่ 1 นาทีถึงเที่ยงคืนแทนที่จะข้ามเดือน

    $start_dt = $starting_calculated;

    $next_month = date("m",strtotime("+1 month",strtotime($start_dt)));
    $next_month_year = date("Y",strtotime("+1 month",strtotime($start_dt)));

    $date_of_month = date("d",$starting_calculated);

    if($date_of_month>28){
        $check_date = false;
        while(!$check_date){
            $check_date = checkdate($next_month,$date_of_month,$next_month_year);
            $date_of_month--;
        }
        $date_of_month++;
        $next_d = $date_of_month;
    }else{
        $next_d = "d";
    }
    $end_dt = date("Y-m-$next_d 23:59:59",strtotime("+1 month"));



0

ฉันต้องการหาวันที่สำหรับ 'เดือนนี้ของปีที่แล้ว' และมันจะไม่เป็นที่พอใจอย่างรวดเร็วเมื่อเดือนนี้คือเดือนกุมภาพันธ์ในปีอธิกสุรทิน อย่างไรก็ตามฉันเชื่อว่ามันได้ผล ... : - / เคล็ดลับดูเหมือนจะอิงกับการเปลี่ยนแปลงของคุณในวันที่ 1 ของเดือน

$this_month_last_year_end = new \DateTime();
$this_month_last_year_end->modify('first day of this month');
$this_month_last_year_end->modify('-1 year');
$this_month_last_year_end->modify('last day of this month');
$this_month_last_year_end->setTime(23, 59, 59);

0
$ds = new DateTime();
$ds->modify('+1 month');
$ds->modify('first day of this month');

1
คุณต้องอธิบายคำตอบของคุณ รหัสคำตอบเท่านั้นที่ถือว่ามีคุณภาพต่ำ
Machavity

ขอบคุณ! นี่คือคำตอบที่ชัดเจนที่สุด หากคุณสลับ 2 บรรทัดสุดท้ายมันจะให้เดือนที่ถูกต้องเสมอ ความรุ่งโรจน์!
Danny Schoemann

0
$month = 1; $year = 2017;
echo date('n', mktime(0, 0, 0, $month + 2, -1, $year));

จะออก2(กุมภาพันธ์) จะทำงานในเดือนอื่น ๆ ด้วย


0
$current_date = new DateTime('now');
$after_3_months = $current_date->add(\DateInterval::createFromDateString('+3 months'));

เป็นเวลาหลายวัน:

$after_3_days = $current_date->add(\DateInterval::createFromDateString('+3 days'));

สำคัญ:

เมธอดadd()ของคลาส DateTime จะแก้ไขค่าอ็อบเจ็กต์ดังนั้นหลังจากเรียกadd()ใช้ DateTime Object มันจะส่งคืนอ็อบเจ็กต์วันที่ใหม่และแก้ไขอ็อบเจ็กต์ด้วยตนเอง


0

คุณสามารถทำได้โดยใช้วันที่ () และ strtotime () เช่นกัน ตัวอย่างเช่นการเพิ่ม 1 เดือนในวันนี้:

date("Y-m-d",strtotime("+1 month",time()));

หากคุณต้องการใช้คลาส datetime ก็ใช้ได้เช่นกัน แต่มันก็ง่ายเหมือนกัน รายละเอียดเพิ่มเติมที่นี่


0

คำตอบที่ได้รับการยอมรับแล้วอธิบายว่าเหตุใดจึงไม่ใช่ แต่และคำตอบอื่น ๆ บางคำก็เป็นวิธีแก้ปัญหาที่เป็นระเบียบด้วยนิพจน์ php เช่น first day of the +2 monthsก่อให้เกิดการแก้ปัญหาที่เรียบร้อยกับการแสดงออกเช่น ปัญหาของนิพจน์เหล่านี้คือไม่ได้เติมข้อความอัตโนมัติ

วิธีแก้ปัญหาค่อนข้างง่าย ขั้นแรกคุณควรหานามธรรมที่เป็นประโยชน์ซึ่งสะท้อนถึงพื้นที่ปัญหาของคุณ ในกรณีนี้มันคือISO8601DateTimeไฟล์. ประการที่สองควรมีการใช้งานหลายอย่างที่สามารถนำมาใช้แทนข้อความที่ต้องการได้ ตัวอย่างเช่นToday, Tomorrow, The first day of this month, Future- ทั้งหมดเป็นตัวแทนของการดำเนินงานที่เฉพาะเจาะจงของISO8601DateTimeแนวคิด

TheFirstDayOfNMonthsLaterดังนั้นในกรณีของการดำเนินการที่คุณต้องมี ง่ายต่อการค้นหาเพียงดูที่รายการคลาสย่อยใน IDE ใด ๆ นี่คือรหัส:

$start = new DateTimeParsedFromISO8601String('2000-12-31');
$firstDayOfOneMonthLater = new TheFirstDayOfNMonthsLater($start, 1);
$firstDayOfTwoMonthsLater = new TheFirstDayOfNMonthsLater($start, 2);
var_dump($start->value()); // 2000-12-31T00:00:00+00:00
var_dump($firstDayOfOneMonthLater->value()); // 2001-01-01T00:00:00+00:00
var_dump($firstDayOfTwoMonthsLater->value()); // 2001-02-01T00:00:00+00:00

สิ่งเดียวกันกับวันสุดท้ายของเดือน สำหรับตัวอย่างเพิ่มเติมของวิธีการนี้อ่านบทความนี้


โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.