Tập lệnh hoặc hàm trả về bao nhiêu ngày kể từ bây giờ cho đến một ngày nhất định


28

Tôi muốn viết một kịch bản hoặc chức năng để cho tôi biết bao nhiêu ngày kể từ bây giờ cho đến một ngày nhất định trong tương lai. Điều tôi đang cố gắng giải quyết là làm thế nào để xử lý ngày đã cho và so sánh nó với ngày hiện tại ... Tôi đang tưởng tượng một cái gì đó như

read -p "enter the date in the format YYYY-MM-DD "

Và sau đó tôi giả sử rằng tôi có một chuỗi vô nghĩa với vỏ và tôi phải thực hiện một số đánh giá như ... ?? (Đây chỉ là một ví dụ; tôi đoán bcsẽ cần thiết)

i=$(($(date +%Y)-${REPLY%%-*}))
j=$(($(date +%m)-${REPLY:5:2}))
k=$(($(date +%d)-${REPLY##*-}))

Và rồi tôi không biết phải làm gì với những con số đó ... ??

if $i > 1 then assign l=$((i*365)) and else what?? # what about leap years?
Using $j somehow assign m   # confused before I've started
Using $k somehow assign n   # just as bad
echo $((l+m+n))   

Tôi chắc chắn làm cho nó quá khó cho bản thân mình; có lẽ có một công cụ xử lý văn bản hiểu ngày tháng và có thể so sánh chúng.

Tôi có thể làm cái này như thế nào?


Có lẽ không có con trăn? Dù sao, chuyển đổi thời gian thành thời gian kỷ nguyên (có thể ở bất kỳ định dạng nào), thật dễ dàng :)
Jacob Vlijm

@JacobVlijm giải pháp python hoàn toàn được chào đón - điều đó sẽ giúp tôi khi cuối cùng tôi cũng được học về python XD và tôi chỉ muốn nó hoạt động quá :)
Zanna

AHA, một khoảnh khắc ...
Jacob Vlijm 7/12/2016

Câu trả lời:


29

Thời đại kỷ nguyên

Nói chung, tính toán đúng thời gian là dễ nhất nếu lần đầu tiên chúng ta chuyển đổi thời gian thành thời gian kỷ nguyên (Unix) (giây từ 1-1-1970). Trong python, chúng tôi có các công cụ để chuyển đổi thời gian thành thời gian kỷ nguyên và quay lại bất kỳ định dạng ngày nào chúng tôi muốn.

Chúng ta chỉ cần đặt một định dạng, như:

pattern = "%Y-%m-%d"

... và xác định ngày hôm nay:

today = "2016-12-07"

và sau đó viết một hàm để thực hiện công việc:

def convert_toepoch(pattern, stamp):
    return int(time.mktime(time.strptime(stamp, pattern)))

Sau đó, đầu ra của:

nowepoch = convert_toepoch(pattern, today)
print(nowepoch)

> 1481065200

... đó là, như đã đề cập, số giây kể từ 1-1-1970

Tính ngày giữa hai ngày

Nếu chúng tôi làm điều này vào cả ngày hôm nay và ngày tương lai của chúng tôi, sau đó sẽ tính toán sự khác biệt:

#!/usr/bin/env python3
import time

# set our date pattern
pattern = "%Y-%m-%d" 

def convert_toepoch(pattern, stamp):
    return int(time.mktime(time.strptime(stamp, pattern)))

# automatically get today's date 
today = time.strftime(pattern); future = "2016-12-28"

nowepoch = convert_toepoch(pattern, today)
future_epoch = convert_toepoch(pattern, future)

print(int((future_epoch - nowepoch)/86400))

Đầu ra sẽ được tính theo ngày , vì chúng tôi sử dụng định dạng %Y-%m-%d. Làm tròn số giây có thể sẽ cho chênh lệch ngày không chính xác, nếu chúng ta ở gần 24 giờ chẳng hạn.

Phiên bản đầu cuối

#!/usr/bin/env python3
import time

# set our date pattern
pattern = "%Y-%m-%d" 

def convert_toepoch(pattern, stamp):
    return int(time.mktime(time.strptime(stamp, pattern)))

# automatically get today's date 
today = time.strftime(pattern)
# set future date
future = input("Please enter the future date (yyyy-mm-dd): ")
nowepoch = convert_toepoch(pattern, today)
future_epoch = convert_toepoch(pattern, future)
print(int((future_epoch - nowepoch)/86400))

nhập mô tả hình ảnh ở đây

... Và tùy chọn Zenity

#!/usr/bin/env python3
import time
import subprocess

# set our date pattern
pattern = "%Y-%m-%d" 

def convert_toepoch(pattern, stamp):
    return int(time.mktime(time.strptime(stamp, pattern)))

# automatically get today's date 
today = time.strftime(pattern)
# set future date
try:
    future = subprocess.check_output(
        ["zenity", "--entry", "--text=Enter a date (yyyy-mm-dd)"]
        ).decode("utf-8").strip()
except subprocess.CalledProcessError:
    pass
else:     
    nowepoch = convert_toepoch(pattern, today)
    future_epoch = convert_toepoch(pattern, future)
    subprocess.call(
        ["zenity", "--info",
         "--text="+str(int((future_epoch - nowepoch)/86400))
         ])

nhập mô tả hình ảnh ở đây

nhập mô tả hình ảnh ở đây

Và chỉ để cho vui ...

Một ứng dụng nhỏ. Thêm nó vào một phím tắt nếu bạn sử dụng nó thường xuyên.

nhập mô tả hình ảnh ở đây

Kịch bản:

#!/usr/bin/env python3
import time
import subprocess
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Pango, Gdk

class OrangDays(Gtk.Window):

    def __init__(self):

        self.pattern = "%Y-%m-%d" 
        self.currdate = time.strftime(self.pattern)
        big_font = "Ubuntu bold 45"
        self.firstchar = True

        Gtk.Window.__init__(self, title="OrangeDays")
        maingrid = Gtk.Grid()
        maingrid.set_border_width(10)
        self.add(maingrid)

        datelabel = Gtk.Label("Enter date")
        maingrid.attach(datelabel, 0, 0, 1, 1)

        self.datentry = Gtk.Entry()
        self.datentry.set_max_width_chars(12)
        self.datentry.set_width_chars(12)
        self.datentry.set_placeholder_text("yyyy-mm-dd")
        maingrid.attach(self.datentry, 2, 0, 1, 1)

        sep1 = Gtk.Grid()
        sep1.set_border_width(10)
        maingrid.attach(sep1, 0, 1, 3, 1)

        buttongrid = Gtk.Grid()
        buttongrid.set_column_homogeneous(True)
        maingrid.attach(buttongrid, 0, 2, 3, 1)

        fakebutton = Gtk.Grid()
        buttongrid.attach(fakebutton, 0, 0, 1, 1)

        calcbutton = Gtk.Button("Calculate")
        calcbutton.connect("clicked", self.showtime)
        calcbutton.set_size_request(80,10)
        buttongrid.attach(calcbutton, 1, 0, 1, 1)

        fakebutton2 = Gtk.Grid()
        buttongrid.attach(fakebutton2, 2, 0, 1, 1)

        sep2 = Gtk.Grid()
        sep2.set_border_width(5)
        buttongrid.attach(sep2, 0, 1, 1, 1)

        self.span = Gtk.Label("0")
        self.span.modify_font(Pango.FontDescription(big_font))
        self.span.set_alignment(xalign=0.5, yalign=0.5)
        self.span.modify_fg(Gtk.StateFlags.NORMAL, Gdk.color_parse("#FF7F2A"))
        maingrid.attach(self.span, 0, 4, 100, 1)

        sep3 = Gtk.Grid()
        sep3.set_border_width(5)
        maingrid.attach(sep3, 0, 5, 1, 1)

        buttonbox = Gtk.Box()
        maingrid.attach(buttonbox, 0, 6, 3, 1)
        quitbutton = Gtk.Button("Quit")
        quitbutton.connect("clicked", Gtk.main_quit)
        quitbutton.set_size_request(80,10)
        buttonbox.pack_end(quitbutton, False, False, 0)

    def convert_toepoch(self, pattern, stamp):
        return int(time.mktime(time.strptime(stamp, self.pattern)))

    def showtime(self, button):
        otherday = self.datentry.get_text()
        try:
            nextepoch = self.convert_toepoch(self.pattern, otherday)
        except ValueError:
            self.span.set_text("?")
        else:
            todayepoch = self.convert_toepoch(self.pattern, self.currdate)
            days = str(int(round((nextepoch-todayepoch)/86400)))
            self.span.set_text(days)


def run_gui():
    window = OrangDays()
    window.connect("delete-event", Gtk.main_quit)
    window.set_resizable(True)
    window.show_all()
    Gtk.main()

run_gui()
  • Sao chép nó vào một tập tin trống, lưu nó dưới dạng orangedays.py
  • Chạy nó:

    python3 /path/to/orangedays.py

Để gói nó

Sử dụng cho tập lệnh ứng dụng nhỏ phía trên .desktoptệp sau :

[Desktop Entry]
Exec=/path/to/orangedays.py
Type=Application
Name=Orange Days
Icon=org.gnome.Calendar

nhập mô tả hình ảnh ở đây

  • Sao chép mã vào một tệp trống, lưu nó như orangedays.desktoptrong~/.local/share/applications
  • Trong dòng

    Exec=/path/to/orangedays.py

    đặt đường dẫn thực tế tới tập lệnh ...


23

Các GNU datetiện ích khá tốt ở các loại điều này. Nó có thể phân tích một loạt các định dạng ngày tốt và sau đó xuất ra ở định dạng khác. Ở đây chúng tôi sử dụng %sđể xuất số giây kể từ kỷ nguyên. Đó là sau đó một vấn đề đơn giản của số học trừ $nowtừ $futurevà chia cho 86400 giây / ngày:

#!/bin/bash

read -p "enter the date in the format YYYY-MM-DD "

future=$(date -d "$REPLY" "+%s")
now=$(date "+%s")
echo "$(( ( $future / 86400 ) - ( $now / 86400 ) )) days"

ngoài việc làm tròn không chính xác (có vẻ như), điều này hoạt động tốt! Tôi cảm thấy ngớ ngẩn vì nghi ngờ sức mạnh của ngày GNU :) Cảm ơn :)
Zanna

