diff --git a/components/availability/AvailabilityForm.js b/components/availability/AvailabilityForm.js index 76dac5c..b911438 100644 --- a/components/availability/AvailabilityForm.js +++ b/components/availability/AvailabilityForm.js @@ -10,7 +10,9 @@ import { bgBG } from '../x-date-pickers/locales/bgBG'; import { ToastContainer } from 'react-toastify'; const common = require('src/helpers/common'); //todo import Availability type from prisma schema -import { isBefore, addMinutes, isAfter, isEqual, set, getHours, getMinutes, getSeconds } from 'date-fns'; +import { isBefore, addMinutes, isAfter, isEqual, set, getHours, getMinutes, getSeconds } from 'date-fns'; //ToDo obsolete + +const { DateTime, FixedOffsetZone } = require('luxon'); @@ -187,15 +189,34 @@ export default function AvailabilityForm({ publisherId, existingItems, inline, o // Common function to set shared properties function setSharedAvailabilityProperties(availability, group, timeSlots) { - let startTime = new Date(availability.startTime || day); - startTime.setHours(group[0].startTime.getHours(), group[0].startTime.getMinutes(), group[0].startTime.getSeconds(), 0); + // Define a fixed offset for Sofia (+2 hours from UTC, ignoring DST) + const fixedZone = FixedOffsetZone.instance(120); // Offset in minutes - let endTime = new Date(availability.endTime || day); - endTime.setHours(group[group.length - 1].endTime.getHours(), group[group.length - 1].endTime.getMinutes(), group[group.length - 1].endTime.getSeconds(), 0); + // availability.startTime = group[0].startTime; + // availability.endTime = group[group.length - 1].endTime; + // Adjust start time + let startTime = DateTime.fromJSDate(group[0].startTime, { zone: 'utc' }) + .setZone(fixedZone, { keepLocalTime: true }); + startTime = startTime.set({ + hour: group[0].startTime.getUTCHours(), + minute: group[0].startTime.getUTCMinutes(), + second: group[0].startTime.getUTCSeconds() + }); - availability.startTime = startTime; - availability.endTime = endTime; - availability.name = common.getTimeFormatted(startTime) + "-" + common.getTimeFormatted(endTime); + // Adjust end time + let endTime = DateTime.fromJSDate(group[group.length - 1].endTime, { zone: 'utc' }) + .setZone(fixedZone, { keepLocalTime: true }); + endTime = endTime.set({ + hour: group[group.length - 1].endTime.getUTCHours(), + minute: group[group.length - 1].endTime.getUTCMinutes(), + second: group[group.length - 1].endTime.getUTCSeconds() + }); + + // Update the availability object with the new times + availability.startTime = startTime.toJSDate(); + availability.endTime = endTime.toJSDate(); + + availability.name = common.getTimeFomatted(group[0].startTime) + "-" + common.getTimeFomatted(group[group.length - 1].endTime); availability.isWithTransportIn = group[0].isFirst && timeSlots[0].isWithTransport; availability.isWithTransportOut = group[group.length - 1].isLast && timeSlots[timeSlots.length - 1].isWithTransport; @@ -209,7 +230,7 @@ export default function AvailabilityForm({ publisherId, existingItems, inline, o } else { availability.type = "OneTime" availability.repeatWeekly = false; - availability.dayOfMonth = startTime.getDate(); + availability.dayOfMonth = availability.startTime.getDate(); availability.endDate = null; } availability.isFromPreviousMonth = false; @@ -286,7 +307,8 @@ export default function AvailabilityForm({ publisherId, existingItems, inline, o const slots = []; let currentTime = start; - const baseDate = new Date(Date.UTC(2000, 0, 1, 0, 0, 0)); + //const baseDate = new Date(Date.UTC(2000, 0, 1, 0, 0, 0)); + const baseDate = new Date(start); while (isBefore(currentTime, end)) { let slotStart = normalizeTime(currentTime, baseDate); diff --git a/components/calendar/avcalendar.tsx b/components/calendar/avcalendar.tsx index 8aca614..f0d47e0 100644 --- a/components/calendar/avcalendar.tsx +++ b/components/calendar/avcalendar.tsx @@ -9,7 +9,7 @@ import common from '../../src/helpers/common'; import { toast } from 'react-toastify'; import { ToastContainer } from 'react-toastify'; import 'react-toastify/dist/ReactToastify.css'; -import moment from 'moment'; +import moment from 'moment'; // ToDo: obsolete, remove it import 'moment/locale/bg'; // Import Bulgarian locale import { ArrowLeftCircleIcon } from '@heroicons/react/24/outline'; diff --git a/package-lock.json b/package-lock.json index 1dbaf43..17cee5c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "pwwa", - "version": "1.2.0", + "version": "1.2.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "pwwa", - "version": "1.2.0", + "version": "1.2.2", "dependencies": { "@auth/prisma-adapter": "^1.4.0", "@emotion/react": "^11.11.3", @@ -49,6 +49,7 @@ "jsonwebtoken": "^9.0.2", "jszip": "^3.10.1", "levenshtein-edit-distance": "^3.0.1", + "luxon": "^3.4.4", "mailtrap": "^3.3.0", "module-alias": "^2.2.3", "moment": "^2.30.1", diff --git a/package.json b/package.json index 7801a61..ba51f22 100644 --- a/package.json +++ b/package.json @@ -67,6 +67,7 @@ "jsonwebtoken": "^9.0.2", "jszip": "^3.10.1", "levenshtein-edit-distance": "^3.0.1", + "luxon": "^3.4.4", "mailtrap": "^3.3.0", "module-alias": "^2.2.3", "moment": "^2.30.1", @@ -117,4 +118,4 @@ "depcheck": "^1.4.7", "prisma": "^5.13.0" } -} \ No newline at end of file +} diff --git a/pages/cart/publishers/import.tsx b/pages/cart/publishers/import.tsx index 0bf0a3c..a031fc5 100644 --- a/pages/cart/publishers/import.tsx +++ b/pages/cart/publishers/import.tsx @@ -11,7 +11,6 @@ import * as XLSX from "xlsx"; // import { Table } from "react-bootstrap"; import { staticGenerationAsyncStorage } from "next/dist/client/components/static-generation-async-storage.external"; -import moment from 'moment'; // import { DatePicker } from '@mui/x-date-pickers'; !! CAUSERS ERROR ??? // import { DatePicker } from '@mui/x-date-pickers/DatePicker'; diff --git a/src/helpers/common.js b/src/helpers/common.js index e5208a3..7d677aa 100644 --- a/src/helpers/common.js +++ b/src/helpers/common.js @@ -10,7 +10,7 @@ const { PrismaClient, UserRole } = require('@prisma/client'); const DayOfWeek = require("@prisma/client").DayOfWeek; const winston = require('winston'); const { getSession } = require("next-auth/react"); - +const { DateTime, FixedOffsetZone } = require('luxon'); const logger = winston.createLogger({ level: 'info', // Set the default log level @@ -36,6 +36,27 @@ exports.logger = logger; // dotenv.config(); // // dotenv.config({ path: ".env.local" }); +exports.adjustUtcTimeToSofia = function (time) { + // Convert the Date object to a Luxon DateTime object in UTC + let result = DateTime.fromJSDate(time, { zone: 'utc' }); + // Convert to Sofia time, retaining the local time as provided + result = result.setZone('Europe/Sofia', { keepLocalTime: true }); + // Set hours, minutes, and seconds to match the input time + result = result.set({ + hour: time.getHours(), + minute: time.getMinutes(), + second: time.getSeconds() + }); + return result.toJSDate(); +}; + +exports.adjustTimeToUTC = function (time) { + let result = DateTime.fromJSDate(time, { zone: 'Europe/Sofia' }); + result = result.setZone('utc', { keepLocalTime: true }); + return result.toJSDate(); +}; + + exports.isValidPhoneNumber = function (phone) { if (typeof phone !== 'string') { return false; // or handle as you see fit @@ -355,11 +376,6 @@ exports.getDateFormatedShort = function (date) { } -exports.getTimeFormatted = function (date) { - return date.toLocaleTimeString('en-US', { hour12: false, hour: '2-digit', minute: '2-digit', timeZone: 'Europe/Sofia' });//timeZone: 'local' - -} - /*Todo: remove: toISOString slice(0, 10) @@ -533,9 +549,11 @@ exports.getCurrentYearMonth = () => { const month = String(currentDate.getMonth() + 1).padStart(2, '0'); // Month is 0-indexed return `${year}-${month}`; } -exports.getTimeFormated = function (date) { - return this.formatTimeHHmm(date); -} +exports.getTimeFormatted = function (date) { + const dateTime = DateTime.fromJSDate(date, { zone: 'Europe/Sofia' }); + return dateTime.toFormat('HH:mm'); +}; + // format date to 'HH:mm' time string required by the time picker exports.formatTimeHHmm = function (input) { // Check if the input is a string or a Date object