diff --git a/api/src/main/java/org/openmrs/module/appointments/conflicts/impl/AppointmentServiceUnavailabilityConflict.java b/api/src/main/java/org/openmrs/module/appointments/conflicts/impl/AppointmentServiceUnavailabilityConflict.java index 24cfafd44..ea8493d86 100644 --- a/api/src/main/java/org/openmrs/module/appointments/conflicts/impl/AppointmentServiceUnavailabilityConflict.java +++ b/api/src/main/java/org/openmrs/module/appointments/conflicts/impl/AppointmentServiceUnavailabilityConflict.java @@ -9,21 +9,16 @@ import java.sql.Time; import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; import static org.openmrs.module.appointments.model.AppointmentConflictType.SERVICE_UNAVAILABLE; -import static org.openmrs.module.appointments.util.DateUtil.getEpochTime; +import static org.openmrs.module.appointments.util.DateUtil.getEpochTimeUTC; public class AppointmentServiceUnavailabilityConflict implements AppointmentConflict { private final static String DAY_OF_WEEK_PATTERN = "EEEE"; - private final SimpleDateFormat DayFormat = new SimpleDateFormat(DAY_OF_WEEK_PATTERN); + private final SimpleDateFormat DayFormat = new SimpleDateFormat(DAY_OF_WEEK_PATTERN, Locale.ENGLISH); @Override public AppointmentConflictType getType() { @@ -55,8 +50,8 @@ private boolean checkConflicts(Appointment appointment, AppointmentServiceDefini } Time serviceStartTime = appointmentServiceDefinition.getStartTime(); Time serviceEndTime = appointmentServiceDefinition.getEndTime(); - long serviceStartMillis = serviceStartTime != null ? serviceStartTime.getTime() : DateUtil.getStartOfDay().getTime(); - long serviceEndMillis = serviceEndTime != null ? serviceEndTime.getTime() : DateUtil.getEndOfDay().getTime(); + long serviceStartMillis = serviceStartTime != null ? serviceStartTime.getTime() : DateUtil.getStartOfDayUTC().getTime(); + long serviceEndMillis = serviceEndTime != null ? serviceEndTime.getTime() : DateUtil.getEndOfDayUTC().getTime(); return checkTimeAvailability(appointment, serviceStartMillis, serviceEndMillis); } @@ -65,10 +60,10 @@ private boolean isObjectPresent(Collection object) { } private boolean checkTimeAvailability(Appointment appointment, long serviceStartTime, long serviceEndTime) { - long appointmentStartTimeMilliSeconds = getEpochTime(appointment.getStartDateTime().getTime()); - long appointmentEndTimeMilliSeconds = getEpochTime(appointment.getEndDateTime().getTime()); - long serviceStartTimeMilliSeconds = getEpochTime(serviceStartTime); - long serviceEndTimeMilliSeconds = getEpochTime(serviceEndTime); + long appointmentStartTimeMilliSeconds = getEpochTimeUTC(appointment.getStartDateTime().getTime()); + long appointmentEndTimeMilliSeconds = getEpochTimeUTC(appointment.getEndDateTime().getTime()); + long serviceStartTimeMilliSeconds = getEpochTimeUTC(serviceStartTime); + long serviceEndTimeMilliSeconds = getEpochTimeUTC(serviceEndTime); boolean isConflict = (appointmentStartTimeMilliSeconds >= appointmentEndTimeMilliSeconds) || ((appointmentStartTimeMilliSeconds < serviceStartTimeMilliSeconds) || (appointmentEndTimeMilliSeconds > serviceEndTimeMilliSeconds)); diff --git a/api/src/main/java/org/openmrs/module/appointments/util/DateUtil.java b/api/src/main/java/org/openmrs/module/appointments/util/DateUtil.java index 63f9ce220..d78cd4835 100644 --- a/api/src/main/java/org/openmrs/module/appointments/util/DateUtil.java +++ b/api/src/main/java/org/openmrs/module/appointments/util/DateUtil.java @@ -56,6 +56,16 @@ public static Date getStartOfDay() { return calendar.getTime(); } + public static Date getStartOfDayUTC() { + Calendar calendar = Calendar.getInstance(); + calendar.setTimeZone(TimeZone.getTimeZone("UTC")); + calendar.set(Calendar.HOUR_OF_DAY, calendar.getMinimum(Calendar.HOUR_OF_DAY)); + calendar.set(Calendar.MINUTE, calendar.getMinimum(Calendar.MINUTE)); + calendar.set(Calendar.SECOND, calendar.getMinimum(Calendar.SECOND)); + calendar.set(Calendar.MILLISECOND, calendar.getMinimum(Calendar.MILLISECOND)); + return calendar.getTime(); + } + public static long getEpochTime(long date) { Calendar calendar = getCalendar(new Date(date)); int hours = calendar.get(Calendar.HOUR_OF_DAY); @@ -65,6 +75,16 @@ public static long getEpochTime(long date) { return milliSeconds; } + public static long getEpochTimeUTC(long date) { + Calendar calendar = getCalendar(new Date(date)); + calendar.setTimeZone(TimeZone.getTimeZone("UTC")); + int hours = calendar.get(Calendar.HOUR_OF_DAY); + int minutes = calendar.get(Calendar.MINUTE); + int seconds = calendar.get(Calendar.SECOND); + long milliSeconds = ((hours * 3600 + minutes * 60 + seconds) * 1000); + return milliSeconds; + } + public static Date getEndOfDay() { Calendar calendar = Calendar.getInstance(); calendar.set(Calendar.HOUR_OF_DAY, calendar.getMaximum(Calendar.HOUR_OF_DAY)); @@ -73,6 +93,16 @@ public static Date getEndOfDay() { calendar.set(Calendar.MILLISECOND, calendar.getMaximum(Calendar.MILLISECOND)); return calendar.getTime(); } + + public static Date getEndOfDayUTC() { + Calendar calendar = Calendar.getInstance(); + calendar.setTimeZone(TimeZone.getTimeZone("UTC")); + calendar.set(Calendar.HOUR_OF_DAY, calendar.getMaximum(Calendar.HOUR_OF_DAY)); + calendar.set(Calendar.MINUTE, calendar.getMaximum(Calendar.MINUTE)); + calendar.set(Calendar.SECOND, calendar.getMaximum(Calendar.SECOND)); + calendar.set(Calendar.MILLISECOND, calendar.getMaximum(Calendar.MILLISECOND)); + return calendar.getTime(); + } } diff --git a/api/src/test/java/org/openmrs/module/appointments/conflicts/impl/AppointmentServiceUnavailabilityConflictTest.java b/api/src/test/java/org/openmrs/module/appointments/conflicts/impl/AppointmentServiceUnavailabilityConflictTest.java index 5b9af307b..b704e729c 100644 --- a/api/src/test/java/org/openmrs/module/appointments/conflicts/impl/AppointmentServiceUnavailabilityConflictTest.java +++ b/api/src/test/java/org/openmrs/module/appointments/conflicts/impl/AppointmentServiceUnavailabilityConflictTest.java @@ -9,13 +9,9 @@ import org.openmrs.module.appointments.model.ServiceWeeklyAvailability; import java.sql.Time; +import java.text.SimpleDateFormat; import java.time.DayOfWeek; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -52,17 +48,24 @@ public void shouldNotHaveAnyServiceUnavailableConflicts() { Appointment appointment = new Appointment(); appointment.setService(appointmentServiceDefinition); //Tuesday Appointment - appointment.setStartDateTime(getDate(2019, 8, 24, 11, 30, 0)); - appointment.setEndDateTime(getDate(2019, 8, 24, 12, 0, 0)); + appointment.setStartDateTime(getDate(2019, Calendar.JANUARY, 24, 11, 30, 0)); + appointment.setEndDateTime(getDate(2019, Calendar.JANUARY, 24, 12, 0, 0)); appointment.setAppointmentId(2); + + SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); + //sdf.setTimeZone(TimeZone.getTimeZone("UTC")); + Date startTimeUTC = new Date(sdf.format(new Time(8, 30, 0))); + Date endTimeUTC = new Date(sdf.format(new Time(17, 30, 0))); + Date startTimeUTC2 = new Date(sdf.format(new Time(8, 30, 0))); + Date endTimeUTC2 = new Date(sdf.format(new Time(17, 30, 0))); ServiceWeeklyAvailability day1 = new ServiceWeeklyAvailability(); - day1.setStartTime(new Time(8, 30, 0)); - day1.setEndTime(new Time(17, 30, 0)); - day1.setDayOfWeek(DayOfWeek.MONDAY); + day1.setStartTime(new Time(startTimeUTC.getHours(), startTimeUTC.getMinutes(), startTimeUTC.getSeconds())); + day1.setEndTime(new Time(endTimeUTC.getHours(), endTimeUTC.getMinutes(), endTimeUTC.getSeconds())); + day1.setDayOfWeek(DayOfWeek.THURSDAY); ServiceWeeklyAvailability day2 = new ServiceWeeklyAvailability(); - day2.setStartTime(new Time(8, 30, 0)); - day2.setEndTime(new Time(17, 30, 0)); - day2.setDayOfWeek(DayOfWeek.TUESDAY); + day2.setStartTime(new Time(startTimeUTC2.getHours(), startTimeUTC2.getMinutes(), startTimeUTC2.getSeconds())); + day2.setEndTime(new Time(endTimeUTC2.getHours(), endTimeUTC2.getMinutes(), endTimeUTC2.getSeconds())); + day2.setDayOfWeek(DayOfWeek.FRIDAY); Set availabilities = new HashSet<>(Arrays.asList(day1, day2)); appointmentServiceDefinition.setWeeklyAvailability(availabilities); @@ -91,13 +94,20 @@ public void shouldReturnServiceUnavailableTimeSlotConflict() { appointmentThree.setStartDateTime(getDate(2019, 8, 23, 16, 30, 0)); appointmentThree.setEndDateTime(getDate(2019, 8, 23, 17, 1, 0)); appointmentThree.setAppointmentId(4); + + SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); + sdf.setTimeZone(TimeZone.getTimeZone("UTC")); + Date startTimeUTC = new Date(sdf.format(new Time(8, 30, 0))); + Date endTimeUTC = new Date(sdf.format(new Time(17, 0, 0))); + Date startTimeUTC2 = new Date(sdf.format(new Time(8, 30, 0))); + Date endTimeUTC2 = new Date(sdf.format(new Time(17, 0, 0))); ServiceWeeklyAvailability day1 = new ServiceWeeklyAvailability(); - day1.setStartTime(new Time(8, 30, 0)); - day1.setEndTime(new Time(17, 0, 0)); + day1.setStartTime(new Time(startTimeUTC.getHours(), startTimeUTC.getMinutes(), startTimeUTC.getSeconds())); + day1.setEndTime(new Time(endTimeUTC.getHours(), endTimeUTC.getMinutes(), endTimeUTC.getSeconds())); day1.setDayOfWeek(DayOfWeek.MONDAY); ServiceWeeklyAvailability day2 = new ServiceWeeklyAvailability(); - day2.setStartTime(new Time(8, 30, 0)); - day2.setEndTime(new Time(17, 0, 0)); + day2.setStartTime(new Time(startTimeUTC2.getHours(), startTimeUTC2.getMinutes(), startTimeUTC2.getSeconds())); + day2.setEndTime(new Time(endTimeUTC2.getHours(), endTimeUTC2.getMinutes(), endTimeUTC2.getSeconds())); day2.setDayOfWeek(DayOfWeek.TUESDAY); Set availabilities = new HashSet<>(Arrays.asList(day1, day2)); appointmentServiceDefinition.setWeeklyAvailability(availabilities); @@ -118,27 +128,27 @@ public void shouldNotReturnServiceUnavailableConflictsForMoreSlotsInSingleDay() // All Appointments are on Monday Appointment appointmentOne = new Appointment(); appointmentOne.setService(appointmentServiceDefinition); - appointmentOne.setStartDateTime(getDate(2019, 8, 23, 6, 30, 0)); - appointmentOne.setEndDateTime(getDate(2019, 8, 23, 7, 0, 0)); + appointmentOne.setStartDateTime(getDate(2019, Calendar.JANUARY, 1, 6, 30, 0)); + appointmentOne.setEndDateTime(getDate(2019, Calendar.JANUARY, 1, 7, 0, 0)); appointmentOne.setAppointmentId(2); Appointment appointmentTwo = new Appointment(); appointmentTwo.setService(appointmentServiceDefinition); - appointmentTwo.setStartDateTime(getDate(2019, 8, 23, 16, 30, 0)); - appointmentTwo.setEndDateTime(getDate(2019, 8, 23, 17, 30, 0)); + appointmentTwo.setStartDateTime(getDate(2019, Calendar.JANUARY, 1, 16, 30, 0)); + appointmentTwo.setEndDateTime(getDate(2019, Calendar.JANUARY, 1, 17, 30, 0)); appointmentTwo.setAppointmentId(3); Appointment appointmentThree = new Appointment(); appointmentThree.setService(appointmentServiceDefinition); - appointmentThree.setStartDateTime(getDate(2019, 8, 23, 16, 30, 0)); - appointmentThree.setEndDateTime(getDate(2019, 8, 23, 17, 0, 0)); + appointmentThree.setStartDateTime(getDate(2019, Calendar.JANUARY, 1, 16, 30, 0)); + appointmentThree.setEndDateTime(getDate(2019, Calendar.JANUARY, 1, 17, 0, 0)); appointmentThree.setAppointmentId(4); ServiceWeeklyAvailability day1 = new ServiceWeeklyAvailability(); day1.setStartTime(new Time(6, 30, 0)); day1.setEndTime(new Time(14, 0, 0)); - day1.setDayOfWeek(DayOfWeek.MONDAY); + day1.setDayOfWeek(DayOfWeek.TUESDAY); ServiceWeeklyAvailability day2 = new ServiceWeeklyAvailability(); day2.setStartTime(new Time(16, 30, 0)); day2.setEndTime(new Time(19, 0, 0)); - day2.setDayOfWeek(DayOfWeek.MONDAY); + day2.setDayOfWeek(DayOfWeek.TUESDAY); Set availabilities = new HashSet<>(Arrays.asList(day1, day2)); appointmentServiceDefinition.setWeeklyAvailability(availabilities); diff --git a/omod/src/main/java/org/openmrs/module/appointments/web/mapper/AppointmentServiceMapper.java b/omod/src/main/java/org/openmrs/module/appointments/web/mapper/AppointmentServiceMapper.java index 07353ff48..0718ca4c0 100644 --- a/omod/src/main/java/org/openmrs/module/appointments/web/mapper/AppointmentServiceMapper.java +++ b/omod/src/main/java/org/openmrs/module/appointments/web/mapper/AppointmentServiceMapper.java @@ -40,8 +40,8 @@ public AppointmentServiceDefinition fromDescription(AppointmentServiceDescriptio appointmentServiceDefinition.setName(appointmentServiceDescription.getName()); appointmentServiceDefinition.setDescription(appointmentServiceDescription.getDescription()); appointmentServiceDefinition.setDurationMins(appointmentServiceDescription.getDurationMins()); - appointmentServiceDefinition.setStartTime(appointmentServiceDescription.getStartTime()); - appointmentServiceDefinition.setEndTime(appointmentServiceDescription.getEndTime()); + appointmentServiceDefinition.setStartTime(utcTimeToServerTime(appointmentServiceDescription.getStartTime())); + appointmentServiceDefinition.setEndTime(utcTimeToServerTime(appointmentServiceDescription.getEndTime())); appointmentServiceDefinition.setMaxAppointmentsLimit(appointmentServiceDescription.getMaxAppointmentsLimit()); appointmentServiceDefinition.setColor(appointmentServiceDescription.getColor()); @@ -105,8 +105,8 @@ private ServiceWeeklyAvailability constructServiceWeeklyAvailability(ServiceWeek else availability = new ServiceWeeklyAvailability(); availability.setDayOfWeek(avb.getDayOfWeek()); - availability.setStartTime(avb.getStartTime()); - availability.setEndTime(avb.getEndTime()); + availability.setStartTime(utcTimeToServerTime(avb.getStartTime())); + availability.setEndTime(utcTimeToServerTime(avb.getEndTime())); availability.setMaxAppointmentsLimit(avb.getMaxAppointmentsLimit()); availability.setService(appointmentServiceDefinition); availability.setVoided(avb.isVoided()); @@ -204,6 +204,29 @@ private Map constructAvailabilityResponse(ServiceWeeklyAvailability availability } private String convertTimeToString(Time time) { - return time != null ? time.toString() : new String(); + if (time == null) { + return new String(); + } + + // Use today's date for the returned time so that hour is adjusted considering daylight saving time + Calendar calendar = Calendar.getInstance(); + calendar.set(Calendar.HOUR_OF_DAY, time.getHours()); + calendar.set(Calendar.MINUTE, time.getMinutes()); + calendar.set(Calendar.SECOND, time.getSeconds()); + calendar.set(Calendar.MILLISECOND, 0); + + return calendar.toInstant().toString(); + } + + private Time utcTimeToServerTime(Time time) { + if (time == null) { + return null; + } + Calendar serviceEndTimeUtc = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + serviceEndTimeUtc.set(Calendar.HOUR_OF_DAY, time.getHours()); + serviceEndTimeUtc.set(Calendar.MINUTE, time.getMinutes()); + serviceEndTimeUtc.set(Calendar.SECOND, time.getSeconds()); + serviceEndTimeUtc.set(Calendar.MILLISECOND, 0); + return new Time(serviceEndTimeUtc.getTime().getHours(), serviceEndTimeUtc.getTime().getMinutes(), serviceEndTimeUtc.getTime().getSeconds()); } } diff --git a/omod/src/test/java/org/openmrs/module/appointments/web/mapper/AppointmentServiceDefinitionMapperTest.java b/omod/src/test/java/org/openmrs/module/appointments/web/mapper/AppointmentServiceDefinitionMapperTest.java index fac3a81f6..eacd6675e 100644 --- a/omod/src/test/java/org/openmrs/module/appointments/web/mapper/AppointmentServiceDefinitionMapperTest.java +++ b/omod/src/test/java/org/openmrs/module/appointments/web/mapper/AppointmentServiceDefinitionMapperTest.java @@ -206,7 +206,7 @@ public void shouldCreateFullResponseFromAnAppointmentService() throws Exception appointmentServiceFullResponse = appointmentServiceMapper.constructResponse(appointmentServiceDefinition); assertEquals(appointmentServiceDefinition.getName(), appointmentServiceFullResponse.getName()); assertEquals(appointmentServiceDefinition.getDurationMins(), appointmentServiceFullResponse.getDurationMins()); - assertEquals(appointmentServiceDefinition.getStartTime().toString(), appointmentServiceFullResponse.getStartTime()); + assertEquals(utcTimeToString(appointmentServiceDefinition.getStartTime()), appointmentServiceFullResponse.getStartTime()); assertEquals(new String(), appointmentServiceFullResponse.getEndTime()); assertEquals(appointmentServiceDefinition.getMaxAppointmentsLimit(), appointmentServiceFullResponse.getMaxAppointmentsLimit()); assertEquals(location.getName(), appointmentServiceFullResponse.getLocation().get("name")); @@ -240,7 +240,7 @@ public void shouldCreateDefaultResponseFromAppointmentServicesList() throws Exce List appointmentServicesResponse = appointmentServiceMapper.constructDefaultResponseForServiceList(appointmentServiceDefinitions); assertEquals(cardiologyService.getName(), appointmentServicesResponse.get(0).getName()); assertEquals(cardiologyService.getDurationMins(), appointmentServicesResponse.get(0).getDurationMins()); - assertEquals(cardiologyService.getStartTime().toString(), appointmentServicesResponse.get(0).getStartTime()); + assertEquals(utcTimeToString(cardiologyService.getStartTime()), appointmentServicesResponse.get(0).getStartTime()); assertEquals(new String(), appointmentServicesResponse.get(0).getEndTime()); assertEquals(cardiologyService.getMaxAppointmentsLimit(), appointmentServicesResponse.get(0).getMaxAppointmentsLimit()); @@ -248,8 +248,8 @@ public void shouldCreateDefaultResponseFromAppointmentServicesList() throws Exce assertEquals(speciality.getName(), appointmentServicesResponse.get(0).getSpeciality().get("name")); assertEquals(chemoTherapyService.getName(), appointmentServicesResponse.get(1).getName()); assertEquals(chemoTherapyService.getDurationMins(), appointmentServicesResponse.get(1).getDurationMins()); - assertEquals(chemoTherapyService.getStartTime().toString(), appointmentServicesResponse.get(1).getStartTime()); - assertEquals(chemoTherapyService.getEndTime().toString(), appointmentServicesResponse.get(1).getEndTime()); + assertEquals(utcTimeToString(chemoTherapyService.getStartTime()), appointmentServicesResponse.get(1).getStartTime()); + assertEquals(utcTimeToString(chemoTherapyService.getEndTime()), appointmentServicesResponse.get(1).getEndTime()); assertEquals(chemoTherapyService.getMaxAppointmentsLimit(), appointmentServicesResponse.get(1).getMaxAppointmentsLimit()); assertEquals(location.getName(), appointmentServicesResponse.get(1).getLocation().get("name")); @@ -436,7 +436,7 @@ public void shouldMapAppointmentServiceToDefaultResponse() { AppointmentServiceDefaultResponse appointmentServiceDefaultResponse = appointmentServiceMapper.constructDefaultResponse(appointmentServiceDefinition); assertEquals(appointmentServiceDefinition.getName(), appointmentServiceDefaultResponse.getName()); assertEquals(appointmentServiceDefinition.getDurationMins(), appointmentServiceDefaultResponse.getDurationMins()); - assertEquals(appointmentServiceDefinition.getStartTime().toString(), appointmentServiceDefaultResponse.getStartTime()); + assertEquals(utcTimeToString(appointmentServiceDefinition.getStartTime()), appointmentServiceDefaultResponse.getStartTime()); assertEquals(appointmentServiceDefinition.getUuid(), appointmentServiceDefaultResponse.getUuid()); } @@ -517,4 +517,16 @@ private ServiceWeeklyAvailabilityDescription createServiceWeeklyAvailabilityDesc return availabilityPayload; } + private String utcTimeToString(Time time) { + Calendar timeCalendar = Calendar.getInstance(); + timeCalendar.setTime(time); + + Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + calendar.set(Calendar.HOUR_OF_DAY, timeCalendar.get(Calendar.HOUR_OF_DAY)); + calendar.set(Calendar.MINUTE, timeCalendar.get(Calendar.MINUTE)); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); + + return calendar.toInstant().toString(); + } }