Open source

แปลงวันที่เป็นภาษาไทย ปี พ.ศ. ใน PHP และ Laravel ด้วย thaidate()

ใช้ format เดียวกับ date() ของ PHP แต่ได้วันที่ภาษาไทยปี พ.ศ. แถมเลขไทย ปีงบประมาณ และอ่านวันที่ พ.ศ. ที่ผู้ใช้กรอกกลับเป็น Carbon

เผยแพร่
อัปเดต
— เวอร์ชัน 14 เพิ่มเลขไทย ปีงบประมาณ และอ่านวันที่ พ.ศ. จากฟอร์ม
เวลาอ่าน
5 นาที

ฟังก์ชัน date() ของ PHP จัดรูปแบบวันที่ได้แทบทุกแบบที่เราอยากได้ ติดแค่ชื่อวันกับชื่อเดือนออกมาเป็นภาษาอังกฤษ และปีเป็น ค.ศ. เสมอ ส่วนงานที่ทำให้คนไทยใช้ ทั้งเอกสาร ใบเสร็จ และหน้าจอ เรามักอยากได้ "10 ตุลาคม 2569" มากกว่า "10 October 2026"

ผมเลยทำแพ็กเกจ phattarachai/thaidate ขึ้นมา แพ็กเกจนี้เพิ่มฟังก์ชัน thaidate() ที่รับ format แบบเดียวกับ date() แต่ได้ชื่อวัน ชื่อเดือนภาษาไทยและปี พ.ศ. ออกมาแทน แล้วยังเพิ่มเมธอดให้ Carbon ใน Laravel อีกชุด

ติดตั้ง

composer require phattarachai/thaidate

เวอร์ชันปัจจุบันใช้ได้กับ PHP 8.3 ขึ้นไป และ Laravel 12 หรือ 13 ลงแล้วใช้ได้เลย Laravel เจอ service provider ของแพ็กเกจเอง ไม่ต้องตั้งค่าอะไรเพิ่ม (โพสต์นี้เขียนตามเวอร์ชัน 14)

ใช้ฟังก์ชัน thaidate()

เรียกเปล่า ๆ ไม่ส่งอะไรเลย จะได้วันนี้ในรูปแบบ j F Y

echo thaidate();
// 10 ตุลาคม 2569

พารามิเตอร์ตัวแรกคือ format ใช้ตัวอักษรชุดเดียวกับ Date format ของ PHP ส่วนข้อความไทยที่เราพิมพ์แทรกไว้ใน format ก็ออกมาตามนั้น

echo thaidate('l j F Y', '2026-10-10');
// เสาร์ 10 ตุลาคม 2569

echo thaidate('D j M y', '2026-10-10');
// ส. 10 ต.ค. 69

echo thaidate('วันlที่ j F พ.ศ. Y เวลา H:i น.', '2026-10-10 14:30');
// วันเสาร์ที่ 10 ตุลาคม พ.ศ. 2569 เวลา 14:30 น.

ตัวอักษรที่ thaidate() แปลงเป็นภาษาไทยมีแค่ชุดนี้ ตัวอื่น (j, d, H, i ฯลฯ) ทำงานเหมือน date() ทุกอย่าง

Format ความหมาย ตัวอย่าง
l ชื่อวันเต็ม เสาร์
D ชื่อวันย่อ ส.
F ชื่อเดือนเต็ม ตุลาคม
M ชื่อเดือนย่อ ต.ค.
Y ปี พ.ศ. 4 หลัก 2569
y ปี พ.ศ. 2 หลัก 69
o ปี ISO-8601 แบบ พ.ศ. 2569

ถ้าอยากได้ตัวอักษรพวกนี้ตรง ๆ ไม่ให้แปลง ก็ใส่ backslash นำหน้าเหมือนใน date() เช่น thaidate('\Y\e\a\r Y') ได้ Year 2569

ส่งวันที่ได้ทั้ง timestamp, string และ DateTime

พารามิเตอร์ตัวที่สองคือวันที่ที่จะแสดง ส่ง Unix timestamp, DateTime, Carbon หรือ string ที่ PHP อ่านเป็นวันที่ได้ก็ได้ ทั้งแบบ 2026-10-10 และแบบนับจากตอนนี้อย่าง tomorrow ถ้าส่ง null หรือไม่ส่งเลยก็ได้วันนี้

