×

คำเตือน

JFolder::create: Path not in open_basedir paths.

สำหรับการเขียนโค๊ดที่เป็น Open Source ที่มีการใช้งานในหลายๆ Time Zone นั้น ปัญหาหนึ่งที่ผมเจอก็คือเรื่องการเก็บข้อมูลและการแสดงผลวันที่และเวลา ซึ่งจากประสบการณ์การพัฒนา JONGman สำหรับใช้เป็นระบบจองทรัพยากรต่างๆ มีการใช้งาน Date Time เป็นองค์ประกอบหลัก หลังจากผ่านการทดลองและการได้รับการแบ่งปันจากผู้รู้หลายๆ ท่าน โดยเฉพาะจากบทความของ Andrew Eddie ตามลิงค์นี้นะครับ ผมก็พอที่จะได้หลักในการทำงานกับวันที่ ซึ่งจริงๆก็ไม่จำเป็นว่าจะต้องเป็นเฉพาะจูมล่าเท่านั้น สามารถใช้ไ้ด้ทั่ไป ดังนี้นเพื่อให้หลักการเหล่านี้ได้มีการนำไปใช้งานอย่างถูกต้องและช่วยประหยัดเวลาของเราในการทำงาน และการใช้งานจูมล่าผมจึงขอนำเสนอโดยอิงหลักของ Andrew Eddie เป็นหลัก

การจัดเก็บวันที่และเวลาในไทม์โซน GMT

มีข้อดีที่มองเห็นได้ชัดในการเก็บเวลาในไทม์โซน (time zone) GMT คือเรื่องของการย้ายเซอร์ฟเวอร์ไงครับ ลองสมมุติว่าถ้าเราเก็บข้อมูลของเป็นเวลาประเทศไทย คือ GMT+7 แล้วเรามีการย้ายเซอร์ฟเวอร์ เราก็ต้องเปลี่ยนเป็นไทม์โซนตัวใหม่ แล้วเท่าที่ทราบในข้อมูลของวันที่และเวลาอย่างใน MySQL ก็ไม่ได้มีไทม์โซนแปะไว้นะครับ ดังนั้นเราก็อาจจะสับสนได้เวลาที่ต้องนำมาใช้งาน ดังนั้นในการเก็บข้อมูลวันที่และเวลาก็ต้องทำการแปลงไไทม์โซนเป็น GMT ก่อนเก็บลงฐานข้อมูลซึ่งจูมล่ามีคลาสที่ให้เราใช้งานได้อย่างสะดวกสบายครับ

การแสดงผลวันที่และเวลาในไทม์โซน LOCAL

สำหรับข้อนี้ชัดเจนในตัวเองครับ ใครๆ ก็ขคุ้นเคยกับเวลาของตัวเอง คงไม่มีใครโดยเฉพาะชาวไทยอย่างเราๆ อยางจะเห็นเวลาในไทม์โซนของอังกฤษ สำหรับไทม์โซนจะสามารถกำหนดค่าแบบทั้งไซต์ได้ใน Global Configuration เป็นลักษณะ หรือสามารถกำหนดไทม์โซนของผู้ใช้แต่ละคนได้ในข้อมูล configuration ของแต่ละคน ซึ่งเราสามารถหาค่าเหล่านี้ได้จากตัวแปร Object ของจูมล่า ดดังนี้ครีับ

$config = JFactory::getConfig();
$tz = $user->getParam('timezone', $config->get('offset'));

ครับในส่วนของบรรทัดแรกก็คือการอ่านค่าตัวแปร object ที่เก็บค่า Global configuration ส่วนบรรทัดที่สองก็คือการอ่านค่าที่กำหนดไว้ของผู้ใช้สำหรับไทม์โซนแต่หากไม่กำหนดก็คือใช้ค่าจาก Global configuration แทนครับ

การใช้งานคลาส JDate

จูมล่ามีคลาสสำหรับการทำงานกับวันที่มาให้เราแล้วซึ่งมีประโยชน์มากพอควรครับ แต่น่าจะมีส่วนของการเปรียบเทียบเวลาหรือการหาความแตกต่างของเวลา และอะไรที่มากกว่านี้ แต่แค่นี้ก็เหมาะและเพียงพอกับการใช้งานทั่วๆไปแล้วครับ

