22.07.2009, 14:57 | #1 |
Участник
|
Как учесть только рабочие часы?
Вот уверен, что я не один с этим сталкивался, но на форуме почему-то не нашёл. Есть у меня дата1, время1, дата2, время2. Можно ли каким-нибудь классиком определить, сколько времени прошло с дата1, время1 по дата2, время2, если необходимо учитывать только рабочие часы (например, с 8.00 по 17.00)?
__________________
I believe! |
|
22.07.2009, 15:30 | #2 |
Участник
|
Маловероятно что такой класс есть
|
|
22.07.2009, 15:36 | #3 |
Участник
|
Посмотрите вот эту тему - там много всего было, возможно и свое найдете.
по датам |
|
22.07.2009, 18:56 | #4 |
Administrator
|
Есть очень классный календарь в los-слое с зарплатой. Находится он в \Расчеты с персоналом\Учет рабочего времени\Календарь\Календари. В нем задаются календарь, шаблон (5 дней для обычной рабочей недели) и самое главное - в нем можно задавать вручную все выходные дни, не попадающие на субботу/воскресенье или рабочие дни, наоборот попадающие на субботу/воскресенье. Т.е. задать фактически только исключения из общего правила, что сб, вс выходные, а остальные дни рабочие.
Также там можно по кнопке Расписания создать времена и указать любой диапазон времени. В коде сей механизм вызывается через статические методы, расположенные на таблице RPayCalendarTable, в частности для Вас интересен метод periodWorkDays А вот такой код - Вам поможет получить дату, ближайшую в будущем после заданной Вами, которая является рабочим днем: X++: #Define.MyDate(systemdateget()) rPayCalendarId = RPayCalendarTable::findCalendarType(RpayCalendarType::FiveDay).CalendarId; select firstonly rPaycalendarDate where rPaycalendarDate.TransDate > #MyDate && rPaycalendarDate.CalendarId == rPayCalendarId && rPaycalendarDate.PayDayType == RPayDayType::WorkDay;
__________________
Возможно сделать все. Вопрос времени |
|
23.07.2009, 02:00 | #5 |
Banned
|
Образец, требует лишь минимальной доработки в плане даты:
\Data Dictionary\Tables\WorkCalendarDate\Methods\calcDefinedSeconds X++: server static Seconds calcDefinedSeconds( CalendarId _calendar, TransDate _transDate, Seconds _fromTime = maxint(), Seconds _toTime = 0, boolean _useEffectivity = true, //sp1 boolean _throwIfError = true ) { WorkCalendarDateLine workCalendarDateLine; Seconds seconds; ; while select workCalendarDateLine index hint CalendarDateFromIdx //sp1 where workCalendarDateLine.CalendarId == WorkCalendar::defined(_calendar,_transDate,_throwIfError) && workCalendarDateLine.TransDate == _transDate { workCalendarDateLine.FromTime = workCalendarDateLine.FromTime < _fromTime ? _fromTime : workCalendarDateLine.FromTime; workCalendarDateLine.ToTime = workCalendarDateLine.ToTime > _toTime ? _toTime : workCalendarDateLine.ToTime; if (workCalendarDateLine.FromTime < workCalendarDateLine.ToTime) seconds += workCalendarDateLine.capacity(100,_useEffectivity); } return seconds; } |
|
|
За это сообщение автора поблагодарили: sukhanchik (2). |
23.07.2009, 14:09 | #6 |
Moderator
|
Как обычно, обожаю подобные задачки. Вечерком прикинул алгоритм (под спилберговские "Челюсти" по НТВ), сегодня неспешно воплотил в перерывах основной работы. Видел, что в теме уже что-то появилось от sukhanchik'а и EVGL'а, но заставил себя не смотреть раньше времени, чтобы увиденное не влияло на мой плотницкий вариант.
Вначале я хотел вообще обойтись одним SELECT'ом классического SQL. Но крыша начала немного коситься (а тут еще и акулы по телеку плавали!), и я свернул на дорожку перебора дат, входящих в диапазон, с отбором диапазонов рабочего времени в map и последующим вычислением общей суммы рабочих часов. Диапазоны представлены 2-х элементными контейнерами [дата/время начала, дата/время окончания]. Дата/время - real, в котором целая часть - дни, а дробная - доля суток. Элементы рабочего календаря имитируются встроенной функцией, в которой реализован конкретный график работы. График работы взял из своего личного жЫзненного опыта - 4 дня с 8-30 до 17-30, в пятницу - до 16-30. Обед - 48 минут (а не час), и за счет ежедневных "переработок" в 12 минут к пятнице набегает час, на который и можно уйти пораньше. Наверное, многим знаком подобный прием. Обед в джобе учтён! X++: #define.secondsOf24hrs( 86400 ) static void KKu_demoCountWorkHoursForPeriod(Args _args) { // ИСХОДНЫЕ ДАННЫЕ ------------------------- // дата/время начала и дата/время окончания date dateBeg = 01\06\2009; timeOfDay timeBeg = str2time('07:00:00'); date dateEnd = 30\06\2009; timeOfDay timeEnd = str2time('24:00:00'); // ----------------------------------------- real dateTimeBeg = date2num(dateBeg) + timeBeg/#secondsOf24hrs; real dateTimeEnd = date2num(dateEnd) + timeEnd/#secondsOf24hrs; real start, finish, workHours; int i, elemCount; Map allDaysRanges = new Map(Types::Integer, Types::Container); Set oneDayRanges = new Set(Types::Container); SetEnumerator setEnumr; MapEnumerator mapEnumr; // встроенная функция имитирует рабочий календарь ------------------------------------ // для одного дня возвращает два периода, с учетом обеденного перерыва // для простоты примера не учитывает праздники и связанные с ними переносы Set workdayRanges(date _date) { Set setRanges = new Set(Types::Container); ; switch (dayOfWk(_date)) { case 1,2,3,4 : // c 8-30 до 17-30 (обед 48 минут) // до обеда setRanges.add([date2num(_date) + str2time('08:30:00')/#secondsOf24hrs, date2num(_date) + str2time('13:00:00')/#secondsOf24hrs]); // после обеда setRanges.add([date2num(_date) + str2time('13:48:00')/#secondsOf24hrs, date2num(_date) + str2time('17:30:00')/#secondsOf24hrs]); break; case 5 : // ПЯТНИЦО! - на час меньше! c 8-30 до 16-30 (обед 48 минут) // до обеда setRanges.add([date2num(_date) + str2time('08:30:00')/#secondsOf24hrs, date2num(_date) + str2time('13:00:00')/#secondsOf24hrs]); // после обеда setRanges.add([date2num(_date) + str2time('13:48:00')/#secondsOf24hrs, date2num(_date) + str2time('16:30:00')/#secondsOf24hrs]); break; case 6,7 : // ничего -> Have a nice weekend! )) break; } return setRanges; } // ----------------------------------------------------------------------------------- ; if (dateTimeEnd < dateTimeBeg) { box::stop('Дата/время окончания меньше даты/времени начала!\nВыполнение прервано.'); return; } // заполнение map циклом между крайними датами периода for (i=date2num(dateBeg); i<=date2num(dateEnd)+1; i++) { oneDayRanges = workdayRanges( num2date(i) ); if (! oneDayRanges.empty()) { setEnumr = oneDayRanges.getEnumerator(); while (setEnumr.moveNext()) { [start, finish] = setEnumr.current(); if ( ((start >= dateTimeBeg) && (start <= dateTimeEnd)) || // start between Beg and End ((finish >= dateTimeBeg) && (finish <= dateTimeEnd)) || // finish between Beg and End ((start <= dateTimeBeg) && (finish >= dateTimeEnd)) ) // на случай внутри одного диапазона { elemCount++; allDaysRanges.insert(elemCount, [start, finish]); } } } } if (! allDaysRanges.empty()) { // коррекция первого диапазона [start, finish] = allDaysRanges.lookup(1); start = max(start, dateTimeBeg); allDaysRanges.insert(1, [start, finish]); // коррекция последнего диапазона [start, finish] = allDaysRanges.lookup(elemCount); finish = min(finish, dateTimeEnd); allDaysRanges.insert(elemCount, [start, finish]); } // подсчет рабочих часов за период mapEnumr = allDaysRanges.getEnumerator(); while (mapEnumr.moveNext()) { [start, finish] = mapEnumr.currentValue(); workHours += (finish - start) * 24; } box::info( strFmt('Кол-во рабочих часов = %1', workHours)); } |
|