thaidate('j F Y', 'now');
thaidate('j F Y', 'next month');
thaidate('j F Y', '-3 days');
thaidate('j F Y', 'tomorrow');
thaidate('j M Y', new DateTimeImmutable('2026-10-10'));
// 10 ต.ค. 2569

ใช้ปี ค.ศ. แทน พ.ศ.

ถ้าอยากได้ชื่อวันชื่อเดือนภาษาไทย แต่ปีเป็น ค.ศ. ให้ส่ง false เป็นพารามิเตอร์ตัวที่สาม

thaidate('j F Y', '2026-10-10', false);
// 10 ตุลาคม 2026

ใช้กับ Carbon และ Eloquent

Laravel ใช้ Carbon กับวันที่อยู่แล้ว ผมจึงเพิ่มเมธอด thaidate() ให้ Carbon ผ่าน macro ด้วย เรียกจาก instance ได้เลย ทั้ง Carbon และ CarbonImmutable

use Illuminate\Support\Carbon;

Carbon::parse('2026-10-10')->thaidate();
// 10 ตุลาคม 2569

Carbon::parse('2026-10-10')->thaidate('D j M y');
// ส. 10 ต.ค. 69

Carbon::parse('2026-10-10')->thaidate('j F Y', false);
// 10 ตุลาคม 2026

เมธอดนี้มีวันที่อยู่ในตัวแล้ว เลยรับแค่ $format, $buddhistEra และ $thaiDigits ตามลำดับ false สำหรับปี ค.ศ. จึงอยู่ตัวที่สองของเมธอด แต่อยู่ตัวที่สามของฟังก์ชัน thaidate()

field ใน Eloquent model ที่ cast เป็นวันที่ เป็น Carbon อยู่แล้ว อย่าง created_at ก็เรียก ->thaidate() ต่อได้เลย

$user->created_at->thaidate();
// 10 ตุลาคม 2569

ถ้าเป็นคอลัมน์วันที่ที่เราเพิ่มเอง ให้ประกาศ cast ไว้ใน model ก่อน

class Post extends Model
{
    protected function casts(): array
    {
        return [
            'published_date' => 'date',
        ];
    }
}

$post->published_date->thaidate('D j M y');

ใน Blade ก็เรียกแบบเดียวกัน

<p>เผยแพร่เมื่อ {{ $post->published_date->thaidate() }}</p>

แอปเก็บเวลาเป็น UTC ก็แสดงเวลาไทยได้

Carbon และ DateTime แสดงเวลาตาม timezone ที่ถืออยู่ในตัว Carbon ที่ตั้งเป็น Asia/Bangkok ก็ออกมาเป็นเวลาไทย ถึงแอปจะตั้ง timezone หลักเป็น UTC ก็ตาม ส่วน timestamp กับ string ไม่มี timezone ในตัว เลยใช้ timezone หลักของแอป (APP_TIMEZONE) เหมือน date()

// APP_TIMEZONE=UTC
Carbon::parse('2026-10-10 23:30', 'Asia/Bangkok')->thaidate('j F Y H:i');
// 10 ตุลาคม 2569 23:30

$order->created_at->setTimezone('Asia/Bangkok')->thaidate('j M Y H:i');

ถ้าแอปเก็บเวลาเป็น UTC แต่ผู้ใช้อยู่ในไทย ก็เรียก setTimezone('Asia/Bangkok') ก่อนแสดงผลได้เลย

พิมพ์วันที่เป็นเลขไทย

หนังสือราชการและเอกสารทางการหลายแบบยังใช้เลขไทย ส่ง thaiDigits: true เข้าไป ตัวเลขในวันที่ก็จะออกมาเป็นเลขไทย

echo thaidate('j F พ.ศ. Y', '2026-10-10', thaiDigits: true);
// ๑๐ ตุลาคม พ.ศ. ๒๕๖๙

echo now()->thaidate('วันที่ j เดือน F พ.ศ. Y', thaiDigits: true);
// วันที่ ๑๐ เดือน ตุลาคม พ.ศ. ๒๕๖๙

แปลงเฉพาะตัวเลขที่มาจากวันที่ ข้อความที่เราพิมพ์ไว้ใน format ออกมาตามเดิม

หาปีงบประมาณของวันที่

ปีงบประมาณของภาครัฐเริ่ม 1 ตุลาคม จบ 30 กันยายน และเรียกตามปีที่จบ วันที่ 10 ตุลาคม 2569 จึงอยู่ในปีงบประมาณ 2570 ระบบที่ทำให้หน่วยงานรัฐมักต้องใช้เลขนี้ทั้งตอนออกรายงานและตอนค้นข้อมูล

