Сдвиг дат не работает так, как ожидалось

Сдвиг дат не работает так, как ожидалось

У меня есть файл с записями в таком формате:

D20220327,S2927,977,1

D20220328,S2927,977,1

D20220329,S2927,977,1

D20220330,S2927,977,1

D20220331,S2927,977,1

D20220401,S2927,977,1

D20220402,S2927,977,1

D20220403,S2927,977,1

D20220404,S2927,977,1

Однако после применения преобразования для смещения их на 7 дней назад в прошлое, это не работает для дат с 28 марта по 03 апреля, однако та же логика кода работает нормально 27 марта и 04 апреля. Я не могу понять, почему это не работает только для одной недели. Это вывод

D20220320,S2927,977,1 -- correct

D20220320,S2927,977,1 -- incorrect 

D20220321,S2927,977,1 -- incorrect

D20220322,S2927,977,1 -- incorrect

D20220323,S2927,977,1 -- incorrect

D20220324,S2927,977,1 -- incorrect

D20220325,S2927,977,1 -- incorrect

D20220326,S2927,977,1 -- incorrect

D20220328,S2927,977,1 -- correct

Логика, используемая здесь, следующая:

    BEGIN {
        OFS = FS = ","
}

{
        t = mktime(sprintf("%4d %.2d %.2d 00 00 00",
                substr($1,2,4),
                substr($1,6,2),
                substr($1,8,2)));

        $1 = substr($1,1,1) strftime("%Y%m%d", t - 7*24*60*60)

        print
}

решение1

Ваши расчеты производятся по местному времени, и на вас повлияет переход на летнее время 27 марта.

Чтобы выполнить расчет по времени UTC (временные метки Unix не соответствуют местному времени), используя последнюю версию GNU awk, убедитесь, что вы передаете дополнительный параметр 1в качестве последнего аргумента mktime():

t = mktime(sprintf("%4d %.2d %.2d 00 00 00",
        substr($1,2,4),
        substr($1,6,2),
        substr($1,8,2)), 1);

Это awkрасширение GNU, доступное в awkверсии GNU 4.2.0+.

В качестве альтернативы вы можете не использовать время около полуночи (UTC) в качестве эталонного времени суток:

t = mktime(sprintf("%4d %.2d %.2d 12 00 00",
        substr($1,2,4),
        substr($1,6,2),
        substr($1,8,2)));

Это позволило бы работать в старых awkреализациях GNU и в любых других awk, имеющих требуемые функции.

Еще один вариант — запустить скрипт с измененным местным часовым поясом:

TZ=UTC awk -f script.awk inputfile

Это устанавливает TZпеременную среды UTCдля выполнения скрипта awk, которая изменяет часовой пояс, используемый функциями mktime()и связанными с ними функциями.

решение2

Использование Raku (ранее известного как Perl_6)

raku -pe 's/^ D <( (\d**4)(\d**2)(\d**2) )> \, /{ "$0-$1-$2".Date.earlier(:7days).Str.subst("-", :g); }/;'

Пример ввода (пустые строки удалены):

D20220327,S2927,977,1
D20220328,S2927,977,1
D20220329,S2927,977,1
D20220330,S2927,977,1
D20220331,S2927,977,1
D20220401,S2927,977,1
D20220402,S2927,977,1
D20220403,S2927,977,1
D20220404,S2927,977,1

Пример вывода:

D20220320,S2927,977,1
D20220321,S2927,977,1
D20220322,S2927,977,1
D20220323,S2927,977,1
D20220324,S2927,977,1
D20220325,S2927,977,1
D20220326,S2927,977,1
D20220327,S2927,977,1
D20220328,S2927,977,1

-peВкратце, используются флаги Raku linewise (автопечать) в сочетании с привычным s///оператором. Цифры захватываются в переменные соответствия $0, $1, и $2со скобками, а маркеры захвата <( … )>используются для удаления всех других элементов соответствия.

В замене Raku выполняет код внутри { … }блока. Захваты $0, $1и $2преобразуются в строку с соответствующими тире ( -), и эта строка распознается как Dateобъект, на котором earlier(:7days)может быть вызван метод. [Примечание: некоторые пользователи могут посчитать запись earlier(days => 7)более привычным синтаксисом — любая форма работает]. Как только Dateобъект отбрасывается на 7 дней назад, он Strпреобразуется в -ingified и substиспользуется для удаления тире ( -) в возврате.

https://docs.raku.org/routine/Дата
https://docs.raku.org/routine/Dateish
https://raku.org

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