Sunday, August 14, 2005

ข้อควรระวังในการ set Gregorian Calendar

ใช้มาตั้งนานพึ่งรู้ว่ามีโอกาศ set ผิดได้ ลองดูตัวอย่างนี้
เราสามารถ set GregorianCalendar ได้ 2 แบบ คือ
ผ่านทาง Constructor หรือ ผ่านทาง method set
ของมัน
GregorianCalendar cal1 = new GregorianCalendar(2005, Calendar.AUGUST, 12);
GregorianCalendar cal2 = new GregorianCalendar(2005, Calendar.AUGUST, 12, 0, 0,
0);
GregorianCalendar cal3 = new GregorianCalendar();
cal3.set(Calendar.MILLISECOND, 0);
cal3.set(Calendar.SECOND, 0);
cal3.set(Calendar.MINUTE, 0);
cal3.set(Calendar.HOUR, 0);
cal3.set(Calendar.YEAR, 2005);
cal3.set(Calendar.MONTH, Calendar.AUGUST);
cal3.set(Calendar.DATE, 12);

ค่า TimeInMillis ของ cal1 กับ cal2 จะมีค่าตรงกัน
ส่วนค่าของ cal3 มีโอกาศผิดได้ ขึ้นอยู่กับว่าเรา
run program นี้ตอนกี่โมง ถ้าเรา run ตอนเข้า ค่า cal3
ก็จะถูกต้อง แต่ถ้าเรา run ช่วงบ่ายค่า cal3 ก็จะไม่ถูกต้อง
(คำว่าถูกต้องหมายถึง ค่าที่ได้จาก method getTimeInMillis
ของ cal3 มีค่าตรงกับ cal2.getTimeInMillis())

ที่เป็นเช่นนี้เพราะว่า algorithm ในการคำนวณ timeInMillis
ของ GregorianCalendar มันจะคำนวณก่อนว่าเรามีการ
set ค่า HOUR_OF_DAY หรือ HOUR
ถ้า set ค่า HOUR มันจะนำ AM_PM value มาคำนวณด้วย
ดังนั้นในกรณีเรา run ช่วงบ่าย ตอนที่เรา new GregorianCalendar()
เราจะได้ AM_PM = Calendar.PM ทำให้ค่า HOUR ที่เรา set
เป็น 0 นั้นหมายถึงตอน 12.00 นาฬิกา
// from GregorianCalendar.java
int hourOfDayStamp = stamp[HOUR_OF_DAY];
int hourStamp = stamp[HOUR];
int bestStamp = (hourStamp > hourOfDayStamp) ? hourStamp : hourOfDayStamp;
// Hours
if (bestStamp != UNSET) {
if (bestStamp == hourOfDayStamp) {
// Don't normalize here; let overflow bump into the next period.
// This is consistent with how we handle other fields.
millisInDay += internalGet(HOUR_OF_DAY);
fieldMask |= 1 << HOUR_OF_DAY;
} else {
// Don't normalize here; let overflow bump into the next period.
// This is consistent with how we handle other fields.
millisInDay += internalGet(HOUR);
fieldMask |= 1 << HOUR;

// The default value of AM_PM is 0 which designates AM.
if (stamp[AM_PM] != UNSET) {
millisInDay += 12 * internalGet(AM_PM);
fieldMask |= 1 << AM_PM;
}
}
}

ดังนั้นในกรณีที่ต้องการ set ให้ถูกต้อง ก็ต้องเลือก set
โดยใช้ HOUR_OF_DAY หรือ HOUR
ในกรณีใช้ HOUR ก็ต้องทำการ set AM_PM ด้วย
// use HOUR_OF_DAY
cal3.set(Calendar.HOUR_OF_DAY, 0);

// use HOUR & AM_PM
cal3.set(Calendar.HOUR, 0);
cal3.set(Calendar.AM_PM, Calendar.AM);

Related link from Roti

No comments: