Напишите функцию VBA, чтобы найти период №.и неделя нет.на 13 периодов - PullRequest
0 голосов
/ 08 декабря 2018

Обзор: я в основном хочу написать версию функции MONTH для работы в течение 13-ти периодного рабочего года ...

(Отказ от ответственности: я новичок в VBA) Каков наилучший способ переформатированиядата дд / мм / гггг в терминах номера периода (1-13) и номера недели (1-4)?Я использовал функцию WEEKNUM, чтобы понять это, но я не могу понять, как это сделать.Я думаю, что проблема в том, что начало нового года (Период 1, Неделя 1) наступает 30/30/18, и в Excel эта дата считается 5-й или даже 6-й неделей в зависимости от ее измерения.

Недели начинаются по воскресеньям, и каждый период года (13) длится 4 недели.До сих пор я пытался:

  • эта базовая часть самописной функции, чтобы заставить работать только часть номера недели, и она просто дает мне #value, но без ошибок в msgbox (если это сработало, я мог бы выяснить все остальное):

     Function PeriodNum(serial_num As Date, number_periods As Integer, start_date As Date)
    
    Dim first_week As Integer
    Dim second_week As Integer
    Dim third_week As Integer
    Dim fourth_week As Integer
    Dim day As Integer
    
    first_week = 1
    second_week = 2
    third_week = 3
    fourth_week = 4
    day = serial_num
    
    If day >= start_date Or day <= start_date + 7 Then
    Selection.Value = first_week
        ElseIf day > start_date + 7 Or day <= start_date + 14 Then
            Selection.Value = second_week
        ElseIf day > start_date + 14 Or day <= start_date + 21 Then
            Selection.Value = third_week
        ElseIf day > start_date + 21 Or day <= start_date + 28 Then
            Selection.Value = fourth_week
        Else
        Selection.Value = "error"
    End If
    
    End Function
    

-Я также пытался использовать функцию WEEKNUM, но я не знаю, как ее получитьдайте мне цифры 1-4 для каждого перспективного периода

Большое спасибо за вашу помощь!Высоко ценится

Ответы [ 3 ]

0 голосов
/ 08 декабря 2018

Вы можете просто рассчитать неделю:

LngPeriod = int((day - start_date +1)/7)
If lngPeriod >4 then
     PeriodNum = “error”
Else
    PeriodNum = lngPeriod
End if

Где функция PeriodNum возвращает предложенный вариант.

0 голосов
/ 08 декабря 2018

В 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
    
0 голосов
/ 08 декабря 2018

1) В соответствии с правилами кода VBA существует одна проблема:

для каждого определения функции PeriodNum (serial_num As Date, number_periods As Integer, start_date As Date), также следует использовать строку:

PeriodNum = ...

тем самым вы возвращаете значение из функции (аналогично return x; в других языках)

2) Использование объекта Selection непредсказуемо и можетизмените любую ячейку, активную в Excel, во время вызова метода.Если вы используете метод для вычисления, вы должны использовать аналогичный способ:

Function weekCompute(date1)
    '...
    weekCompute = "val1"
End Function

С этой функцией вы можете использовать эту формулу ячейки: = weekCompute (A1)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...