У меня есть файл с записями в таком формате:
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