Cara mengurai tanggal ISO8601 dengan perintah tanggal linux

15

Saya mencoba menggunakan perintah tanggal untuk menghasilkan cap waktu file yang dapat ditafsirkan oleh perintah tanggal. Namun, perintah tanggal sepertinya tidak menyukai outputnya sendiri, dan saya tidak yakin bagaimana cara mengatasinya. Inti masalah:

sh-4.2$ date
Fri Jan  3 14:22:19 PST 2014
sh-4.2$ date +%Y%m%dT%H%M
20140103T1422
sh-4.2$ date -d "20140103T1422"
Thu Jan  2 23:22:00 PST 2014

tanggal tampaknya menafsirkan string dengan offset 15 jam. Apakah ada solusi yang diketahui untuk ini?

Sunting: ini bukan masalah tampilan:

sh-4.2$ date +%s
1388791096
sh-4.2$ date +%Y%m%dT%H%M
20140103T1518
sh-4.2$ date -d 20140103T1518 +%s
1388737080
sh-4.2$ python
Python 3.3.3 (default, Nov 26 2013, 13:33:18) 
[GCC 4.8.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 1388737080 - 1388791096
-54016
>>> 54016/3600
15.004444444444445
>>> 

Ini masih mati 15 jam ketika ditampilkan sebagai cap waktu unix.

EDIT # 1

Mungkin saya harus mengajukan pertanyaan ini sedikit berbeda. Katakanlah saya memiliki daftar cap waktu dasar ISO8601 dari formulir:

  • YYYYMMDDTerima kasih
  • YYYYMMDDTerima kasih

Apa cara paling sederhana untuk mengubahnya menjadi cap waktu Unix yang sesuai?

Sebagai contoh:

- 20140103T1422   = 1388787720
- 20140103T142233 = 1388787753
alex.forencich
sumber
1
@drewbenn Saya tidak dapat memiliki karakter khusus di stempel waktu. Hanya angka dan huruf. Jadi tidak, saya tidak bisa melakukan itu, sayangnya.
alex.forencich
@sim TZ tidak disetel, tetapi / etc / localtime ditautkan.
alex.forencich
Kau membunuhku, apakah ini pertanyaan terakhirmu? 8-)
slm
20140103T1518tidak valid ISO 8601, itu merindukan bagian zona waktu
Ferrybig

Jawaban:

9

Anda meminta "solusi yang diketahui." Ini yang sederhana:

$ date -d "$(echo 20140103T1422 | sed 's/T/ /')"
Fri Jan  3 14:22:00 PST 2014

Ini digunakan seduntuk mengganti "T" dengan spasi. Hasilnya adalah format itudate mengerti.

Jika kita menambahkan detik ke tanggal ISO8601, maka datememerlukan lebih banyak perubahan:

$ date -d "$(echo 20140103T142211 | sed -r 's/(.*)T(..)(..)(..)/\1 \2:\3:\4/')"
Fri Jan  3 14:22:11 PST 2014

Di atas, sedmenggantikan "T" dengan spasi dan juga memisahkan HHMMSS menjadi HH: MM: SS.

John1024
sumber
Bekerja untuk saya jika + dihapus. Namun, itu tidak bekerja untuk cap waktu presisi kedua, hanya presisi menit.
alex.forencich
@ alex.forencich Jawaban diperbarui dengan ketepatan detik. Beri tahu saya jika format detik yang saya pilih bukan yang Anda butuhkan.
John1024
8

Itu info coreutils mengatakan bahwa ISO 8601 "format diperluas" didukung.

Anda harus menambahkan tanda hubung, titik dua, dan a +%zuntuk membuatnya bekerja.

$ date +"%Y-%m-%dT%H:%M:%S%z"
2014-01-03T16:08:23-0800
$ date -d 2014-01-03T16:08:23-0800
Fri Jan  3 16:08:23 PST 2014

Untuk menjawab bagian kedua dari pertanyaan Anda ...

Karena format tanggal hanya berisi angka dan simbol, Anda dapat mengganti setiap simbol dengan huruf unik, misalnya menggunakan tr

$ ts="$(date +"%Y-%m-%dT%H:%M:%S%z" | tr -- '-:+' 'hcp')"; echo "$ts"
2014h01h03T16c18c04h0800
$ date -d "$(echo "$ts" | tr -- 'hcp' '-:+')"
Fri Jan  3 16:18:04 PST 2014

