В Excel функции, встроенные (например, SUM, COUNT) или определяемые пользователем, например, PeriodNum, обычно возвращают значение в ячейку, в которую они были введены. Они не изменяют объекты рабочей книги, такие как диапазоны, которые Selection
является членом.Процедуры Sub
s изменяют объекты, но не имеют возвращаемых значений.
Как @VitezslavSimon упомянул в своем ответе, вам необходимо присвоить значение имени функции где-то в вашем коде - PeriodNum = 1
или PeriodNum ="error"
.
Также неплохо бы явно добавить возвращаемый тип в ваши функции.В вашем случае вы можете использовать String
, если вы не выполняете никаких арифметических вычислений для возвращаемого значения, или Variant
, если вы хотите использовать возвращаемое значение в дальнейших вычислениях.Тогда определение вашей функции будет следующим:
Function PeriodNum(serial_num As Date, number_periods As Integer, start_date As Date) As Variant
Итеративный подход к ошибкам функций
Я скопировал ваш код в стандартный модуль VBA и поставил точку останова на линииfirst_week = 1
чтобы я мог пройти через функцию после ввода ее в ячейку.Чтобы проверить это, я поместил ваш start_date
в ячейку C2 и serial_num
в C3.Я вызвал функцию, введя =PeriodNum(C3,13,$C$2)
в D3.
При переходе по коду, строка day = serial_num
вызвала завершение функции и вернула ошибку #VALUE!
в D3.
#VALUE!
- единственная ошибка, возвращаемая UDF с ошибкой.Вы не можете позволить себе роскошь ошибок во время выполнения в UDF, поэтому единственный способ исследовать это пройти по коду и посмотреть, где он падает.
Дата, когда я тестировал, была 12/30/18который имеет серийный номер 43464. Вы определили day As Integer
, который должен быть от -32,768 до 32,767.Таким образом, основной ошибкой является Overflow
, потому что 43464 выходит за допустимые значения для Integer
.
2nd.Итерация
Изменение Integer
на Long
преодолевает эту ошибку, но затем
If day >= start_date Or day <= start_date + 7 Then
PeriodNum = first_week
всегда будет возвращать 1 для serial_num
> = start_date
или "error"
для serial_num
<<code>start_date.Вторая часть вашего теста Or day <= start_date + 7
на самом деле не имеет значения, потому что если это FALSE
, другой тест - TRUE
.Я проверил это, расширив даты до C381, чтобы перенести их на 2020 год. Итак, вернемся к чертежной доске!
3-я итерация
Изменение всех Or
на And
гарантирует, что вторая часть будет оценена.
If day >= start_date And day <= start_date + 7 Then
PeriodNum = first_week
ElseIf day > start_date + 7 And day <= start_date + 14 Then
PeriodNum = second_week
ElseIf day > start_date + 14 And day <= start_date + 21 Then
PeriodNum = third_week
ElseIf day > start_date + 21 And day <= start_date + 28 Then
PeriodNum = fourth_week
Else
PeriodNum = "error"
End If
Вернувшись в Excel, я замечаю, что 1/6/19, который должен быть первым днем периода 2, отображается как 1. Так что-то не так стестовое задание.Изменение day <= start_date + 7
на day < start_date + 7
должно исправить это, но возвращает "error"
.Это потому, что последующие тесты не допускают day = start_date + 7
.
4th.итерация
If day >= start_date And day < start_date + 7 Then
PeriodNum = first_week
ElseIf day >= start_date + 7 And day < start_date + 14 Then
PeriodNum = second_week
ElseIf day >= start_date + 14 And day < start_date + 21 Then
PeriodNum = third_week
ElseIf day >= start_date + 21 And day < start_date + 28 Then
PeriodNum = fourth_week
Else
PeriodNum = "error"
End If
Назад в Excel, и она выглядит хорошо до прокрутки вниз.27.01.19 и все ниже возвращается "error"
.day
, очевидно, не может быть больше, чем start_date + 28
.
5th.итерация Измените day = serial_num
на day = (serial_num - start_date) Mod 28
и тесты, чтобы посмотреть на это число по отношению к 0, 7, 14, 21 и 28.
day = (serial_num - start_date) Mod 28
If day >= 0 And day < 7 Then
PeriodNum = first_week
ElseIf day >= 7 And day < 14 Then
PeriodNum = second_week
ElseIf day >= 14 And day < 21 Then
PeriodNum = third_week
ElseIf day >= 21 And day < 28 Then
PeriodNum = fourth_week
Else
PeriodNum = "error"
End If
Назад в Excel, и все это выглядит хорошо.
Последняя итерация - отполируйте ее
Есть еще некоторые улучшения:
number_periods As Integer
не используется, поэтому удалите его - функция
PeriodNum name is deceptive. It isn't returning a period but a week in a period so
WeekInPeriod` - Не рекомендуется использовать зарезервированные слова или функции VBA в качестве имен переменных - -
day
является функцией VBA (возвращает день в месяце).Я изменил его на DayInPeriod28
.Описательные имена переменных легче анализировать. - Я также изменил
serial_num
на MyDate
. - Вместо вложенных Ifs,
Select Case
более компактен. Не объявляйте и не определяйте переменную, которую вы собираетесь использовать только один раз.Это пустая трата времени и пространства.
Function WeekInPeriod(MyDate As Date, start_date As Date) As Variant
Dim DayInPeriod28 As Long
DayInPeriod28 = (MyDate - start_date) Mod 28
Select Case DayInPeriod28
Case Is < 7
WeekInPeriod = 1
Case Is < 14
WeekInPeriod = 2
Case Is < 21
WeekInPeriod = 3
Case Is < 28
WeekInPeriod = 4
Case Else
WeekInPeriod = "error"
End Select
End Function