จากภาพตัวอย่าง เป็นการรวบรวมปฏิทินในเดือนต่างๆที่มีวันที่ตั้งแต่ 28 - 31 วัน และวันที่เริ่มต้นของเดือนตั้งแต่วันอาทิตย์ไปจนถึงวันเสาร์ เพื่อให้เห็นรูปแบบของปฏิทินที่มีทั้งหมดว่าเป็นอย่างไรบ้าง. แม้วันที่ในเดือนอื่นๆปีอื่นๆจะต่างออกไปจากตัวอย่างนี้ แต่ถ้าทราบวันที่เริ่มต้นของเดือนแล้ว วันที่สิ้นสุดของเดือนก็สามารถเอามาเทียบกันได้อย่างไม่มีผิดเพี้ยน.
สูตร
โดยหลักการ จะหาความแน่นอนที่สุดเป็นตัวนำ นั่นคือเดือนที่มี 29 วัน เพราะว่าไม่ว่าวันที่เริ่มต้นของเดือนจะเป็นวันใดก็ตาม มันจะมีทั้งหมดแค่ 5 สัปดาห์ต่อเดือนเสมอ. โดยใช้สูตรดังต่อไปนี้.
ความต่างของเลขวัน = (เลขวันสุดท้าย-(เลขวันเริ่มต้น-1))
ตัวอย่าง เดือน ก.พ. 2012
เลขวันสุดท้ายคือ 3 (วันพุธ) เลขวันเริ่มต้นคือ 3 (วันพุธ) -1 คือค่าตายตัว (ยังหาเหตุผลไม่ได้ ผู้เชี่ยวชาญทางคณิตโปรดอธิบายด้วย)
ก็จะได้เป็น 3-(3-1) = 1
จากนั้นหาค่าจำนวนสัปดาห์
จำนวนสัปดาห์ = 4+ความต่างของเลขวัน
ตัวอย่างเดือน ก.พ. 2012
4 คือจำนวนวันที่ทั้งหมดในเดือนหารด้วยจำนวนวันต่อสัปดาห์ จะได้ 4 เสมอ ต่างกันเพียงจุดทศนิยม จึงเอา 4 ตั้ง.
4+1 = 5
สำหรับเดือนที่มีวันน้อยกว่า 29 ก็ให้เปลี่ยนค่าตายตัวจาก -1 เป็น -2 แล้วเช็คค่าคำณวนถ้าน้อยกว่า 0 ก็ถือว่าจำนวนรอบต่ำกว่า ให้กำหนดค่าความต่างเลขวันเป็น 0
เดือนที่มีวันที่มากกว่า 29 ก็ให้เปลี่ยนค่าตายตัวจาก -1 เป็น 0 หรือมากขึ้นเรื่อยๆตามจำนวนวันที่มีมากกว่า เช่น เดือนที่มี 31 วันก็ +1. แล้วเช็คค่าคำณวนของความต่างเลขวันไปตามลำดับรอบ.
ทั้งนี้ สูตรนี้อาจไม่ใช่วิธีที่ดีที่สุด เนื่องจากผู้เขียนไม่มีความเชี่ยวชาญทางคณิตศาสตร์ จึงอาจจะมีวิธีที่ดีกว่านี้มากอยู่ ก็ขออภัยไว้ล่วงหน้า.
การทดสอบ
เพื่อที่จะทำการทดสอบ เดือนและปีทั้งหมดตามภาพ จึงขอทำเป็นข้อมูลลงใน array เพื่อนำมาใช้ทดสอบนี้.
test-date-set.php
<?php
$test_date = [];
$test_date[28] = ['2009-02', '2010-02', '2011-02', '2006-02', '2007-02', '2013-02', '2014-02'];
$test_date[29] = ['2032-02', '2016-02', '2028-02', '2012-02', '2024-02', '2036-02', '2020-02'];
$test_date[30] = ['2015-11', '2015-06', '2014-04', '2015-04', '2012-11', '2012-06', '2013-06'];
$test_date[31] = ['2015-03', '2014-12', '2014-07', '2014-10', '2014-05', '2013-03', '2014-03'];
weeks-in-month-function.php
<?php
/**
* Get total weeks in a month
*
* @author Vee W.
* @param string $month Current month in number. For example: 04 for April.
* @param string $year Current year in number 4 digits. For example: 2016
* @param integer $first_day_of_week First day of week. (Sunday is 0, Monday is 1, Tuesday is 2, ..., Saturday is 6).
* @return mixed Return the number of total weeks in this month and year. Return false if failed to work.
*/
function weeksInMonth($month, $year, $first_day_of_week = 0)
{
$timestamp_first_day = mktime(0, 0, 0, $month, 1, $year);
$total_days = date('t', $timestamp_first_day);
$timestamp_last_day = mktime(0, 0, 0, $month, $total_days, $year);
// by default the date('w') will got 0(sunday) - 6(saturday)
$daynum_firstday = (date('w', $timestamp_first_day) - $first_day_of_week);
$daynum_lastday = (date('w', $timestamp_last_day) - $first_day_of_week);
if ($daynum_firstday > 7) {
$daynum_firstday = 0;
} elseif ($daynum_firstday < 0) {
$daynum_firstday = (6 - ((-$daynum_firstday) - 1));
}
if ($daynum_lastday > 7) {
$daynum_lastday = 0;
} elseif ($daynum_lastday < 0) {
$daynum_lastday = (6 - ((-$daynum_lastday) - 1));
}
unset($timestamp_first_day, $timestamp_last_day);
// find total weeks (total days / 7) 7 means 7 days a week.
// 31/7 = 4.4285714285714285714285714285714
// 30/7 = 4.2857142857142857142857142857143
// 29/7 = 4.1428571428571428571428571428571
// 28/7 = 4
// this will always be 4 and 4.xxx, just use 4.
$total_weeks_approximately = 4;
switch ($total_days) {
case 28:
$daynum_firstday_offset = ($daynum_firstday - 1) - 1;
$difference_of_day_number = ($daynum_lastday - $daynum_firstday_offset);
if ($daynum_lastday > $daynum_firstday) {
// this means fewer the round.
$difference_of_day_number = 0;
}
$total_weeks = ($total_weeks_approximately + $difference_of_day_number);
break;
case 29:
$daynum_firstday_offset = ($daynum_firstday - 1) - 0;
$difference_of_day_number = ($daynum_lastday - $daynum_firstday_offset);
$total_weeks = ($total_weeks_approximately + $difference_of_day_number);
break;
case 30:
$daynum_firstday_offset = ($daynum_firstday - 1) + 1;
$difference_of_day_number = ($daynum_lastday - $daynum_firstday_offset);
if ($daynum_lastday < $daynum_firstday) {
// this means over the round.
$difference_of_day_number = 2;
}
$total_weeks = ($total_weeks_approximately + $difference_of_day_number);
break;
case 31:
$daynum_firstday_offset = ($daynum_firstday - 1) + 2;
$difference_of_day_number = ($daynum_lastday - $daynum_firstday_offset);
if ($daynum_lastday < $daynum_firstday) {
// this means over the round.
$difference_of_day_number = 2;
}
$total_weeks = ($total_weeks_approximately + $difference_of_day_number);
break;
default:
unset($daynum_firstday, $daynum_lastday, $total_days, $total_weeks_approximately);
return false;
}
unset($daynum_firstday, $daynum_firstday_offset, $daynum_lastday, $difference_of_day_number, $total_days, $total_weeks_approximately);
if (isset($total_weeks)) {
return $total_weeks;
}
}// weeksInMonth
test.php
<?php
require __DIR__.'/test-date-set.php';
require_once __DIR__.'/weeks-in-month-function.php';
foreach ($test_date as $key => $items) {
if (is_array($items)) {
echo '<h4>'.$key.' days in month</h4>'."\n";
foreach ($items as $item) {
$item_exp = explode('-', $item);
$current_month = end($item_exp);
$current_year = $item_exp[0];
unset($item_exp);
// showing month year.
$timestamp = mktime(0, 0, 0, $current_month);
$formatted_datetime = strftime('%B', $timestamp);
unset($timestamp);
$detected_strftime_encoding = mb_detect_encoding($formatted_datetime, 'auto', true);
if (strtoupper($detected_strftime_encoding) != 'UTF-8') {
$showing_month = iconv($detected_strftime_encoding, 'UTF-8', $formatted_datetime);
} else {
unset($detected_strftime_encoding);
$showing_month = $formatted_datetime;
}
echo $showing_month.' '.$current_year.'<br>'."\n";
unset($detected_strftime_encoding, $formatted_datetime, $showing_month);
// showing what day of week for first day
$timestamp = mktime(0, 0, 0, $current_month, 1, $current_year);
$formatted_datetime = strftime('%A', $timestamp);
$detected_strftime_encoding = mb_detect_encoding($formatted_datetime, 'auto', true);
if (strtoupper($detected_strftime_encoding) != 'UTF-8') {
$showing_day = iconv($detected_strftime_encoding, 'UTF-8', $formatted_datetime);
} else {
unset($detected_strftime_encoding);
$showing_day = $formatted_datetime;
}
echo 'first day: '.$showing_day.' 1<br>'."\n";
unset($detected_strftime_encoding, $formatted_datetime, $showing_day);
// showing what day of week for last day
$total_days = date('t', $timestamp);
$timestamp_last_day = mktime(0, 0, 0, $current_month, $total_days, $current_year);
$formatted_datetime = strftime('%A', $timestamp_last_day);
$detected_strftime_encoding = mb_detect_encoding($formatted_datetime, 'auto', true);
if (strtoupper($detected_strftime_encoding) != 'UTF-8') {
$showing_day = iconv($detected_strftime_encoding, 'UTF-8', $formatted_datetime);
} else {
unset($detected_strftime_encoding);
$showing_day = $formatted_datetime;
}
echo 'last day: '.$showing_day.' '.$total_days.'<br>'."\n";
unset($detected_strftime_encoding, $formatted_datetime, $showing_day);
// showing total weeks
echo 'total weeks: '.weeksInMonth($current_month, $current_year).'<br>'."\n";
echo '<br>'."\n";
unset($total_days);
}
}
echo '<hr>'."\n";
}
unset($item, $items, $key, $test_date);
ผลการทดสอบ
ผลลัพธ์จากการหาค่าจำนวนสัปดาห์ต่อเดือน สามารถนำไปใช้งานต่อยอดได้อีก เช่น ใช้สำหรับคำณวนและสั่งเขียนปฏิทินออกมา เป็นต้น.