การใช้งาน JDate โดยตรง

ก่อนการใช้งานคลาสเราต้องทำการ import ก่อนนะครับเพื่อมั่นใจว่า JDate ถูกโหลดแล้วดังโค้ดด้านล่างครับ

jimport('joomla.utilities.date');
$date = new JDate();
echo $date->toSql();

ในบรรทัดที่สองเราทำการสร้างตัวแปร object ของ JDate โดยมันรับค่าพารามีเตอร์สองตัวคือ วันที่และไทม์โซนโดยค่าเราไม่ส่งค่าใดๆ ให้มันก็ึคือวันที่และเวลาปัจจุบันและไทม์โซนก็คือ GMT ครับ ส่วนบรรทัดที่สามเป็นการแปลงเป็นรูปแบบสำหรับบันทึกลงฐานข้อมูลครับ ตัวนี้มีความพิเศษในตัวคือจูมล่าจะตรวจสอบให้เองว่า driver ของฐานข้อมูลคืออะไรเช่น MySQL หรือ MSSQL มันก็จะปรับให้เอง การใช้งานแบบนี้ก็เหมาะดีครับไม่ต้องสนใจว่าฐานข้อมูลเป็นอะไร และมันจะมีค่าพารามีเตอร์สองตัวเช่นกันครับคือ $local ซึ่งค่าโดยปริยายคือ FALSE นั่นคือไม่แปลงเป็นไทม์โซน LOCAL นั่นเองครับ มันก็จะเป็น GMT สำหรับการบันทึกลงฐานข้อมูลแบบที่เราต้องการพอดีครับ ส่วนตัวที่สองคือ $dbo หรือ object ของฐานข้อมูลซึ่งถ้าเราไม่กำหนดก็ใช้ค่าของจูมล่าครับ เรามาดูตัวอย่างอีกรูปแบบครับ

$date1 = new JDate('2009-01-01 01:00:00');
echo $date1->toSql();

$date2 = new JDate('2009-01-01 01:00:00', $tz);
echo $date2->toSql();

ในส่วนนี้เราผ่านค่า $date เป็นสตริงของวันที่ซึ่งเป็นรูปแบบที่ PHP เข้าใจครับ มีสองตัวอย่างให้ดูก็คือการผ่านค่าไทม์โซนด้วย ซึ่งสามารถหาได้จากตัวอย่างด้านบนนะครับ นอกจากนี้แล้ว JDate ยังมีฟังก์ชันในการแปลงรูปแบบคือ format() ที่เราสามารถใช้ในการแสดงผลได้ ซึ่งจะกล่าวถึงภายหลังครับ

การใช้งานผ่าน JFactory

เราสามารถเรียกใช้งาน JDate ผ่าน JFactory ได้ครับ สำหรับตัวพารามีเตอร์ก็แบบเดียวกันคือ $date และ ไทม์โซนแต่มีความแตกต่างกันเล็กน้อยถ้าใช้งานผ่าน JFactory คือมันทำงานแบบ Single Design Pattern ครับคือค่าพารามีเตอร์ที่ถูกส่งไปหลังจากใช้สร้าง object ของวันทีั่และเวลาแล้วมันจะถูกเก็บค่าไว้ (cached) ทุกครั้งที่เราเรีัยกใช้ตัว object เดิมจะถูกส่งออกมา เช่นกรณีเราไม่ส่งเวลาให้มันก็คือ 'now' หรือเวลาปัจจุบัน ณ ตอนเรียกครั้งแรก ถ้าเราเรียกใช้ครั้งที่สองโดยพารามีเตอ์รเหมือนเดิมก็ยังคงได้เวลาเดิม การ Cached ในที่นี้ไม่ใช้ Session นะครับเป็นการ Cached ในหน่วยความจำต่อการเรียกในแต่รอบของ PHP เช่นการเรียกที่ Controller และที่ View ก็จะได้ค่าเดียวกันครับ

$date3 = JFactory::getDate();
// เรียกใช้โค้ดอื่นๆ ทีใช้เวลามากๆ เช่นซัก 1-2 วินาที เรียก getDate() อีกทีก็ได้เวลาเดิม
$date4 = JFactory::getDate();
echo $date3->toSql().' = '.$date4->toSql();