Carbon::parse('2026-10-10')->thaiFiscalYear();        // 2570
Carbon::parse('2026-09-30')->thaiFiscalYear();        // 2569
Carbon::parse('2026-10-10')->thaiFiscalYear(false);   // 2027 (ค.ศ.)

now()->startOfThaiFiscalYear();   // 2026-10-01 00:00:00
now()->endOfThaiFiscalYear();     // 2027-09-30 23:59:59

วันเริ่มกับวันสิ้นปีงบส่งเข้า query ได้ตรง ๆ เช่น หายอดรวมใบแจ้งหนี้ของปีงบประมาณนี้

Invoice::whereBetween('issued_at', [now()->startOfThaiFiscalYear(), now()->endOfThaiFiscalYear()])
    ->sum('total');

อ่านวันที่ พ.ศ. ที่ผู้ใช้กรอกในฟอร์ม

คนไทยกรอกวันที่เป็น พ.ศ. กันจนชิน เช่น 10/10/2569 ถ้าเราส่งค่านี้ให้ Carbon::createFromFormat() ตรง ๆ ฐานข้อมูลจะได้ปี ค.ศ. 2569 ไปเก็บ Carbon::createFromThaiFormat() ทำกลับด้านกับ thaidate() คืออ่านวันที่ภาษาไทยแล้วคืน Carbon ที่ปีถูกต้องให้

Carbon::createFromThaiFormat('d/m/Y', '10/10/2569');
// 2026-10-10 00:00:00

Carbon::createFromThaiFormat('j F Y', '10 ตุลาคม 2569');
Carbon::createFromThaiFormat('j M y', '10 ต.ค. 69');
Carbon::createFromThaiFormat('วันlที่ j F พ.ศ. Y', 'วันเสาร์ที่ 10 ตุลาคม พ.ศ. 2569');
Carbon::createFromThaiFormat('j F Y H:i', '๑๐ ตุลาคม ๒๕๖๙ ๐๙:๐๕');
// ได้ 2026-10-10 ทุกบรรทัด
  • Y อ่านเป็นปี พ.ศ. ส่วน y อ่านปี 2 หลักเป็น 25xx (69 คือ 2569)
  • F, M, l และ D อ่านชื่อเดือนและชื่อวันภาษาไทยได้ทั้งแบบเต็มและแบบย่อ
  • อ่านเลขไทยได้ทุกตำแหน่ง
  • ส่วนที่ format ไม่ได้บอก จะได้ค่าของวันนี้และเวลาเที่ยงคืน d/m/Y จึงได้ 00:00:00
  • วันที่ที่ไม่มีอยู่จริง เช่น 29/02/2568 หรือค่าที่ไม่ตรงกับ format จะโยน InvalidFormatException

เมธอดนี้เช็กปีอธิกสุรทินจากปี ค.ศ. ที่แปลงแล้ว 29/02/2567 (ค.ศ. 2024) จึงผ่าน ถ้ามี timezone ให้ส่งเป็นพารามิเตอร์ตัวที่สาม และถ้าเรียกจาก CarbonImmutable ก็ได้ CarbonImmutable กลับมา

ใน Form Request เราตรวจค่าได้ด้วยการลองอ่านดู อ่านไม่ได้ก็ไม่ผ่าน

use Carbon\Exceptions\InvalidFormatException;
use Illuminate\Support\Carbon;

public function rules(): array
{
    return [
        'born_on' => ['required', function (string $attribute, mixed $value, Closure $fail) {
            try {
                Carbon::createFromThaiFormat('d/m/Y', (string) $value);
            } catch (InvalidFormatException) {
                $fail('กรุณากรอกวันที่เป็น วว/ดด/ปปปป (พ.ศ.)');
            }
        }],
    ];
}

ถ้าฟอร์มทำด้วย Filament จะให้ผู้ใช้เลือกจาก DatePicker ปี พ.ศ. แทนพิมพ์เองก็ได้ ถ้าอยากเห็นวิธีทำ ลองอ่านต่อที่ แสดง DatePicker ปี พ.ศ. เป็นภาษาไทย ด้วย FilamentPHP ได้ครับ

งานวันที่บางอย่าง Carbon กับเบราว์เซอร์ทำเองได้

