---
title: "Intro to timeperiodsR"
author: "Alexey Seleznev"
date: "`r Sys.Date()`"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Intro to timeperiodsR}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include = FALSE}
knitr::opts_chunk$set(
  eval=TRUE,
  collapse = TRUE,
  comment = "#>"
)
```

# Цель пакета timeperiodsR
Зачастую при создании скриптов которые в последствии будут запускаться по расписанию нам необходимо определить некий отчётный период. Как правило, таким периодом может быть прошлая неделя, прошлый месяц, либо какое-то количество прошлых дней. Пакет timeperiodsR предоставляет вам набор функций которые автоматически будут вычислять такой период от заданной даты. Все функции пакета возвращают период в виде объекта класса *tpr*. В этой виньетке мы подробно разберём все функции пакета `timeperiodsR`, а также компоненты и методы класса *tpr*.

# Содержание
* Простой пример использования
* Функции пакета
* Компоненты класса tpr
* Аргументы
* Методы
* Операторы
* Конвертация других объектов в класс tpr
* Пользовательские выходные дни

Помимо данной виньетки к пакету `timeperiodsR` есть видео инструкция, ознакомится с которой можно по этой [ссылке](https://youtu.be/NgfWELbM6Fk).

# Простой пример использования
Как я уже писал выше, основная цель `timeperiodsR` упростить процесс определения относительного периода. Если у вас есть скрипт который запускается по рассписанию, и собирает данные за прошлый месяц, то вы можете получить прошлый месяц следующим образом:

```{r, echo=TRUE, eval=TRUE}
library(timeperiodsR)

# получаем прошлый месяц
period <- previous_month()

# получаем начальную и конечную дату прошлого месяца
start <- period$start # первый день прошлого месяца
end   <- period$end   # последний день прошлого месяца
```

Код определения временно периода с помощью `timeperiodsR` компактен и легко читаем, таким же бразом вы можете определять и любые другие периоды, о чём вы более подробно узнаете из данной статьи.

# Функции пакета timeperiodsR
Текущая версия пакета состоит из 24 функций, по названию каждой из функций можно определить какой временной интервал она возвращает.
Название состоит из префикса `last` / `previous` / `this` / `next` и временной единицы `day` / `week` / `month` / `quarter` / `year`. Нижнее подчёркивание `_` является разделителем слов в названиях функций. 

## Список функций
* `last_n_days()`
* `last_n_weeks()`
* `last_n_months()`
* `last_n_quarters()`
* `last_n_years()`
* `previous_week()`
* `previous_month()`
* `previous_quarter()`
* `previous_year()`
* `this_week()`
* `this_month()`
* `this_quarter()`
* `this_year()`
* `next_week()`
* `next_month()`
* `next_quarter()`
* `next_year()`
* `next_n_days()`
* `next_n_weeks()`
* `next_n_months()`
* `next_n_quarters()`
* `next_n_years()`
* `custom_period()`
* `as_timeperiod()`
* `check_dayoffs()`

## Компоненты получаемых объектов
Любая из функций пакета возвращает объект класса `tpr` состоящий из следующих компонентов:

**Обязательные компоненты**

* start - начальная дата;
* end - конечная дата;
* first_workday - первый рабочий день;
* last_workday - последний рабочий день;
* first_weekend - первый выходной день;
* last_weekend - последний выходной день;
* sequence - последовательность дат;
* workdays - последовательность будних дней;
* weekends - последовательность выходных дней;
* length - количество дней входящих в период;
* workdays_length - количество будних дней входящих в период;
* weekends_length - количество выходных дней входящих в период;

**Дополнительные компоненты**

* official_workdays - Официальные рабочие дни;
* official_first_workday - Первый официальный рабочий день;
* official_last_workday - Последний официальный рабочий день;
* official_day_offs - официальные выходные дни в выбранной стране, по умолчанию России;
* custom_day_offs - пользовательские выходные дни;
* dayoffs_marks - пометки о том, какой статус имеет день в официальном календаре страны;
* custom_day_offs - пользовательские выходные.

Начиная с версии `timeperiodsR 0.5.0` в пакет была интегрирована поддержка API производственного календаря [isDayOff](https://www.isdayoff.ru/). Этот API позволяет получить официальные праздничные дни в следующих странах: Россия, Украина, Казахстан и Белоруссия. 

Для активации дополнительных компонентов вам необходимо установить пакет `httr`.

```{r eval=FALSE}
install.packages("httr")
```

По умолчанию дополнительные компоненты приведённые выше выключены, т.к. они нужны не всем пользователям пакета, а пакет с использованием API работает значительно медленнее.

Для расширения класса `tpr` дополнительными компонентами вы можете воспользоваться опциями или переменными среды.

#### Опции для включения дополнительных компонентов

* timeperiodsR.official_day_offs - включение дополнительных компонентов, по умолчанию имеет значение `FALSE`;
* timeperiodsR.official_day_offs_country - установить страну для которой будет запрашиваться список официальных выходных дней:
    * ru - Россия, значение по умолчанию;
    * ua - Украина
    * by - Белоруссия
    * kz - Казахстан
* timeperiodsR.official_day_offs_pre - распознавать, что день предпраздничный, т.е. сокращённым:
    * 0 - без вывода сокращённых дней (по умолчанию)
    * 1 - распознавать, что день предпраздничный  (сокращённый)
    
Пример установки опций:
```{r eval=TRUE}
options("timeperiodsR.official_day_offs" = TRUE,
        "timeperiodsR.official_day_offs_country" = "ua",
        "timeperiodsR.official_day_offs_pre" = 1)