การเก็บค่าวันที่และเวลาในฐานข้อมูล

การเก็บค่าข้อมูลก็มีสองแบบครับเท่้าที่พอสรุปได้ ก็คือเวลาของระบบที่ระบบกำหนดเอง เช่นเวลาที่ทำการสร้าง record หรือแก้ไข record อีกแบบก็คือเวลาที่ผู้ใช้กรอกให้กับเราไงครับ ในที่นี้เรามาดูกันทั้งสองแบบ ทั้งแบบที่กำหนดโดยระบบไม่ให้ผู้ใช้แก้และเวลาที่กรอกโดยผู้ใช้

การเก็บเวลาของระบบ

การเก็บข้อมูลในส่วนนี้ปกติเราก็ทำในส่วนของ Table เป็นส่วนใหญ่ ซึ่งทำได้สองที่นะครับคือใน Model ในส่วนของ method preprocessTable($table) ที่จะถูกเรียกก่อนการบันทึกข้อมูลของ Model หรือใน method store($updateNulls =false) ของคลาส Table ของเราครับ หลักการก็คือดูว่าถ้าเป็น record ใหม่ที่ยังไม่มี ID เราก็กำหนดค่าให้กับเวลาในการ create record หรือผมจะใช้ว่า created ครับ ดังตัวอย่างครับ

	public function store($updateNulls = false)
	{
		// Initialiase variables.
		$date	= JFactory::getDate()->toSql();
		$userId	= JFactory::getUser()->get('id');

		if (empty($this->id)) {
			// New record.
			$this->created		= $date;
			$this->created_by	= $userId;
		} 
		else {
			// Existing record.
			$this->modified	        = $date;
			$this->modified_by	= $userId;
		}

		// Attempt to store the data.
		return parent::store($updateNulls);
	}

 

การเก็บเวลาที่ส่งมาโดยผู้ใช้

การเก็บค่าที่ส่งมาจากผู้ใช้ก็ง่ายๆ ครับ เราก็อ่านค่าตัวแปรมาจากฟอร์มซึ่งถ้าใช้งาน JForm ก็มี JFormField ให้เราใช้งานอยู่แล้วที่จะทำการแปลงไทม์โซนให้เราด้วย หลักการก็ง่ายๆ ครับค่าที่เราอ่านมาจากผู้่ใช้ซึ่งเราแสดงในไทม์โซน LOCAL เวลาเราสร้าง object ของ JDate เราต้องส่งค่าไทม์โซนด้วย เวลาที่เราต้องการจะบันทึกลงฐานข้อมูลก็เรียกใช้ method toSql() โดยไม่ต้องส่งค่าพารามีเตอร์อะไรเพราะมันจะแปลงให้เราเองเป็น GMT ครับ

การแสดงผลเวลาในวิว

การแสดงเวลาอย่างที่กล่าวไว้แล้วครับก็มีสองรูปแบบคือแสดงเป็นข้อมูลที่ผู้ใช้ไม่สามารถแก้ไขได้ หรือแบบเป็นอินพุทรับค่าจากผู้ใช้ครับ ส่วนแรกก็ทำผ่านคลาส JHtml::date() โดยมีรูปแบบการใช้งานดังนั้ครับ

public static function date($input = 'now', $format = null, $tz = true, $gregorian = false)

ส่วนแบบที่สองก็ทำผ่าน JFormField ที่ชื่อ calendar ครับเราสามารถระบุ Filter เป็น user_utc ไ้้ด้เพื่อให้การแสดงผลเป็น LOCAL TIME สำหรับการใช้งานก็ไม่ยากนะครับ ลองดูได้ครับ หากมีเวลาผมจะมาแก้ไขบทความเพิ่มเติมให้ครับ

สำหรับบทความนี้ก็ขอบคุณ Andrew Eddie ซึ่งเรียกได้ว่าเป็นอาจารย์ของผมคนนึงเพราะนอกจากติดตาม e-mail ในกลุ่มของเขาแล้ว ผมยังลงทะเบียนเรียนหลักสูตรการพัฒนาจูมล่า extension ในตอนแรกๆ ที่จูมล่า 2.5 ยังเป็น Beta อยู่ด้วยก็ได้ประโยชน์และแนวคืดมากมายครับ

comments