openhour business logic
현재 식당 주문관리앱을 하나만들고있는데 openhour관련 내용을 만들어야해서 정리를 한번 해봣다.
문제
- openhour를 1주일 단위로 보여줘라.
- opehhour는 입력시 am pm 과 시간을 이용해라.
- timezone을 고려해라.
- day light saving을 고려해라.
- 현재 시간을 기준으로 오픈인지 아닌지를 보여줘라.
위 문제를 모두 고려해서 처리를 해야했다.
해결 방법
문제는 3가지로 압축될수 있었다.
- 오픈시간 입력
- 식당 오픈 시간을 보여주기
- 현재 시간을 기준으로 오픈인지 확인하기
식당 오픈 입력
입력할때 am/pm을 사용하여 입력하는 조건이여서 현지 시간을 기준으로 오픈시간을 입력한다. 그리고 dayofweek를 이용하여 0 - 6 까지 일,월…토 이렇게 정리한다. 그리고 타임존이 필요하다.
{
"WorkStart": "01:00 PM",
"WorkEnd": "04:00 PM",
"isWorking": true,
"restaurantId": "08d897fd-02e3-4386-8a1f-33ec3a9bb61e",
"isAvailableMon": true,
"isAvailableTue": false,
"isAvailableWed": false,
"isAvailableThu": false,
"isAvailableFri": false,
"isAvailableSat": false,
"isAvailableSun": false,
"timezone": "America/Los_Angeles"
}
이와 같이 서버로 보내면 서버에서는 요일별로 workstart와 workend timezone을 보내서 서버에 저장한다.
백앤드에서는 2020-01-01 이후에 이 시간을 붙여서 저장해준다.
if (request.IsAvailableMon)
{
var openHourEntity = _mapper.Map<OpenHour>(request);
openHourEntity.DayOfWeek = DayOfWeek.Monday;
openHourEntity.TimeOfStartWork = getOpenHourStartDateTime(request.WorkStart);
openHourEntity.TimeOfEndWork = getOpenHourEndDateTime(request.WorkStart,request.WorkEnd);
_context.OpenHours.Add(openHourEntity);
}
...
private DateTime getOpenHourStartDateTime(string time)
{
var result = DateTime.ParseExact(
"01/01/2020 " + time,
"MM/dd/yyyy h:mm tt",
CultureInfo.InvariantCulture
);
// Console.WriteLine(result);
return result;
}
private DateTime getOpenHourEndDateTime(string stime,string etime)
{
var sDateTime = DateTime.ParseExact(
"01/01/2020 " + stime,
"MM/dd/yyyy h:mm tt",
CultureInfo.InvariantCulture
);
var eDateTime = DateTime.ParseExact(
"01/01/2020 " + etime,
"MM/dd/yyyy h:mm tt",
CultureInfo.InvariantCulture
);
if(sDateTime > eDateTime)
return eDateTime.AddDays(1);
else
return eDateTime;
}
혹시 시작이 11pm이고 끝이 다음날 2am이면 1/2일로 넣어준다. 여기서 중요하게 생각한것은 스트링을 특정일에 datetime으로 변경을 해서 디비에 입력을 해준것이다. 그리고 이건 식당들에 로컬타임이다.
오픈시간 보여주기
오픈시간을 프론트로 가서 보여줄때 전체 날짜가 아닌 hh:mm만 보여주면 된다.
현재 시간을 기준으로 검색하기
서버 시간은 gmt 기준으로 되있다. 그리고 식당에 위치는 LA / NY 이 될수도 있다. 이들사이에는 시차가 3시간이 난다. day light saving을 적용해야한다. 이걸 처리하기 위해 다음처럼 로직을 만들어 봤다.
TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById(openHour.Timezone); //저장된 타임존을 가져온다. America/Los_Angeles
var currentLocalTime = TimeZoneInfo.ConvertTimeFromUtc(current, tz); //식당의 위치에 localtime으로 바꾼다.
if(openHour.DayOfWeek == currentLocalTime.DayOfWeek) // 요일이 같은날이면
{
var differntDay = (int)(currentLocalTime - openHour.TimeOfStartWork).TotalDays; //현재 로컬날짜와 디비에 잇는 workstart와 날자 차이를 계산한다.
// 차이나는 날짜를 start에 더해준다. summertime인지 아닌지 알기 위해 그리고 현재 로컬 날짜가 이 더한 날짜 사이에잇는지 확인한다.
var isCurrentlyOpen = Between(currentLocalTime, openHour.TimeOfStartWork.AddDays(differntDay), openHour.TimeOfEndWork.AddDays(differntDay));
if(isCurrentlyOpen){
Console.WriteLine("find openhour currently time:");
item.IsOpen = true;
break;
}
}
private bool Between(DateTime input, DateTime sdate, DateTime edate)
{
System.Console.WriteLine("input "+input);
System.Console.WriteLine("sdate "+sdate);
System.Console.WriteLine("edate "+edate);
if(input >= sdate && input <= edate){
return true;
}
return false;
}
gmt인 서버시간을 고객의 로컬시간으로 바꾸고 그 로컬시간이 기준시간(2020-01-01)로부터 몇일이 차이나는지 확인후 그 날짜만큼 식당 open시간에 더해준다.
그리고 현재 시간이 그 두 시간사이에 잇는지 확인하면 된다.
이렇게 하면 고객의 타임존과 상관없이 매번 고객의 로컬 시간으로 처리가 되서 쉽게 된다.
결론
프론트로 am/pm으로 입력받은걸 서버에서는 특정일(2020-01-01) 이후에 붙여서 datetime을 만든후 디비에 저장.
보여줄때는 hh:mm tt 를 이용하여 시간만 보여주면 되고
현재 시간 기준으로 open인지를 확인하려면 현재 gmt시간을 고객 시간대에 현지 시간(A)으로 바꾸고 . 이 현지 시간(A)이 기준일 부터 몇일이 지난건지 확인해서 workstart/workend에 추가해주면 식당의 오늘 오픈시간(daylight saving을 포함)을 알수있다.
이 오픈시간에 현지 시간(A)이 오픈된건지만 확인하면 된다.
모든걸 date로 처리하므로 스트링 파싱등이 필요가 없어지게 되어서 편해졋다.