1
@Zanna - Tôi nghĩ rằng giải pháp cho vấn đề làm tròn chỉ đơn giản là chia số nguyên cho cả hai dấu thời gian cho 86400, trước khi có sự khác biệt. Nhưng có thể có một số chi tiết tôi bị thiếu ở đây. Ngoài ra, bạn muốn ngày nhập là giờ địa phương hoặc UTC? Nếu UTC của nó, sau đó thêm -utham số vào date.
Chấn thương kỹ thuật số

Những ngày chuyển đổi giữa thời gian bình thường và thời gian tiết kiệm ánh sáng ban ngày, có thể khác nhau trong khoảng +/- 1 giờ và hiếm khi có những giây điều chỉnh được đặt trong một số ngày nhất định. Nhưng trong thực tế, điều này có thể không quan trọng trong hầu hết các trường hợp.
người dùng không xác định

10

Bạn có thể thử làm một cái gì đó trong awk, sử dụng mktimechức năng

awk '{print (mktime($0) - systime())/86400}'

Awk dự kiến ​​sẽ đọc ngày từ đầu vào tiêu chuẩn ở định dạng "YYYY MM DD HH MM SS" và sau đó in sự khác biệt giữa thời gian được chỉ định và thời gian hiện tại tính bằng ngày.

mktimechỉ cần chuyển đổi thời gian (theo định dạng đã chỉ định) thành số giây từ thời gian tham chiếu (1970-01-01 00:00:00 UTC); systime đơn giản chỉ định thời gian hiện tại trong cùng định dạng. Trừ đi cái khác và bạn sẽ cách nhau bao xa trong vài giây. Chia cho 86400 (24 * 60 * 60) để chuyển đổi thành ngày.


