Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -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);
}

Expand All @@ -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));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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));
Expand All @@ -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();
}
}


Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<ServiceWeeklyAvailability> availabilities = new HashSet<>(Arrays.asList(day1, day2));
appointmentServiceDefinition.setWeeklyAvailability(availabilities);

Expand Down Expand Up @@ -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<ServiceWeeklyAvailability> availabilities = new HashSet<>(Arrays.asList(day1, day2));
appointmentServiceDefinition.setWeeklyAvailability(availabilities);
Expand All @@ -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<ServiceWeeklyAvailability> availabilities = new HashSet<>(Arrays.asList(day1, day2));
appointmentServiceDefinition.setWeeklyAvailability(availabilities);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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());

Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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"));
Expand Down Expand Up @@ -240,16 +240,16 @@ public void shouldCreateDefaultResponseFromAppointmentServicesList() throws Exce
List<AppointmentServiceDefaultResponse> 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());
assertEquals(location.getName(), appointmentServicesResponse.get(0).getLocation().get("name"));
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"));
Expand Down Expand Up @@ -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());
}

Expand Down Expand Up @@ -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();
}
}