Причина, по которой исходное решение медленное, связана с системным вызовом date
.Каждая запись / строка, которую вы обрабатываете с помощью awk, вызывает внешнюю команду для выполнения преобразования даты.Такой внешний вызов должен быть загружен в память, выполнен, и его вывод должен быть обработан awk.Мы можем ускорить это, если сможем выполнить фактическое преобразование даты в самом awk.
общий комментарий: при преобразовании даты и времени из UTC в местный часовой пояс выНеобходимо учитывать, что 1 января находится в другом часовом поясе, чем 1 августа. Это связано с переходом на летнее время.Приведенный ниже алгоритм не дает решения этой проблемы, поскольку OP только что запросил смещение на 4 часа или смещение в его текущий часовой пояс.( примечание: решение для gawk 4.1.2 или новее будет учитывать DST) *
Ниже я представлю несколько решений, которые вы можете использовать в зависимости от того, какой awk вы используете:
Gnu awk: Одним из различных расширений gawk
являются функции времени.Две полезные функции времени для этой задачи: mktime
и strftime
:
mktime(datespec,[utc-flag])
: это преобразует строку спецификации даты,datespec
, формы YYYY MM DD hh mm ss
во время эпохи Unix, то есть общее количество секунд с 1970 г. 01 01 UTC.Начиная с gawk-4.2.1 вы можете использовать utc-flag
, чтобы указать, datespec
в UTC или нет.До gawk-4.2.1 предполагался местный часовой пояс.
strftime(format,timestamp,[utc-flag])
: это преобразовывает время эпохи timestamp
в форматированную строку(форматирование такое же, как у команды date
).Вы можете использовать utc-flag
, чтобы указать, что возвращаемое время должно быть в UTC или в местном часовом поясе.
Подробнее в руководстве GNU awk
Мы хотим преобразовать поле 1 из UTC в местный часовой пояс.Поскольку мы не знаем формат поля 1, мы предполагаем существование функции convert_time(str)
, которая форматирует str
в формат YYYY MM DD hh mm ss
, который может быть принят mktime
:
gawk 4.1.2 или новее:
$ awk 'BEGIN{FS=OFS=","}
{ # convert $1 into YYYY MM DD hh mm ss
datestring=convert_time($1)
# convert datestring (as UTC) into epoch
datum=mktime(datestring,1)
# convert epoch into string (local TZ)
datum=strftime("\042%Y%m%d_%H\042",datum)
# print and append
print $0,datum
}' data.txt
до gawk 4.1.2: Здесь мы не можем использовать utc-flag
, поэтому мы заставляем awk работать вUTC:
$ TZ=UTC awk 'BEGIN{FS=OFS=","}
{ # convert $1 into YYYY MM DD hh mm ss
datestring=convert_time($1)
# convert datestring (as UTC) into epoch
datum=mktime(datestring)
# perform TZ correction
datum-=4*3600;
# convert epoch into string (local TZ)
datum=strftime("\042%Y%m%d_%H\042",datum)
# print and append
print $0,datum
}' data.txt
POSIX awk: если у вас нет GNU awk, но есть любой другой awk, вы не можете использовать эти функции времени, поскольку они специфичны для GNU awk.Однако их можно реализовать:
awk '
# Algorithm from "Astronomical Algorithms" By J.Meeus
function mktime_posix(datestring, a,t) {
split(datestring,a," ")
if (a[1] < 1970) return -1
if (a[2] <= 2) { a[1]--; a[2]+=12 }
t=int(a[1]/100); t=2-t+int(t/4)
t=int(365.25*a[1]) + int(30.6001*(a[2]+1)) + a[3] + t - 719593
return t*86400 + a[4]*3600 + a[5]*60 + a[6]
}
function strftime_posix(epoch, JD,yyyy,mm,dd,HH,MM,SS,A,B,C,D,E ) {
if (epoch < 0 ) return "0000 00 00 00 00 00.000000"
JD=epoch; SS=JD%60; JD-=SS; JD/=60; MM=JD%60;
JD-=MM; JD/=60; HH=JD%24; JD-=HH; JD/=24;
JD+=2440588
A=int((JD - 1867216.25)/(36524.25))
A=JD+1+A-int(A/4)
B=A+1524; C=int((B-122.1)/365.25); D=int(365.25*C); E=int((B-D)/30.6001)
dd=B-D-int(30.6001*E)
mm = E < 14 ? E-1 : E - 13
yyyy=mm>2?C-4716:C-4715
return sprintf("\042%0.4d%0.2d%0.2d_%0.2d\-42",yyyy,mm,dd,HH)
}
{ # convert $1 into YYYY MM DD hh mm ss
datestring=convert_time($1)
# convert datestring (as UTC) into epoch
datum=mktime_posix(datestring)
# perform TZ correction
datum-=4*3600;
# convert epoch into string (local TZ)
datum=strftime_posix(datum)
# print and append
print $0,datum
}' data.txt