1
Tuy nhiên, có một vấn đề: Tôi đoán bạn không muốn số ngày trôi qua, sau đó chỉ cần chia cho 86400 sẽ không hoạt động, có thể làm tròn như một giải pháp cho đầu ra không chính xác nếu bạn ở gần 24 giờ
Jacob Vlijm

lưu ý các chức năng thời gian Awk không phải là POSIX
Steven Penny

10

Đây là phiên bản Ruby

require 'date'

puts "Enter a future date in format YYYY-MM-DD"
answer = gets.chomp

difference = (Date.parse(answer) - Date.today).numerator

puts difference > 1 ? "That day will come after #{difference} days" :
  (difference < 0) ? "That day passed #{difference.abs} days ago" :
 "Hey! That is today!"

Chạy ví dụ:

Ví dụ chạy tập lệnh ruby ./day-difference.rbđược đưa ra dưới đây (giả sử bạn đã lưu nó dưới dạng day-difference.rb)

Với một ngày trong tương lai

$ ruby day-difference.rb
Enter a future date in format YYYY-MM-DD
2021-12-30
That day will come after 1848 days

Với một ngày trôi qua

$ ruby day-difference.rb
Enter a future date in format YYYY-MM-DD
2007-11-12
That day passed 3314 days ago

Khi qua ngày hôm nay

$ ruby day-difference.rb
Enter a future date in format YYYY-MM-DD
2016-12-8
Hey! That is today!

Đây là một trang web đẹp để kiểm tra sự khác biệt của ngày http://www.timeanddate.com/date/duration.html


Tuyệt vời! Thật đơn giản và rõ ràng. Ruby có vẻ như là một ngôn ngữ tuyệt vời :)
Zanna

Tuyệt vời! Chào mừng bạn đến với Ruby :)
Jacob Vlijm