```

#### Переменные среды для включения дополнительных компонентов

Опции удобны для работы, но их необходимо задавать при каждом новом сеансе работы с R, если все указанные в опциях параметры в большинстве случаев у вас статичны, то лучше задать их на уровне операционной системы.

О том как создавать переменные среды можно узнать из этого [видео](https://www.youtube.com/watch?v=zoNvu2T6IFc&list=PLD2LDq8edf4pItOb-vZTG5AXZK2niJ8_R&index=5&t).

Задайте следующие переменные среды для того, что бы не указывать опции в каждом сеансе:

* TPR_DAY_OFFS - TRUE, для активации дополнительных компонентов;
* TPR_COUNTRY - Страна: ru, ua, by или kz;
* TPR_PRE - Включать в компонент *official_day_offs* сокращённые рабочие дни, в компоненте *dayoffs_marks* они будут помечены числом 2.

После того как вы установите значения для переменных среды пакет автоматически будет считывать их при подключении.

## Аргументы
В функциях пакета timeperiodsR присутствуют следующие аргументы:

* *x* - Объект даты, от которой будет вычисляться интервал, по умолчанию это текущий день;
* *n* - Количество временных интервалов на которое необходимо сместится от даты указанной в аргументе *x*;
* *part* - Какую часть объекта вам необходимо получить:
    * "all" - объект со всеми доступными компонентами;
    * "start" - начальную дату;
    * "end" - конечную дату;
    * "first_workday" - первый рабочий день периода;
    * "last_workday" - последний рабочий день периода;
    * "first_weekend" - первый выходной день периода;
    * "last_weekend" - последний выходной день периода;
    * "sequence" - последовательность дат;
    * "workdays" - последовательность будних дней в периоде;
    * "weekends" - последовательность выходных дней в периоде;
    * "length" - количество дней входящих в период;
    * "workdays_length" - количество будних дней входящих в период;
    * "weekends_length" - количество выходных дней входящих в период;
    * "official_workdays" - официальные рабочие дни для указанной страны;
    * "official_first_workday" - первый официальный рабочий день для указанной страны;
    * "official_last_workday" - последний официальный рабочий день для указанной страны;
    * "official_day_offs" - официальные выходные дни для указанной страны;
    * "dayoffs_marks" - пометки для каждого дня;
* *week_start* - Какой день будет являться началом недели: 1 - понедельник, 7 - воскресенье;
* *include_current* - Включать ли в период текущий временной объект, TRUE или FALSE.

## Методы
Пакет `timeperiodsR` имеет несколько методов, позволяющих вам извлекать некоторые элементы объектов класса *tpr*.

* `seq` - получить последовательность дат из объекта класса *tpr*;
* `length` - получить длительность объекта класса *tpr* в днях;
* `start` - получить первую дату из объекта класса *tpr*;
* `end` - получить последнюю дату из объекта класса *tpr*;
* `first_workday` - получить первый рабочий день из объекта класса *tpr*;
* `last_workday` - получить последний рабочий день из объекта класса *tpr*;
* `first_weekend` - получить первый выходной день из объекта класса *tpr*;
* `last_weekend` - получить последний выходной из объекта класса *tpr*;
* `workdays` - получить последовательность рабочих дней из объекта класса *tpr*;
* `weekends` - получить последовательность выходных из объекта класса *tpr*;
* `workdays_length` - получить  из объекта класса *tpr*;
* `weekends_length` - получить  из объекта класса *tpr*;
* `print` - вывести в консоль информацию о периоде содержащемся в объекте класса *tpr*.

# last\_n\_\*
Функции блока `last_n_*()` позволяют вам получить прошлый период с заданным количеством временных единиц. 

Вместо `*` подставьте один из нужных вам временных интервалов: *days, weeks, months, quarters, years*

Например, допустим, что сегодня 26 сентября 2019 года и вам необходимо получить 2 прошлые недели то используйте функцию `last_n_weeks()`:

```{r, echo=TRUE, eval=TRUE}
last2weeks <- last_n_weeks(n = 2)
```

![2 прошлые недели](http://img.netpeak.ua/alsey/156864246904_kiss_75kb.png)

Код захватит даты с 9 по 22 сентября включительно.

После чего у вас появится объект **last2weeks** класса `tpr`. Из него вы легко можете получить начальную или конечную дату периода, а так же всю последовательность дат которая вошла в этот период, или количество дней вошедших в период.

```{r, echo=TRUE, eval=TRUE}
# начальная дата
last2weeks$start
## или
start(last2weeks)

