Как разбить диапазон дат на дни с помощью скрипта

Как разбить диапазон дат на дни с помощью скрипта

У меня есть следующие данные:

      startdate             end date         val1    val2
2015-10-13 07:00:02 2015-10-19 00:00:00      45      1900

в котором одна строка указывает диапазон дат, охватывающий несколько дней, и я хочу разбить диапазон на отдельные периоды времени, каждый из которых будет подмножеством дня (каждый на отдельной строке), чтобы облегчить параллельную обработку (многодневного) диапазона.

Выход должен быть

2015-10-13 07:00:02 2015-10-13 23:59:59      45      1900
2015-10-14 00:00:01 2015-10-14 23:59:59      45      1900
2015-10-15 00:00:01 2015-10-15 23:59:59      45      1900
2015-10-16 00:00:01 2015-10-16 23:59:59      45      1900
2015-10-17 00:00:01 2015-10-17 23:59:59      45      1900
2015-10-18 00:00:01 2015-10-18 23:59:59      45      1900
2015-10-19 00:00:01 2015-10-19 00:00:00      45      1900

где данные после конечного времени (val1 и val2) реплицируются в каждой строке. 

  1. На самом деле входные записи поступают из таблицы Hive, а выходные записи также будут сохраняться в разделенной таблице.

Модификации:

Разделение по дате нормально. Необходимо также разделить значение val2 в соответствии с датой разделения.

если разница дат равна 2, то мы разделим 2 строки, которые должны быть

  • ряд 1:

отношение = отношение времени, затраченного на 1-й день (т.е. конец-начало в 1-й день) /val1

val2= отношение*val2

  • ряд 2:

отношение = отношение времени, затраченного на 1-й день (т.е. конец-начало на 2-й день) /val1

val2= отношение*знач2

Как мне это написать?

решение1

Этот скрипт сделает то, что вы хотите (если я правильно понял ваши требования). Я взял на себя смелость экстраполировать вашу спецификацию, чтобы разрешить вводу иметь одну строку заголовка, а затем любое количество строк с диапазонами даты/времени. Я проиллюстрирую это и обсужу подробнее ниже.

#!/bin/sh
if IFS= read header
then
        printf "%s\n" "$header"
else
        echo 'EOF on first line!' >&2
        exit 1
fi
while read start_date start_time end_date end_time other_data           # See note, below.
do
        start_epoch=$(date +"%s" -d "$start_date $start_time")  ||  {
                echo "Error processing start date&time $start_date $start_time" >&2
                exit 1
        }
        end_epoch=$(date +"%s" -d "$end_date $end_time")  ||  {
                echo "Error processing end date&time $end_date $end_time" >&2
                exit 1
        }
        if [ "$end_epoch" -lt "$start_epoch" ]
        then
                echo "End date&time $end_date $end_time is before start date&time $start_date $start_time" >&2
                # Now what?
                continue
        fi
        ok_seq=1        # Flag: we are moving forward.
        current_date="$start_date"
        current_time="$start_time"
        while [ "$ok_seq" -ne 0 ]
        do
                # Most days end at 23:59:59.
                eod_time="23:59:59"
                eod_epoch=$(date +"%s" -d "$current_date $eod_time")  ||  {
                        # This should never happen.
                        echo "Error processing end-of-day date&time $current_date $eod_time" >&2
                        exit 1
                }
                if [ "$end_epoch" -lt "$eod_epoch" ]    # We’re passing the end of the date/time range.
                then
                        if [ "$current_date" != "$end_date" ]
                        then
                                # Sanity check -- this should not happen.
                                echo "We're finishing, but the current date is $current_date and the end date is $end_date" >&2
                        fi
                        eod_time="$end_time"
                        ok_seq=0
                fi
                                                                        # See note, below.
                printf "%s %s %s %s      %s\n" "$current_date" "$current_time" "$current_date" "$eod_time" "$other_data"
                # We could also use +"%F" for the full YYYY-mm-dd date.
                current_date=$(date +"%Y-%m-%d" -d "$current_date next day")  ||  {
                        # This shouldn’t happen.
                        echo "Error getting next day after $current_date" >&2
                        exit 1
                }
                current_time="00:00:01"
        done
done

