ค้นหาจำนวนสัปดาห์ในหนึ่งเดือน

number of weeks in calendar

จากภาพตัวอย่าง เป็นการรวบรวมปฏิทินในเดือนต่างๆที่มีวันที่ตั้งแต่ 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);

ผลการทดสอบ

weeks in month test result

ผลลัพธ์จากการหาค่าจำนวนสัปดาห์ต่อเดือน สามารถนำไปใช้งานต่อยอดได้อีก เช่น ใช้สำหรับคำณวนและสั่งเขียนปฏิทินออกมา เป็นต้น.

 

ใส่ความเห็น

อีเมลของคุณจะไม่แสดงให้คนอื่นเห็น

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>