# конечная дата
last2weeks$end
## или
end(last2weeks)

# первый рабочий день
last2weeks$first_workday
## или
first_workday(last2weeks)

# последний рабочий день
last2weeks$last_workday
## или
last_workday(last2weeks)

# первый выходной день
last2weeks$first_weekend
## или
first_weekend(last2weeks)

# последний выходной день  
last2weeks$last_weekend
## или
last_weekend(last2weeks)

# последовательность дат
last2weeks$sequence
## или
seq(last2weeks)

# последовательность будних дней
last2weeks$workdays
## или
workdays(last2weeks)

# последовательность выходных дней
last2weeks$weekends
# или
weekends(last2weeks)

# количество дней вошедших в период
last2weeks$length
## или
length(last2weeks)

# количество будних дней в периоде
last2weeks$workdays_length
## или
workdays_length(last2weeks)
  
# количество выходных дней в периоде
last2weeks$weekends_length
## или
weekends_length(last2weeks)

```

Во всех функциях предназначенных для работы с неделями т.е. `last_n_weeks()`, `previous_week()`, `this_week()`, `next_week()`, `next_n_weeks()` присутствует аргумент *week_start*, с его помощью можно указать день недели который будет являться её началом. По умолчанию неделя начинается с понедельника, т.е. `week_start = 1`, но вы можете задать и любой другой день:

1. Понедельник
2. Вторник
3. Среда
4. Четверг
5. Пятница
6. Суббота
7. Воскресенье

Т.е. если вы хотите получить 2 предыдущие недели отталкиваясь от 26 сентября, и при этом необходимо считать началом недели воскресенье то используйте следующий код:

```{r, echo=TRUE}
library(timeperiodsR)

last2weeks <- last_n_weeks(x = "2019-09-26", n = 2, week_start = 7)
```
![Две предыдущие недели, начало недели воскресенье](http://img.netpeak.ua/alsey/156864661236_kiss_74kb.png)

# previous\_\*
Функции блока `previous_*()` позволяют вам получить прошлый период со смещением на заданное количеством временных единиц. Т.е. например получить позапрошлую неделю.

Вместо `*` подставьте один из нужных вам временных интервалов: *week, month, quarter, year*

Допустим нам необходимо получить позапрошлую неделю отталкиваясь от 26 сентября 2019 года, началом недели должен быть понедельник

```{r, echo=TRUE}
previous2weeks <- previous_week(x = "2019-09-26", n = 2)
```

![Позапрошлая неделя](http://img.netpeak.ua/alsey/156863814902_kiss_70kb.png)

# this\_\*
Функции блока `this_*()` позволяют вам получить текущий период.

Вместо `*` подставьте один из нужных вам временных интервалов: *week, month, quarter, year*

Если вам необходимо получить первую и последнюю дату текущего месяца, и всю последовательность дат которые в него входят, то используйте следующий код:
```{r, echo=TRUE}
this_month()
```

![Текущий месяц](http://img.netpeak.ua/alsey/156880445292_kiss_73kb.png)

Т.е. если сегодня 26 сентября 2019 года функция `this_month()` захватит весь сентябрь 2019 года.

# next\_\*
Функции блока `next_*()` противоположный функциям блока `previous_*()`. Т.е. позволяют вам получить будущий период со смещением на заданное количеством временных единиц. Т.е. например получить следующую неделю от 12 сентября.

Вместо `*` подставьте один из нужных вам временных интервалов: *week, month, quarter, year*

```{r, echo=TRUE}

nextweek_from_12sep <- next_week("2019-09-12")

nextweek_from_today <- next_week()
```

![Следующая неделя](http://img.netpeak.ua/alsey/156881039998_kiss_72kb.png)

# next\_n\_\*
Блок `next_n_*` позволяет создать период обратный от того, что создают функции блока `last_n_*`, т.е. получить будущий период с заданным количеством временных единиц.

Вместо `*` подставьте один из нужных вам временных интервалов: *days, weeks, months, quarters, years*

Например если вам необходимо получить 5 следующих дней воспользуйтесь следующим кодом.

```{r, echo=TRUE}
# получить 5 следующих дней не включая текущую дату
next5days <- next_n_days(n = 5)