Обсуждение:

  • Прочитайте строку заголовка. Если это не удалось, прервите выполнение скрипта. Если это удалось, запишите строку в вывод. Если (как показывает ваш вопрос) вы не хотите, чтобы заголовок был в вашем выводе, удалите оператор printf "%s\n" "$header".
  • Как упоминалось выше: цикл, чтение строк начала/конца/значения из ввода, пока не достигнем конца ввода (или не получим фатальную ошибку). Если вы не хотите этого делать, удалите while, doи соответствующий done.
  • Считывает начальную дату, время начала, конечную дату, время окончания и другие данные.  other_dataВключает все после времени окончания, т. е. val1 и val2 (и все пространство между ними).
  • Используйте команду для преобразования произвольных строк даты/времени в Unix «время эпохи» — количество секунд с 1970-01-01 00:00:00 (GMT). Это позволяет нам проверять ввод (и выходить в случае ошибки), а также дает нам числа, которые мы можем сравнивать. (Хотя я предполагаю, что мы могли бы просто сравнивать строки со значениями, отформатированными как YYYY-MM-DD HH:MM:SS.)date +"%s" -d "date/time string"
  • Если конечная дата/время предшествует начальной дате/времени, пропустите эту запись и перейдите к следующей строке. Если вы хотите сделать что-то другое (например, завершить) в этом случае, измените этот код.
  • Установите флаг ( ok_seq), который мы будем использовать для управления циклом, проходящим через дни. Инициализируйте начальную дату/время для первого дня, чтобы они стали начальной датой/временем для всего периода.
  • В каждой выходной строке начальная и конечная даты совпадают. В большинстве строк время конца дня (eod) — 23:59:59. Если (та же дата) + 23:59:59 больше (позже) даты/времени конца периода, то мы находимся на последнем дне (выходной строке) диапазона. Установите время eod на конечное время и установите ok_seqна 0, чтобы выйти из цикла.
  • Запишите строку вывода, включая «другие данные» (val1 и val2 и т. д.)
  • Вычислите дату следующего дня. Установите начальное время на 00:00:01, которое появится в каждой выходной строке, кроме первой.

Пример:

$ cat input
      startdate             end date         val1    val2
2015-10-13 07:00:02 2015-10-19 00:00:00      45      1900
2015-11-01 08:30:00 2015-11-05 15:00:00      42      6083
2015-12-27 12:00:00 2016-01-04 12:34:56      17      quux

$ ./script < input
      startdate             end date         val1    val2
2015-10-13 07:00:02 2015-10-13 23:59:59      45      1900
2015-10-14 00:00:01 2015-10-14 23:59:59      45      1900
2015-10-15 00:00:01 2015-10-15 23:59:59      45      1900
2015-10-16 00:00:01 2015-10-16 23:59:59      45      1900
2015-10-17 00:00:01 2015-10-17 23:59:59      45      1900
2015-10-18 00:00:01 2015-10-18 23:59:59      45      1900
2015-10-19 00:00:01 2015-10-19 00:00:00      45      1900
2015-11-01 08:30:00 2015-11-01 23:59:59      42      6083
2015-11-02 00:00:01 2015-11-02 23:59:59      42      6083
2015-11-03 00:00:01 2015-11-03 23:59:59      42      6083
2015-11-04 00:00:01 2015-11-04 23:59:59      42      6083
2015-11-05 00:00:01 2015-11-05 15:00:00      42      6083
2015-12-27 12:00:00 2015-12-27 23:59:59      17      quux
2015-12-28 00:00:01 2015-12-28 23:59:59      17      quux
2015-12-29 00:00:01 2015-12-29 23:59:59      17      quux
2015-12-30 00:00:01 2015-12-30 23:59:59      17      quux
2015-12-31 00:00:01 2015-12-31 23:59:59      17      quux
2016-01-01 00:00:01 2016-01-01 23:59:59      17      quux
2016-01-02 00:00:01 2016-01-02 23:59:59      17      quux
2016-01-03 00:00:01 2016-01-03 23:59:59      17      quux
2016-01-04 00:00:01 2016-01-04 12:34:56      17      quux

Обратите внимание, что проблем с переходом не возникает не только с одного месяца на другой, но и с одного года на другой.


Примечание: Когда я писал приведенную выше версию скрипта, я не мог понять, как захватить пробел между конечным временем и val1, поэтому я получал вывод, который выглядел следующим образом:

      startdate             end date         val1    val2
2015-10-13 07:00:02 2015-10-13 23:59:59 45      1900
2015-10-14 00:00:01 2015-10-14 23:59:59 45      1900
2015-10-15 00:00:01 2015-10-15 23:59:59 45      1900

поэтому я «схитрил», встроив «правильное количество» пространства в printfкоманду (перед последним %s). Но если вы измените интервал во входных данных, приведенная выше версия скрипта снова выведет неправильно выровненные столбцы. Я понял, как это исправить, хотя это немного беспорядочно. Замените строки while … dostart_epoch=…на:

while read start_date start_time end_date other_data
do
        # $other_data includes end_time and all the following values.
        # Break them apart:
        end_time="${other_data%%[       ]*}"
        other_data="${other_data#"$end_time"}"
        start_epoch=…

where end_timeбыл удален из readкоманды, а символы между скобками [и ] являются пробелом и табуляцией. Так что теперь other_dataсодержит пробелы перед val1. Затем измените printfна

                printf "%s %s %s %s%s\n" "$current_date" "$current_time" "$current_date" "$eod_time" "$other_data"

(обратите внимание, что естьнетпробел между четвертым и пятым %s). Итак, теперь все готово.

решение2

Я предполагаю, что вы хотите избавиться от верхней строки заголовка. Допустим, функция, из которой вы получаете эти входные данные, называется 'timefunc'. Вы можете попробовать перенаправить вывод timefunc в команду cut, например так:

timefunc | cut -d$'\n' -f2

Теперь вывод такой:

2015-10-13 07:00:02 2015-10-19 00:00:00      45      1900

решение3

Вы можете удалить строки заголовка из вывода с помощью grep:

inputcmd | grep -v startdate

Связанный контент