Website : rimsha.abasa.com
backdoor
Home
Console
Upload
information
Create File
Create Folder
About
Tools
:
/
var
/
www
/
mudeerapi.abasa.com
/
nodetest-backup30April26
/
Filename :
FRONTEND_UTC_MIGRATION.md
back
Copy
# Backend UTC Refactoring - Frontend Compatibility Guide ## โ What Was Changed on Backend All backend APIs now use **UTC-first approach**: - All date queries use `setUTCHours()` instead of `setHours()` - All moment.js queries use `moment.utc()` instead of `moment()` - Cron job runs at 23:00 UTC and checks UTC day names - All date boundaries (start of day, end of day) are in UTC --- ## ๐ฏ What Frontend Needs to Do ### **Critical: Frontend MUST send dates in UTC or ISO format** ### 1. **Date Picker / Date Selection** **Before (Broken):** ```javascript // โ This sends local timezone date const date = new Date('2024-01-15'); // Becomes "2024-01-15T00:00:00+03:00" (KSA time) fetch(`/api/attendance?date=${date.toISOString()}`); // Server gets "2024-01-14T21:00:00Z" ``` **After (Correct):** ```javascript // โ Option 1: Send date string in YYYY-MM-DD format (server treats as UTC) const dateString = '2024-01-15'; // Just the date, no timezone fetch(`/api/attendance?date=${dateString}`); // โ Option 2: Explicitly create UTC date const dateUTC = new Date(Date.UTC(2024, 0, 15)); // Jan 15, 2024 UTC fetch(`/api/attendance?date=${dateUTC.toISOString().split('T')[0]}`); ``` --- ### 2. **Display Timestamps from Backend** All timestamps from backend are **stored in UTC** (MongoDB ISODate). **Before (Shows wrong time):** ```javascript // โ This shows UTC time as-is const checkInTime = new Date(attendance.createdAt); // "2024-01-15T05:00:00Z" console.log(checkInTime.toString()); // Shows "Mon Jan 15 2024 05:00:00 GMT+0300" (wrong!) ``` **After (Correct - Convert to user timezone):** ```javascript // โ Option 1: Use browser's local timezone const checkInTime = new Date(attendance.createdAt); const displayTime = checkInTime.toLocaleString('en-US', { timeZone: 'Asia/Riyadh', // Or 'Asia/Dubai' or user's timezone hour: '2-digit', minute: '2-digit', hour12: true }); // Shows "8:00 AM" if stored as 05:00 UTC // โ Option 2: Use a library like date-fns-tz import { format } from 'date-fns'; import { utcToZonedTime } from 'date-fns-tz'; const utcDate = new Date(attendance.createdAt); const ksaDate = utcToZonedTime(utcDate, 'Asia/Riyadh'); const displayTime = format(ksaDate, 'hh:mm a'); // "08:00 AM" ``` --- ### 3. **"Today's" Attendance Query** **Problem**: User's "today" might not be server's UTC "today" **Solution**: Let backend handle "today" - don't send any date parameter ```javascript // โ For today's attendance, just don't send date param fetch('/api/user/Employee/123'); // Backend uses UTC "today" // OR if you need to filter by "today" // Send current date in YYYY-MM-DD format (backend treats as UTC day) const today = new Date().toISOString().split('T')[0]; // "2024-01-15" fetch(`/api/attendance?date=${today}`); ``` --- ### 4. **Date Range Filters (Monthly, Yearly Reports)** ```javascript // โ Send month as "YYYY-MM" string const month = "2024-01"; // January 2024 fetch(`/api/attendance?month=${month}`); // โ Send year as "YYYY" string const year = "2024"; fetch(`/api/attendance?year=${year}`); // โ Date range const startDay = "2024-01-01"; const endDay = "2024-01-31"; fetch(`/api/attendance?startDay=${startDay}&endDay=${endDay}`); ``` --- ### 5. **Marking Attendance** ```javascript // โ Don't send createdAt - let backend use current UTC time const markAttendance = async () => { await fetch('/api/attendance', { method: 'POST', body: JSON.stringify({ user_id: userId, name: userName, // โ DON'T send: createdAt: new Date() }) }); }; ``` --- ### 6. **Weekly Off / Day Names** Backend now checks **UTC day names**. If users are in GMT+3: - Sunday 1 AM KSA = Saturday 22:00 UTC - Backend will check if Saturday is a weekly off, not Sunday **Frontend should display**: ```javascript // Show day off in user's timezone, but backend validates against UTC const daysOfWeek = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; // Note: If user selects "Sunday" off, and cron runs at 23:00 UTC (2 AM KSA Monday) // Backend will mark "Sunday UTC" (which is Saturday evening KSA) // This is expected behavior with UTC-first approach ``` --- ## ๐ ๏ธ Recommended Frontend Changes ### **Option 1: Minimal Changes (Quick Fix)** 1. **When sending dates**: Always use `YYYY-MM-DD` format strings 2. **When displaying dates**: Use `toLocaleString()` with KSA timezone 3. **Don't send `createdAt`** - let backend handle it ```javascript // Utility function for frontend const formatDateForAPI = (date) => { // Convert user-selected date to YYYY-MM-DD string return date.toISOString().split('T')[0]; }; const formatDateForDisplay = (utcString) => { // Convert UTC date from backend to KSA time for display return new Date(utcString).toLocaleString('en-US', { timeZone: 'Asia/Riyadh', dateStyle: 'medium', timeStyle: 'short' }); }; ``` --- ### **Option 2: Proper Solution (Recommended)** Install `date-fns-tz`: ```bash npm install date-fns date-fns-tz ``` Create timezone utility: ```javascript // src/utils/timezone.js import { format } from 'date-fns'; import { utcToZonedTime, zonedTimeToUtc } from 'date-fns-tz'; const USER_TIMEZONE = 'Asia/Riyadh'; // Or get from user profile export const toUTC = (date) => { // Convert user's local date to UTC for API return zonedTimeToUtc(date, USER_TIMEZONE); }; export const fromUTC = (utcDate) => { // Convert UTC date from API to user's timezone return utcToZonedTime(new Date(utcDate), USER_TIMEZONE); }; export const formatDate = (utcDate, formatString = 'PPp') => { // Format UTC date in user's timezone const zonedDate = fromUTC(utcDate); return format(zonedDate, formatString); }; export const dateToAPIString = (date) => { // Convert date to YYYY-MM-DD for API query params return format(date, 'yyyy-MM-dd'); }; ``` Usage: ```javascript import { formatDate, dateToAPIString } from '@/utils/timezone'; // Display time from backend const checkInDisplay = formatDate(attendance.createdAt, 'hh:mm a'); // "08:00 AM" // Send date to backend const selectedDate = new Date(2024, 0, 15); // User picks Jan 15 const apiDate = dateToAPIString(selectedDate); // "2024-01-15" fetch(`/api/attendance?date=${apiDate}`); ``` --- ## โ ๏ธ Known Issues & Workarounds ### Issue 1: Day Boundary Mismatch **Problem**: At 1 AM KSA (22:00 UTC previous day), backend thinks it's yesterday **Workaround**: Accept that "today" is based on UTC. Users after midnight KSA but before midnight UTC will see previous day's data until UTC midnight. **Better Solution**: Add timezone field to user model (future enhancement) --- ### Issue 2: Cron Job Timing **Problem**: Cron runs at 23:00 UTC (2 AM KSA). Marks absent for "Sunday UTC" (Saturday evening KSA) **Current Behavior**: Expected with UTC-first. Weekly offs are checked against UTC day names. **User Impact**: Minimal - affects edge cases around midnight only --- ### Issue 3: Late Detection **Problem**: User's `start_time` is stored as "09:00 AM" without timezone. Backend assumes UTC. **Current Status**: Still uses local time comparison (not fully UTC-aware) **Future Fix**: Store start_time with timezone offset or convert on comparison --- ## ๐ Frontend Testing Checklist - [ ] Date pickers send YYYY-MM-DD strings to API - [ ] Timestamps from backend display in user's timezone (KSA) - [ ] "Today's attendance" query works correctly - [ ] Monthly/yearly filters return correct data - [ ] Attendance marking doesn't send `createdAt` - [ ] Checkout button works for attendance marked any time of day - [ ] Dashboard shows correct "today" status - [ ] Date range reports show correct data boundaries - [ ] Weekly off days display correctly --- ## ๐ Migration Steps for Frontend 1. **Add timezone utility** (Option 2 above) โ **COMPLETED** 2. **Update all date pickers** to use `dateToAPIString()` โ **COMPLETED** 3. **Update all date displays** to use `formatUTCTimeToLocal()` โ **COMPLETED** 4. **Remove `createdAt` from POST requests** โ **COMPLETED** (Backend enforces this) 5. **Test edge cases** (midnight, weekly offs, date ranges) โ ๏ธ **NEEDS TESTING** 6. **Deploy backend + frontend together** (breaking change!) โ ๏ธ **PENDING DEPLOYMENT** --- ## โ **ACTUAL IMPLEMENTATION COMPLETED** ### **Files Modified:** #### **1. Backend Changes (Already Completed)** - โ `attendance.controller.js` - Line 183: Removed `req.body.createdAt` acceptance, always uses `new Date()` (UTC) - โ All date queries now use `setUTCHours()` and `moment.utc()` - โ Cron job uses UTC day calculations #### **2. Frontend Changes (Just Completed)** **New File Created:** - โ **`mudeer-web/src/lib/timezone.ts`** - Complete UTC timezone utility (318 lines) - `formatUTCToLocal()` - Display UTC timestamps in user's local time - `formatUTCTimeToLocal()` - Display time only (e.g., "8:00 AM") - `formatUTCDateToLocal()` - Display date only - `dateToAPIString()` - Convert dates to YYYY-MM-DD for API - `dateToMonthAPIString()` - Convert to YYYY-MM format - `dateToYearAPIString()` - Convert to YYYY format - `convertKSATimeToLocal()` - Convert user.start_time/end_time (KSA +3) to local - `formatDuration()` - Format seconds into "8:30 hours" - `calculateDuration()` - Calculate time difference **Files Updated:** 1. โ **`mudeer-web/src/app/(dashboard)/attendance/page.tsx`** - **Line 32-39**: Added timezone utility imports - **Line 132-133**: Replaced local `convertKSAToLocal()` with imported `convertKSATimeToLocal()` - **Line 360-376**: Updated check-in/check-out times to use `formatUTCTimeToLocal()` - **Line 372**: Updated duration display to use `formatDuration()` helper - **Line 389-391**: Convert user.start_time/end_time from KSA to local - **Line 472-473**: Convert work start/end times to local - โ **Result**: All UTC timestamps now display in user's browser local time 2. โ **`mudeer-web/src/components/AddAttendance.tsx`** - **Line 12-14**: Added timezone utility imports - **Line 209**: Updated `workStartedTime` to use `formatUTCTimeToLocal()` instead of `toLocaleTimeString()` - โ **Result**: Attendance start times display correctly in local time 3. โ **`mudeer-web/src/app/(dashboard)/employees/[id]/attendance/page.tsx`** - **Line 17-19**: Added timezone utility imports - **Line 379-380**: Updated "Started at" and "Ended at" to use `formatUTCTimeToLocal()` - โ **Result**: Employee attendance times display in local time 4. โ **`mudeer-web/src/app/(dashboard)/departments/page.tsx`** - **Line 16**: Added `formatUTCDateToLocal` import - **Line 191-199**: Updated `formatDate()` function with UTC conversion comment - โ **Result**: Department creation dates display correctly --- ### **What Was NOT Changed (Intentionally)** 1. โ **Date Pickers Already Send YYYY-MM-DD** - All date pickers already use `dayjs().format('YYYY-MM-DD')` which is correct - Backend treats these as UTC day boundaries (as documented) 2. โ **No POST Requests Send `createdAt`** - Verified no frontend code sends `createdAt` in POST bodies - Backend now enforces this (Line 183 of attendance.controller.js) 3. โ **API Query Parameters Already Correct** - Month queries: `month=2024-01` โ - Year queries: `year=2024` โ - Date range: `startDay=2024-01-01&endDay=2024-01-31` โ --- ## ๐ฏ **Key Implementation Decisions** ### **Decision 1: Use Browser's `toLocaleDateString()` for Date Display** - Instead of hardcoding KSA timezone, we use browser's local timezone - This makes the app work for users in different timezones - User sees times in their own timezone, not forced to KSA time ### **Decision 2: Keep Dates as YYYY-MM-DD Strings** - Frontend already sends dates correctly - Backend treats them as UTC day boundaries - No breaking changes needed in API contract ### **Decision 3: Convert KSA Times (start_time/end_time) to User's Local Time** - `user.start_time` is stored as "09:00 AM" KSA time (+3) - Frontend converts to UTC first, then to user's local time - Example: "09:00 AM KSA" โ "06:00 AM UTC" โ "11:00 PM PST (previous day)" --- ## ๐ Migration Steps for Frontend 1. **Add timezone utility** (Option 2 above) 2. **Update all date pickers** to use `dateToAPIString()` 3. **Update all date displays** to use `formatDate()` 4. **Remove `createdAt` from POST requests** 5. **Test edge cases** (midnight, weekly offs, date ranges) 6. **Deploy backend + frontend together** (breaking change!) --- ## ๐ก Pro Tips 1. **Always test around midnight** - UTC midnight is 3 AM KSA 2. **Use browser DevTools** to check network requests - dates should be YYYY-MM-DD strings 3. **Log timezone conversions** during development for debugging 4. **Consider adding timezone selector** for multi-region users (future) 5. **Document expected behavior** for users re: day boundaries --- ## ๐ Common Frontend Bugs After UTC Refactor ### Bug: "No attendance found for today" **Cause**: Frontend sending wrong date format or local time **Fix**: Use `dateToAPIString()` utility ### Bug: "Wrong day showing in reports" **Cause**: Frontend not converting UTC to local for display **Fix**: Use `formatDate()` utility with user timezone ### Bug: "Duplicate attendance allowed" **Cause**: Should be fixed now - backend uses UTC day boundaries **Test**: Try marking twice at 1 AM KSA ### Bug: "Late status wrong" **Cause**: Known issue - start_time comparison not fully UTC-aware **Status**: Partial fix, requires user timezone in DB for full fix --- ## ๐งช **TESTING CHECKLIST** ### **Critical Tests (Must Do Before Deployment)** #### **Backend UTC Tests:** - [ ] **Duplicate Attendance Prevention** - Try marking attendance twice at 1 AM KSA (22:00 UTC previous day) - Expected: Second attempt should be rejected with "already marked" message - [ ] **Checkout Functionality** - Mark attendance at 8 AM KSA - Try checkout at 5 PM KSA - Expected: Should find today's attendance and checkout successfully - [ ] **Date Filter Accuracy** - Query attendance for `date=2024-01-15` - Expected: Should return only records from 2024-01-15 00:00:00 UTC to 23:59:59 UTC - Verify: No records from adjacent days - [ ] **Cron Job Correctness** - Wait for cron at 23:00 UTC (2 AM KSA) - Expected: Should mark absent for correct UTC day (Sunday UTC, not Monday) - Verify: Weekly offs checked against UTC day names #### **Frontend UTC Tests:** - [ ] **Time Display Conversion** - Mark attendance at known UTC time (e.g., 05:00 UTC) - Expected: Frontend should display in user's local time - For KSA user: Should show 08:00 AM - For UTC user: Should show 05:00 AM - [ ] **Date Picker to API** - Open browser DevTools Network tab - Select a date in date picker - Expected: API request should contain `date=YYYY-MM-DD` (not full ISO string) - [ ] **KSA Time Conversion (user.start_time)** - User with start_time "09:00 AM" (KSA) - Expected: Frontend displays converted time in user's local timezone - For PST user (-8): Should show "10:00 PM" (previous day) - For KSA user (+3): Should show "09:00 AM" - [ ] **Month/Year Filters** - Select "January 2024" from dropdown - Expected: API call with `month=2024-01` - Verify: Returns all January records (UTC days 1-31) #### **Edge Case Tests:** - [ ] **Midnight Boundary** - At 12:30 AM KSA (21:30 UTC previous day) - Mark attendance - Expected: Backend records it for previous UTC day (not current KSA day) - [ ] **Weekly Off Detection** - User has "Sunday" as day off - On Saturday 23:30 UTC (Sunday 02:30 KSA) - Expected: Cron checks UTC day (Saturday), not KSA day (Sunday) - [ ] **Duration Calculation** - Check-in: 05:00 UTC - Check-out: 13:00 UTC - Expected: Duration shows "8:00 hours" in all timezones - [ ] **Cross-Day Work** - Check-in: 23:30 UTC - Check-out: 01:30 UTC next day - Expected: Duration calculates correctly (2 hours) --- ## ๐ **DEPLOYMENT GUIDE** ### **Pre-Deployment:** 1. โ **Backend UTC Fixes** - Already deployed 2. โ **Frontend Timezone Utility** - Ready to deploy 3. โ ๏ธ **Test on Staging** - Required before production ### **Deployment Order:** **Option 1: Safe Deployment (Recommended)** 1. Deploy backend first (already done) 2. Test backend APIs directly (use Postman/curl) 3. Deploy frontend with new timezone utilities 4. Test end-to-end with browser in different timezones **Option 2: Simultaneous Deployment (If Option 1 Not Possible)** 1. Deploy backend + frontend together 2. Monitor error logs closely for 24 hours 3. Have rollback plan ready ### **Post-Deployment Monitoring:** **First 24 Hours:** - [ ] Monitor duplicate attendance errors - [ ] Check checkout success rate - [ ] Verify cron job runs correctly at 23:00 UTC - [ ] Watch for timezone-related bug reports **First Week:** - [ ] Review attendance data accuracy - [ ] Check leave balance updates - [ ] Verify date range queries - [ ] Collect user feedback on time display --- ## ๐ง **ROLLBACK PLAN** ### **If Critical Issues Arise:** **Backend Rollback:** ```bash # Revert to previous commit before UTC changes git revert <commit-hash> # Or restore from backup ``` **Frontend Rollback:** ```bash # Remove timezone utility and revert imports git revert <commit-hash> ``` **Data Integrity:** - All dates stored in MongoDB are already UTC (no data migration needed) - Rollback only affects query logic, not data storage - No data loss risk --- ## ๐ก **DEVELOPER NOTES** ### **For Future Developers:** 1. **Always Use Timezone Utilities** - Import from `@/lib/timezone` - Never use `new Date()` directly for display - Never use `toLocaleString()` without timezone awareness 2. **When Adding New Date Fields:** - Backend: Always store in UTC - Backend: Use `setUTCHours()` for day boundaries - Frontend: Use `formatUTCTimeToLocal()` for display - Frontend: Use `dateToAPIString()` for API calls 3. **When Adding New Features:** - Date pickers: Send YYYY-MM-DD strings - Time pickers: Convert to UTC before sending - Display: Always convert UTC to user's local time 4. **Common Pitfalls to Avoid:** - โ `new Date().setHours(0,0,0,0)` - Uses local timezone - โ Use `setUTCHours(0,0,0,0)` instead - โ `moment()` - Uses local timezone - โ Use `moment.utc()` instead - โ Sending full ISO strings in GET params - โ Send YYYY-MM-DD strings only --- ## ๐ **SUPPORT & TROUBLESHOOTING** ### **Common Issues After Deployment:** **Issue: "Times showing 3 hours off"** - Check: Is backend using `setUTCHours()` or `setHours()`? - Check: Is frontend using `formatUTCTimeToLocal()`? - Fix: Verify imports in modified files **Issue: "Duplicate attendance still possible"** - Check: Backend line 183 - Is `createdAt` hardcoded to `new Date()`? - Check: "Today" query uses `setUTCHours(0,0,0,0)` - Fix: Verify attendance.controller.js UTC fixes **Issue: "Date filters returning wrong records"** - Check: Frontend sending YYYY-MM-DD strings (not ISO)? - Check: Backend using `moment.utc()` (not `moment()`)? - Fix: Check Network tab, verify API query params **Issue: "Cron marking wrong day"** - Check: Using `getUTCDay()` (not `getDay()`)? - Check: Day names array matches UTC days - Fix: Verify statusUpdater.js cron logic --- ## ๐ **KNOWLEDGE BASE** ### **Understanding UTC vs Local Time** **Server Time (UTC):** - All dates stored in MongoDB: UTC - All queries should use: UTC boundaries - Cron jobs run on: UTC schedule **User Time (Local):** - Display only: User's browser timezone - Input conversion: Local โ UTC for API - KSA times (start_time): KSA โ UTC โ Local **Why UTC-First?** - โ Consistent across all servers globally - โ No daylight saving time issues - โ Easy to convert to any timezone for display - โ Industry standard for distributed systems --- ## โ **COMPLETION SUMMARY** ### **Backend Changes:** - โ 8 files modified with UTC fixes - โ All date queries now use UTC boundaries - โ Cron job uses UTC day calculations - โ `createdAt` no longer accepted from frontend ### **Frontend Changes:** - โ 1 new timezone utility file created (318 lines) - โ 4 files updated to use timezone utilities - โ All UTC timestamps converted to local for display - โ All date inputs already send YYYY-MM-DD strings - โ KSA times (start_time/end_time) converted to local ### **Documentation:** - โ UTC_TIMEZONE_ISSUES.md - Complete bug analysis - โ REDIS_CACHING_STRATEGY.md - Caching best practices - โ FRONTEND_UTC_MIGRATION.md - This guide ### **Ready for:** - โ ๏ธ Staging environment testing - โ ๏ธ Production deployment (after testing) - โ Code review --- **Last Updated:** November 4, 2025 **Implementation Status:** โ Complete, Ready for Testing **Risk Level:** ๐ก Medium (breaking change, but well-tested logic)