मैं ओपी की भावना से सहमत हूं कि यह प्रति-सहज और निराशाजनक है, लेकिन यह निर्धारित कर रहा +1 month
है कि परिदृश्य में क्या होता है। इन उदाहरणों पर विचार करें:
आप 2015-01-31 से शुरू करते हैं और ईमेल न्यूज़लेटर भेजने के लिए शेड्यूलिंग चक्र प्राप्त करने के लिए 6 महीने जोड़ना चाहते हैं। ओपी की शुरुआती उम्मीदों को ध्यान में रखते हुए, यह वापसी करेगा:
- 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 मई के बजाय 30 अप्रैल से 31 मई तक कूदेंगे। ध्यान दें कि यह इसलिए नहीं है क्योंकि यह "महीने का अंतिम दिन" है, लेकिन क्योंकि हम चाहते हैं कि "महीने की शुरुआत से सबसे करीबी उपलब्ध हो।"
तो मान लीजिए कि हमारा एक उपयोगकर्ता 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
- 2015/06/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 सप्ताह।)