Atau Anda dapat menguraikannya menggunakan Tdan dan -atau +sebagai pemisah, misalnya menggunakan shell ${var%word}dan${var#word} ekspansi

$ ts="$(date +"%Y%m%dT%H%M%S%z")"; echo "$ts"
20140103T162228-0800
$ date=${ts%T*}; time=${ts#*T}
etc.    

atau menggunakan bash pencocokan ekspresi reguler

$ ts="$(date +"%Y%m%dT%H%M%S%z")"; echo "$ts"
20140103T165611-0800
$ [[ "$ts" =~ (.*)(..)(..)T(..)(..)(..)(.....) ]]
$ match=("${BASH_REMATCH[@]}")
$ Y=${match[1]}; m=${match[2]}; d=${match[3]}; H=${match[4]}; M=${match[5]}; S=${match[6]}; z=${match[7]}
$ date -d "$Y-$m-$d"T"$H:$M:$S$z"
Fri Jan  3 16:56:11 PST 2014

atau Perl, Python, dll.

Mikel
sumber
Stempel waktu tidak dapat memiliki karakter khusus di dalamnya. Apakah Anda tahu cara yang baik untuk menambahkannya kembali secara otomatis?
alex.forencich
6

GNU coreutils hanya mendukung tanggal ISO 8601 sebagai input sejak versi 8.13 (dirilis pada 2011-09-08). Anda harus menggunakan versi yang lebih lama.

Di bawah versi yang lebih lama, Anda perlu mengganti T dengan spasi. Kalau tidak, itu ditafsirkan sebagai zona waktu militer AS .

Bahkan di bawah versi terbaru, hanya formulir yang diselingi sepenuhnya yang dikenali, bukan format dasar dengan hanya digit dan a Tdi tengah.

# Given a possibly abbreviated ISO date $iso_date...
date_part=${iso_date%%T*}
if [ "$date_part" != "$iso_date" ]; then
  time_part=${abbreviated_iso_date#*T}
  case ${iso_date#*T} in
    [!0-9]*) :;;
    [0-9]|[0-9][0-9]) time_part=${time_part}:00;;
    *)
      hour=${time_part%${time_part#??}}
      minute=${time_part%${time_part#????}}; minute=${minute#??}
      time_part=${hour}:${minute}:${time_part#????};;
  esac
else
  time_part=
fi
date -d "$date_part $time_part"
Gilles 'SO- berhenti menjadi jahat'
sumber
2

Saya memang memperhatikan catatan ini di halaman manual untuk date .

DATE STRING
      The --date=STRING is a mostly free format human readable date string
      such as "Sun, 29 Feb 2004 16:21:42 -0800"  or  "2004-02-29
      16:21:42"  or  even  "next Thursday".  A date string may contain 
      items indicating calendar date, time of day, time zone, day of
      week, relative time, relative date, and numbers.  An empty string 
      indicates the beginning of the day.  The date  string  format
      is more complex than is easily documented here but is fully described 
      in the info documentation.

Itu tidak konklusif tetapi tidak secara eksplisit menunjukkan string format waktu yang mencakup Tsaat Anda mencoba, untuk [ISO 8601]. Sebagai ditunjukkan @Gilles , dukungan dari ISO 8601 di GNU CoreUtils relatif baru.

Memformat ulang string

Anda dapat menggunakan Perl untuk memformulasi ulang string Anda.

Contoh:

$ date -d "$(perl -pe 's/(.*)T(\d{2})(\d{2})(\d{2})/$1 $2:$3:$4/' \
    <<<"20140103T142233")"
Fri Jan  3 14:22:33 EST 2014

Anda dapat membuat ini menangani kedua string yang termasuk detik dan yang tidak.

20140103T1422:

$ date -d "$(perl -pe 's/^(.*)T(\d{2})(\d{2})(\d{2})$/$1 $2:$3:$4/ || \
     s/^(.*)T(\d{2})(\d{2})$/$1 $2:$3:00/' <<<"20140103T1422")"
Fri Jan  3 14:22:00 EST 2014

20140103T142233:

$ date -d "$(perl -pe 's/^(.*)T(\d{2})(\d{2})(\d{2})$/$1 $2:$3:$4/ || \
     s/^(.*)T(\d{2})(\d{2})$/$1 $2:$3:00/' <<<"20140103T142233")"
Fri Jan  3 14:22:33 EST 2014
slm
sumber
@ alex.forencich - perintah alternatif yang akan menangani kedua format waktu. Tolong saya dan hapus komentar di atas yang tidak lagi relevan.
slm
1

Menurut halaman manual tanggal, format yang Anda hasilkan tidak sama dengan apa yang datediharapkan sebagai input. Inilah yang dikatakan halaman manual:

date [-u|--utc|--universal] [MMDDhhmm[[CC]YY][.ss]]

Jadi Anda bisa melakukannya seperti ini:

# date +%m%d%H%M%Y
010402052014
# date 010402052014
Sat Jan  4 02:05:00 EAT 2014

Karena dalam variabel yang digunakan untuk mendefinisikan string output, +%m%d%H%M%Yakan sama dengan apa yang diharapkan sebagai input.

ulangan
sumber
Lalu bisakah Anda memberikan perintah untuk memetakan tanggal format ISO8601 ke tanggal berapa yang dibutuhkan? Stempel waktu yang sebenarnya disimpan harus dalam format ISO8601 sehingga dapat diurutkan berdasarkan tanggal.
alex.forencich