Cách phân tích ngày ISO8601 bằng lệnh ngày linux


15

Tôi đang cố gắng sử dụng lệnh date để tạo dấu thời gian tệp mà chính lệnh date có thể diễn giải. Tuy nhiên, lệnh date dường như không thích đầu ra của chính nó và tôi không chắc làm thế nào để khắc phục điều này. Trường hợp tại điểm:

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

ngày dường như đang diễn giải chuỗi với độ lệch 15 giờ. Có bất kỳ giải pháp được biết đến cho điều này?

Chỉnh sửa: đây không phải là vấn đề hiển thị:

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
>>> 

Nó vẫn tắt sau 15 giờ khi được hiển thị dưới dạng dấu thời gian unix.

EDIT # 1

Có lẽ tôi nên đặt ra câu hỏi này một chút khác nhau. Giả sử tôi có một danh sách các dấu thời gian cơ bản của ISO8601 có dạng:

  • YYYYMMDDThhmm
  • YYYYMMDDThhmmss

Cách đơn giản nhất để chuyển đổi chúng thành dấu thời gian Unix tương ứng là gì?

Ví dụ:

- 20140103T1422   = 1388787720
- 20140103T142233 = 1388787753

1
@drewbenn Tôi không thể có bất kỳ ký tự đặc biệt nào trong dấu thời gian. Chỉ cần số và chữ. Vì vậy, không, tôi không thể làm điều đó, thật không may.
alex.forencich

@sim TZ không được đặt, nhưng / etc / localtime được liên kết.
alex.forencich

Bạn đang giết tôi, đây có phải là câu hỏi cuối cùng của bạn? 8-)
slm

20140103T1518không hợp lệ ISO 8601, nó bỏ lỡ phần múi giờ
Ferrybig

Câu trả lời:


9

Bạn yêu cầu "cách giải quyết đã biết." Đây là một điều đơn giản:

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

Điều này sử dụng sedđể thay thế "T" bằng một khoảng trắng. Kết quả là một định dạng mà datehiểu.

Nếu chúng ta thêm giây vào ngày ISO8601, thì datecần nhiều thay đổi hơn:

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

Trong phần trên, sedthay thế chữ "T" bằng khoảng trắng và cũng tách HHMMSS thành HH: MM: SS.


Hoạt động với tôi nếu dấu + bị xóa. Tuy nhiên, nó không hoạt động đối với dấu thời gian chính xác thứ hai, chỉ chính xác phút.
alex.forencich

@ alex.forencich Trả lời cập nhật với độ chính xác giây. Hãy cho tôi biết nếu định dạng giây mà tôi chọn không phải là định dạng bạn cần.
John1024

8

Các tài liệu thông tin về coreutils nói rằng "định dạng mở rộng" ISO 8601 được hỗ trợ.

Bạn sẽ cần thêm dấu gạch ngang, dấu hai chấm và a +%zđể làm cho nó hoạt động.

$ 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

Để trả lời phần thứ hai của câu hỏi ...

Vì định dạng ngày chỉ chứa số và ký hiệu, bạn có thể thay thế mỗi ký hiệu bằng một chữ cái duy nhất, ví dụ: sử dụng 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

Hoặc bạn có thể phân tích cú pháp bằng cách sử dụng T-hoặc +như dấu phân cách, ví dụ: sử dụng shell ${var%word}${var#word}mở rộng

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

hoặc sử dụng bashkết hợp biểu thức chính quy

$ 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

hoặc Perl, Python, v.v.


Dấu thời gian không thể có bất kỳ ký tự đặc biệt nào trong đó. Bạn có biết một cách tốt để thêm lại những thứ đó một cách tự động không?
alex.forencich

6

GNU coreutils chỉ hỗ trợ ngày ISO 8601 làm đầu vào kể từ phiên bản 8.13 (phát hành ngày 2011-09-08). Bạn phải sử dụng một phiên bản cũ hơn.

Trong các phiên bản cũ hơn, bạn cần thay thế Tbằng một khoảng trắng. Mặt khác, nó được hiểu là múi giờ của quân đội Hoa Kỳ .

Ngay cả trong các phiên bản gần đây, chỉ có hình thức chấm câu đầy đủ được công nhận, không phải là định dạng cơ bản chỉ có chữ số và chữ Tở giữa.

# 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"

2

Tôi đã nhận thấy ghi chú này trong trang người đàn ông cho 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.

Nó không kết luận nhưng nó không hiển thị rõ ràng chuỗi định dạng thời gian bao gồm Tnhư bạn đang cố gắng, cho [ISO 8601]. Như câu trả lời @Gilles đã chỉ ra, sự hỗ trợ của ISO 8601 trong GNU CoreUtils là tương đối mới.

Định dạng lại chuỗi

Bạn có thể sử dụng Perl để định dạng lại chuỗi của bạn.

Thí dụ:

$ 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

Bạn có thể làm cho điều này xử lý cả hai chuỗi bao gồm giây và những chuỗi không.

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

@ alex.forencich - một lệnh thay thế sẽ xử lý cả hai định dạng thời gian. Làm cho tôi một ưu tiên và xóa các ý kiến ​​trên không còn phù hợp.
slm

1

Theo trang man của ngày, định dạng mà bạn xuất ra không giống với những gì datemong đợi như đầu vào. Đây là những gì trang người đàn ông nói:

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

Vì vậy, bạn có thể làm điều đó như thế này:

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

Bởi vì trong các biến được sử dụng để xác định chuỗi đầu ra, +%m%d%H%M%Ysẽ bằng với những gì nó mong đợi là đầu vào.


Sau đó, bạn có thể cung cấp một lệnh để ánh xạ ngày định dạng ISO8601 vào ngày nào yêu cầu không? Các dấu thời gian được lưu trữ thực tế phải ở định dạng ISO8601 để chúng có thể được sắp xếp theo ngày.
alex.forencich
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.