thaidate ทำเฉพาะส่วนที่ Carbon กับเบราว์เซอร์ยังไม่มีให้ คือปี พ.ศ. ใน format ของ PHP ปีงบประมาณ และอ่าน พ.ศ. กลับเป็น Carbon งานวันที่ส่วนอื่นเราใช้ของที่มีอยู่แล้วได้เลย

แสดงเวลาแบบ "3 วันที่แล้ว"

อันนี้ Carbon มีภาษาไทยให้ในตัวแล้ว

now()->subDays(3)->locale('th')->diffForHumans();
// 3 วันที่แล้ว

ส่วน ->locale('th')->translatedFormat('j F Y') ของ Carbon ได้ชื่อเดือนภาษาไทยก็จริง แต่ปียังเป็น ค.ศ. (10 ตุลาคม 2026) ถ้าอยากได้ พ.ศ. ก็ใช้ ->thaidate('j F Y') แทน

จัดรูปแบบวันที่ในเบราว์เซอร์

ถ้าหน้าจอทำด้วย Inertia, React, Vue หรือ Alpine เราจัดรูปแบบวันที่ในเบราว์เซอร์ด้วย Intl.DateTimeFormat ได้เลย ใส่ locale th-TH แล้วได้ปี พ.ศ. เอง ไม่ต้องลงแพ็กเกจเพิ่ม แต่ Laravel ส่งวันที่ใน JSON เป็น ISO 8601 แบบ UTC เราเลยต้องบอก timeZone ที่จะแสดงด้วย

const date = new Date(order.created_at) // "2026-10-10T16:30:00.000000Z"

new Intl.DateTimeFormat('th-TH', { dateStyle: 'long', timeZone: 'Asia/Bangkok' }).format(date)
// 10 ตุลาคม 2569

new Intl.DateTimeFormat('th-TH', { dateStyle: 'full', timeStyle: 'short', timeZone: 'Asia/Bangkok' }).format(date)
// วันเสาร์ที่ 10 ตุลาคม พ.ศ. 2569 เวลา 23:30

new Intl.DateTimeFormat('th-TH-u-nu-thai', { dateStyle: 'long', timeZone: 'Asia/Bangkok' }).format(date)
// ๑๐ ตุลาคม ๒๕๖๙

new Intl.RelativeTimeFormat('th', { numeric: 'auto' }).format(-1, 'day')
// เมื่อวาน

เลือกฝั่งที่จัดรูปแบบตามที่ที่ข้อความจะไปแสดง

  • ฝั่ง PHP ด้วย thaidate() เมื่อข้อความต้องออกไปนอกหน้าจอด้วย เช่น อีเมล PDF ไฟล์ Excel หรือแจ้งเตือน หรือเมื่อเราอยากได้ format ที่ Intl ทำไม่ได้ อย่าง D j M y หรือมีข้อความไทยแทรกกลางวันที่
  • ฝั่งเบราว์เซอร์ด้วย Intl เมื่อข้อความอยู่บนหน้าจออย่างเดียว

ถ้าหน้าไหนต้องการข้อความตรงกันเป๊ะทั้งสองฝั่ง ก็จัดรูปแบบจาก PHP แล้วส่งไปเป็น prop

return Inertia::render('Orders/Show', [
    'order' => $order,
    'createdAtThai' => $order->created_at->setTimezone('Asia/Bangkok')->thaidate('j M Y H:i'),
]);

PHPStan บอกว่าหาเมธอด thaidate() ไม่เจอ

แพ็กเกจเพิ่มเมธอดทั้งหมดให้ Carbon ตอนแอปรัน แต่ PHPStan อ่านแค่โค้ด เลยมองไม่เห็นเมธอดพวกนี้ ผมจึงแนบ extension ของ PHPStan มาในแพ็กเกจด้วย ถ้าโปรเจกต์ลง phpstan/extension-installer ไว้ extension นี้จะเปิดใช้เอง ->thaidate() เมธอดปีงบประมาณ และ ::createFromThaiFormat() จะได้ type ที่ถูกต้อง (เรียกจาก CarbonImmutable ก็ได้ CarbonImmutable กลับมา) ไม่มี error ว่าหาเมธอดไม่เจอ และไม่ต้องเพิ่ม baseline

composer require --dev phpstan/extension-installer

GitHub: phattarachai/thaidate

อ่านต่อ

ล่าสุด

ดูทั้งหมด →