# получить 5 следующих дней включая текущую дату
next5days_wt <- next_n_days(n = 5, include_current = T)
```

# custom_period
Данная функция позволяет создавать объект класса *tpr* с любым произвольным периодом.
```{r, echo=TRUE}
period1 <- custom_period("2019-09-03", "2019-09-11")
```

# Операторы
В `timeperiodsR` есть несколько операторов.

* %.in% - проверяет вхождение одного вектора дат, или объекта класса tpr в другой, и возвращает логический вектор.
* %left_out% - сравнивает два объекта класса tpr, и возвращает значение из левого, которые отсутствуют в правом.
* %left_in% - сравнивает два объекта класса tpr, и возвращает даты из левого объекта которые входят в правый
* %right_out% - сравнивает два объекта класса tpr, и возвращает значение из правого, которые отсутствуют в левом.
* %right_in% - сравнивает два объекта класса tpr, и возвращает даты из правого объекта которые присутствуют в левом.

Из представленного описания возможно сложно понять зачем эти операторы нужны, и как именно они работают. Поэтому рассмотрим несколько примеров.

Сначала создадим два объекта tpr класса, относительно 7 ноября 2019 года. Один будет соответствовать текущему месяцу, а второй предыдущей неделе.
```{r, echo=TRUE}
period1 <- this_month("2019-11-07")
period2 <- previous_week("2019-11-07")

print(period1)
print(period2)
```

В таком случае первый период содержит даты с 1 по 30 ноября, а второй с 28 октября по 3 ноября. Т.е. частично эти два периода пересекаются и мы можем фильтровать один из используя значения другого.

```{r, echo=TRUE}
period1 %left_in% period2   # получить даты из period1 которые входят в period2
period1 %left_out% period2  # получить даты из period1 которые не входят в period2
period1 %right_in% period2  # получить даты из period2 которые входят в period1
period1 %right_out% period2 # получить даты из period2 которые не входят в period2
```

Такие операторы фильтрации удобно использовать например при проверке наличия данных в базе за какой-то период, в тех случаях когда вы наполняете базу данными из внешних источников на ежедневной основе. 

К примеру вы каждый день запрашиваете данные из API Google Analytics, и записываете их в базу. При этом API периодически может давать сбой, и за какой-то день в вашей базе будут отсутствовать данные, тогда вы можете определить проверочный период, например предыдущие 30 дней, с помощью команды `last_n_days(n = 30)`. Далее запрашивать данные за этот из вашей СУБД. После, с помощью приведённых выше операторов фильтрации убрать из общего 30 дневного периода даты которые присутствуют в базе, и по оставшимся датам подгрузить данные.

# Конвертация различных объектов в класс tpr
Вы можете преобразоватьвектор из дат, или строковый вектор состоящий из дат в формате *YYYY-MM-DD* в объект класса *tpr* с помощью функции `as_timeperiod()`.

```{r, echo=TRUE}
dates <- c("2019-09-11", "2019-09-02", "2019-10-11", "2019-08-30")
dates_tpr <- as_timeperiod(dates)
dates_tpr
```

В приведённом выше примере конвертации строкового вектора в объект класса *tpr*, исходный вектор содержит 4 даты, они идут в произвольном порядке, и в целом между ними много пропущенных дней, т.е. данный вектор не является непрерывной последовательностью дат. В таком случае функция `as_timeperiod()` находит в исходном векторе минимальную и максимальную дату, и создаёт объект класса *tpr* равный временному интервалу между этими двумя датами. 

# Пользовательские выходные дни
Иногда помимо официальных выходных дней вам может понадобиться выделить собственные дополнительные выходные дни, например ваш отпуск.
Пакет `timeperiodsR` даёт вам такую возможность с помощью опции или переменной среды.

В опцию `timeperiodsR.custom_day_offs` вы можете передать вектор с датами которые будут помечены как дополнительные выходные дни, такие дни в компоненте *dayoffs_marks* будут помечены значением 3.

Также вы можете задать переменную среды `TPR_CUSTOM_DAY_OFFS`, в которой укажите даты в формате ГГГГ-ММ-ДД разделённые запятыми или точками с запятой.

Таким образом вы можете помечать дополнительные выходные дни.
```{r, echo=TRUE}
options("timeperiodsR.custom_day_offs" = c("2020-01-14", "2020-01-15", "2020-01-16", "2020-01-17", "2020-02-12"))
tm <- this_month("2020-01-01")

# получить ваши пользовательские выходные которые входят в текущий период
tm$custom_day_offs

# пользовательские выходные будут помечены 3
tm$dayoffs_marks
```