1
@Zanna cảm ơn. Nó thực sự là. hãy thử ở đây nếu bạn có 15 phút. :)
Anwar

@JacobVlijm Cảm ơn bạn đã khuyến khích. Mặc dù tôi vẫn còn là học sinh :)
Anwar

6

Có một dateutilsgói rất thuận tiện cho việc xử lý ngày. Tìm hiểu thêm về nó ở đây github: dateutils

Cài đặt nó bằng cách

sudo apt install dateutils

Đối với vấn đề của bạn, đơn giản,

dateutils.ddiff <start date> <end date> -f "%d days"

trong đó đầu ra có thể được chọn là giây, phút, giờ, ngày, tuần, tháng hoặc năm. Nó có thể được sử dụng thuận tiện trong các tập lệnh trong đó đầu ra có thể được sử dụng cho các tác vụ khác.


Ví dụ,

dateutils.ddiff 2016-12-26  2017-05-12 -f "%m month and %d days"
4 month and 16 days

dateutils.ddiff 2016-12-26  2017-05-12 -f "%d days"
137 days

Tuyệt vời :) Tốt để biết về gói này.
Zanna

2

Bạn có thể sử dụng thư viện Velk awk :

$ velour -n 'print t_secday(t_utc(2018, 7, 1) - t_now())'
7.16478

Hoặc là:

$ velour -n 'print t_secday(t_utc(ARGV[1], ARGV[2], ARGV[3]) - t_now())' 2018 7 1
7.16477

0

Một giải pháp ngắn, nếu cả hai ngày thuộc cùng một năm, là:

echo $((1$(date -d 2019-04-14 +%j) - 1$(date +%j)))

sử dụng định dạng "% j", trả về vị trí của ngày tính theo ngày trong năm, tức là 135 cho ngày hiện tại. Nó tránh các vấn đề làm tròn và xử lý ngày trong quá khứ, cho kết quả tiêu cực.

Tuy nhiên, vượt qua biên giới năm, điều này sẽ thất bại. Bạn có thể thêm (hoặc trừ) 365 thủ công cho mỗi năm hoặc 365 cho mỗi năm nhuận, nếu cuối tháng hai bị vượt qua, nhưng điều đó sẽ gần như dài dòng như các giải pháp khác.

Đây là giải pháp bash tinh khiết:

#!/bin/bash
#
# Input sanitizing and asking for user input, if no date was given, is left as an exercise
# Suitable only for dates from 1.1.1970 to 31.12.9999
#
# Get date as parameter (in format yyyy-MM-dd
#
date2=$1
# for testing, more convenient:
# date2=2019-04-14
#
year2=${date2:0:4}
year1=$(date +%Y)
#
# difference in days, ignoring years:
# since %j may lead to values like 080..099, 
# which get interpreted as invalid octal numbers, 
# I prefix them with "1" each (leads to 1080..1099) 
daydiff=$((1$(date -d 1$date2 +%j)- $(date +%j)))
#
yeardiff=$((year2-year1))
# echo yeardiff $yeardiff
#
#
# summarize days per year, except for the last year:
#
daysPerYearFromTo () {
    year1=$1
    year2=$2
    days=0
    for y in $(seq $year1 $((year2-1)))
    do
        ((days+=$(date -d $y-12-31 +"%j")))
    done
    echo $days
}
# summarize days per year in the past, except for the last year:
#
daysPerYearReverse () {
    year1=$1
    year2=$2
    days=0
    for y in $(seq $((year1-1)) -1 $year2)
    do
        ((days+=$(date -d $y-12-31 +"%j")))
    done
    echo $days
}

case $yeardiff in
    0) echo $daydiff
        ;;
    # date in one of previous years:
    -[0-9]*) echo $((daydiff-$(daysPerYearReverse $year1 $year2)))
        ;;
    # date in one of future years:
    [0-9]*) echo $((daydiff+$(daysPerYearFromTo $year1 $year2)))
        ;;
esac

Shellcheck gợi ý rất nhiều trích dẫn kép, nhưng trong những ngày vượt quá năm 9999, bạn nên xem xét một cách tiếp cận khác. Đối với quá khứ, nó sẽ thất bại âm thầm cho những ngày trước 1970.01.01. Vệ sinh đầu vào của người dùng được để lại như một bài tập cho người dùng.

Hai chức năng có thể được tái cấu trúc thành một, nhưng điều đó có thể làm cho nó khó hiểu hơn.

Xin lưu ý rằng tập lệnh cần thử nghiệm toàn diện để xử lý các năm nhuận trong quá khứ một cách chính xác. Tôi sẽ không đặt cược nó là đúng.

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.