ฉันเห็นด้วยกับความเชื่อมั่นของ 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 สัปดาห์แทน)