N

Nokfa Docs

ไม่มีชื่อบทความ

การจัดการ Timezone และการ Validate ข้อมูลลงเวลา

1. Timezone ที่ใช้ในระบบ

ระบบกำหนดให้ทุกการลงเวลาใช้ Timezone แบบตายตัวคือ:

  • Asia/Bangkok (UTC+7)

เหตุผล: เพื่อให้เวลาที่ลงทะเบียนตรงกับเวลาจริงในประเทศไทย โดยไม่ขึ้นกับว่า user อยู่โซนเวลาไหน

2. ตรวจสอบ Timezone ฝั่ง Client

สามารถตรวจ timezone ของเครื่องผู้ใช้ได้ผ่าน JavaScript API:

const userTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
console.log('Your timezone:', userTimezone);
  • ถ้า userTimezone !== 'Asia/Bangkok' สามารถแสดงคำเตือนเบา ๆ ว่า "เวลาที่แสดงอาจคลาดเคลื่อน"
  • แต่สุดท้ายการบันทึกจริงยึดเวลาฝั่ง Server (Date.now()) เสมอ

3. Validate เงื่อนไขพิเศษก่อนส่งข้อมูล

3.1 ไม่อนุญาตให้ส่งเวลาที่อยู่ในอนาคต

Server จะสร้าง timestamp ตอนรับ request อยู่แล้ว ดังนั้น Client ไม่ต้องส่งเวลา แต่ถ้าเพิ่มฟีเจอร์เลือกเวลาในอนาคต จะต้อง block ไม่ให้เลือกเวลาอนาคต

3.2 จำกัดการลงเวลาให้ลงได้ทุก 5 นาที

ตัวอย่างเช่น:

  • ลงเวลา 08:00, 08:05, 08:10, ... ได้
  • แต่ไม่สามารถลง 08:02, 08:07 ได้

หลักการคือให้ timestamp ต้องหาร 300000 (5 นาที) ลงตัว

4. โค้ด Helper: validateClockPayload()

สร้างไฟล์:

src/utils/validateClockPayload.js

เนื้อหา:

// src/utils/validateClockPayload.js

/**
 * Validate payload ก่อนส่ง API ลงเวลา
 * @param {string} employeeCode
 * @param {string} action (Check-in | Check-out)
 * @param {Date} dateObject (เวลาปัจจุบันฝั่ง client)
 */
export function validateClockPayload(employeeCode, action, dateObject) {
  if (!employeeCode || typeof employeeCode !== 'string') return false;
  if (!['Check-in', 'Check-out'].includes(action)) return false;

  const timestamp = dateObject.getTime();
  const now = Date.now();

  // ไม่ให้อนาคตเกิน 1 นาที
  if (timestamp - now > 60000) return false;

  // ให้ลงเวลาได้ทุก 5 นาที (300,000 ms)
  if (timestamp % 300000 !== 0) return false;

  return true;
}

5. ตัวอย่าง Unit Test ด้วย Jest

สร้างไฟล์:

src/utils/__tests__/validateClockPayload.test.js

เนื้อหา:

import { validateClockPayload } from '../validateClockPayload';

test('valid input should pass', () => {
  const date = new Date(Math.floor(Date.now() / 300000) * 300000);
  expect(validateClockPayload('E123', 'Check-in', date)).toBe(true);
});

test('invalid action should fail', () => {
  const date = new Date();
  expect(validateClockPayload('E123', 'INVALID', date)).toBe(false);
});

test('future time should fail', () => {
  const date = new Date(Date.now() + 120000); // +2 นาที
  expect(validateClockPayload('E123', 'Check-in', date)).toBe(false);
});

test('not aligned 5 min should fail', () => {
  const date = new Date(Date.now());
  expect(validateClockPayload('E123', 'Check-in', date)).toBe(false);
});

6. แนวทาง UI แสดงเวลาปัจจุบัน

ในหน้า TimeClockForm สามารถแสดงเวลาปัจจุบันให้ผู้ใช้เห็นด้วย เช่น:

const [currentTime, setCurrentTime] = useState(new Date());

useEffect(() => {
  const timer = setInterval(() => setCurrentTime(new Date()), 60000);
  return () => clearInterval(timer);
}, []);

return (
  <p className="text-center text-gray-600 text-sm">
    เวลาปัจจุบัน: {currentTime.toLocaleTimeString('th-TH', { timeZone: 'Asia/Bangkok' })}
  </p>
);

หมายเหตุ:

  • อัปเดตเวลาใหม่ทุก ๆ 1 นาที (ไม่ต้อง real-time วินาที)
  • ใช้ timeZone: 'Asia/Bangkok' ใน toLocaleTimeString เพื่อให้แม่นยำ

หมายเหตุ: หลังขั้นตอนนี้ ระบบจะเริ่มรองรับการตรวจสอบเวลาลงงานได้ละเอียดขึ้น และรองรับการแสดงเวลาให้ตรงกับประเทศไทยเสมอ