diff --git a/app/Http/Controllers/Api/AttendanceSyncController.php b/app/Http/Controllers/Api/AttendanceSyncController.php index 975897f38..3448bfee9 100644 --- a/app/Http/Controllers/Api/AttendanceSyncController.php +++ b/app/Http/Controllers/Api/AttendanceSyncController.php @@ -81,9 +81,6 @@ class AttendanceSyncController extends Controller ]); } - /** - * Safely parse various date formats coming from ZKTeco devices or JS agents. - */ private function parseDateSafe($dateStr) { if (!$dateStr) { @@ -94,16 +91,16 @@ class AttendanceSyncController extends Controller if (is_numeric($dateStr)) { if (strlen((string)$dateStr) > 10) { // It's likely in milliseconds - return \Carbon\Carbon::createFromTimestampMs($dateStr); + return \Carbon\Carbon::createFromTimestampMs($dateStr)->setTimezone('Asia/Manila'); } - return \Carbon\Carbon::createFromTimestamp($dateStr); + return \Carbon\Carbon::createFromTimestamp($dateStr)->setTimezone('Asia/Manila'); } // Remove JS-specific timezone names like "(China Standard Time)" to prevent Carbon parse errors $cleanDateStr = trim(explode('(', $dateStr)[0]); try { - return \Carbon\Carbon::parse($cleanDateStr); + return \Carbon\Carbon::parse($cleanDateStr, 'Asia/Manila')->setTimezone('Asia/Manila'); } catch (\Exception $e) { return null; } diff --git a/database/migrations/2026_05_30_030000_fix_biometric_timezone_offset.php b/database/migrations/2026_05_30_030000_fix_biometric_timezone_offset.php new file mode 100644 index 000000000..fc2a40fab --- /dev/null +++ b/database/migrations/2026_05_30_030000_fix_biometric_timezone_offset.php @@ -0,0 +1,33 @@ + Asia/Manila) + // Only shift records that have already been imported before this fix. + // We can do this efficiently via raw SQL to avoid loading large datasets in PHP. + DB::statement("UPDATE biometric_attendances SET punch_time = DATE_ADD(punch_time, INTERVAL 8 HOUR)"); + + // 2. Note: For any attendance records that were already synchronized and generated + // with the shifted time, the user can easily select the "Overwrite" option in the + // Biometric Attendance Bulk Sync UI. This will recalculate the correct work dates, + // pair the updated punches, and overwrite the attendance_records. + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + // Subtract 8 hours to revert + DB::statement("UPDATE biometric_attendances SET punch_time = DATE_SUB(punch_time, INTERVAL 8 HOUR)"); + } +};