สคริปต์หรือฟังก์ชันเพื่อส่งคืนจำนวนวันนับจากวันนี้จนถึงวันที่กำหนด


28

ฉันต้องการเขียนสคริปต์หรือฟังก์ชั่นเพื่อบอกฉันว่ากี่วันนับจากนี้จนถึงวันที่กำหนดในอนาคต สิ่งที่ฉันกำลังดิ้นรนเพื่อหาวิธีคือการประมวลผลวันที่ที่กำหนดและเปรียบเทียบกับวันที่ปัจจุบัน ... ฉันจินตนาการถึงบางอย่างเช่น

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

แล้วฉันคิดว่าฉันมีสตริงที่ไม่มีความหมายต่อเชลล์และฉันต้องทำการประเมินบางอย่างเช่น ... ?? (นี่เป็นเพียงตัวอย่าง; ฉันคิดว่าbcจะต้อง)

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

แล้วฉันก็ไม่รู้จะทำยังไงกับตัวเลขเหล่านั้น ... ?

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

แน่นอนว่าฉันจะทำให้มันยากเกินไปสำหรับตัวเอง อาจมีเครื่องมือประมวลผลข้อความที่เข้าใจวันที่และสามารถเปรียบเทียบได้

ฉันจะทำสิ่งนี้ได้อย่างไร


อาจจะไม่มีงูหลาม? อย่างไรก็ตามแปลงเวลายุค (อาจจะอยู่ในรูปแบบใด ๆ ) แล้วมันเป็นเรื่องง่าย :)
จาค็อบ Vlijm

@JacobVlijm โซลูชัน python ยินดีต้อนรับโดยสิ้นเชิง - นั่นจะช่วยฉันในที่สุดเมื่อฉันได้รับรอบเพื่อเรียนรู้ python XD และฉันแค่ต้องการให้มันทำงานด้วย :)
Zanna

AHA สักครู่ ...
Jacob Vlijm

คำตอบ:


29

กาลเวลา

โดยทั่วไปการคำนวณตรงเวลานั้นง่ายที่สุดถ้าเราแปลงเวลาเป็นเวลา (Unix) ยุค (วินาทีจาก 1-1-1970) ในไพ ธ อนเรามีเครื่องมือในการแปลงเวลาเป็นยุคกาลและกลับไปเป็นรูปแบบวันที่ใด ๆ ที่เราต้องการ

เราสามารถกำหนดรูปแบบเช่น:

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

... และกำหนดวันนี้:

today = "2016-12-07"

และเขียนฟังก์ชั่นเพื่อทำงาน:

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

จากนั้นผลลัพธ์ของ:

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

> 1481065200

... ซึ่งก็คือตามที่กล่าวถึงจำนวนวินาทีตั้งแต่ 1-1-1970

การคำนวณวันระหว่างสองวัน

หากเราทำทั้งในวันนี้และวันที่ในอนาคตของเราให้คำนวณความแตกต่าง:

#!/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))

เอาท์พุทจะถูกคำนวณโดยวัน%Y-%m-%dเนื่องจากเราใช้รูปแบบ การปัดเศษเป็นวินาทีอาจทำให้ความแตกต่างของวันที่ไม่ถูกต้องหากเราอยู่ใกล้ 24 ชั่วโมง

เวอร์ชันเทอร์มินัล

#!/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))

ป้อนคำอธิบายรูปภาพที่นี่

... และตัวเลือก 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))
         ])

ป้อนคำอธิบายรูปภาพที่นี่

ป้อนคำอธิบายรูปภาพที่นี่

และเพื่อความสนุกสนาน ...

แอปพลิเคชั่นเล็ก ๆ เพิ่มลงในทางลัดหากคุณใช้บ่อย

ป้อนคำอธิบายรูปภาพที่นี่

บท:

#!/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()
  • คัดลอกลงในไฟล์เปล่าบันทึกเป็น orangedays.py
  • เรียกใช้:

    python3 /path/to/orangedays.py

ในการพันมัน

ใช้สำหรับสคริปต์แอปพลิเคชันขนาดเล็กด้านบน.desktopไฟล์ต่อไปนี้:

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

ป้อนคำอธิบายรูปภาพที่นี่

  • คัดลอกรหัสไปยังไฟล์ที่ว่างเปล่าบันทึกorangedays.desktopใน~/.local/share/applications
  • ในสาย

    Exec=/path/to/orangedays.py

    กำหนดเส้นทางที่แท้จริงไปยังสคริปต์ ...


23

GNU dateสาธารณูปโภคค่อนข้างดีที่จัดเรียงของสิ่งนี้ สามารถแยกวิเคราะห์รูปแบบวันที่ที่หลากหลายและแสดงผลในรูปแบบอื่นได้ ที่นี่เราใช้%sในการส่งออกจำนวนวินาทีตั้งแต่ยุค จากนั้นเป็นเรื่องง่ายของการคำนวณทางคณิตศาสตร์ที่จะลบออก$nowจาก$futureและหารด้วย 86400 วินาที / วัน:

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

นอกเหนือจากการปัดเศษที่ไม่ถูกต้อง (ดูเหมือน) นี่ใช้ได้ดี! ฉันรู้สึกไร้สาระที่สงสัยอำนาจของวันที่ GNU :) ขอบคุณ :)
Zanna

1
@ ซานน่า - ฉันคิดว่าวิธีแก้ปัญหาการปัดเศษเป็นเพียงการหารจำนวนเต็มทั้งสองด้วย 86400 ก่อนที่จะทำให้เกิดความแตกต่าง แต่อาจมีรายละเอียดบางอย่างฉันหายไปที่นี่ คุณต้องการให้วันที่ที่ป้อนเป็นเวลาท้องถิ่นหรือ UTC ด้วยหรือไม่ หาก UTC ของมันแล้วเพิ่มพารามิเตอร์-u date
บาดเจ็บทางดิจิทัล

วันที่สลับไปมาระหว่างเวลาปกติและเวลาออมแสงอาจแตกต่างกันสำหรับ +/- 1 ชั่วโมงและไม่ค่อยมีการแก้ไขวินาทีในบางวัน แต่ในทางปฏิบัติอาจไม่สำคัญในกรณีส่วนใหญ่
ผู้ใช้ไม่ทราบ

10

คุณสามารถลองทำบางสิ่งawkได้โดยใช้mktimeฟังก์ชั่น

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

awk คาดว่าจะอ่านวันที่จากอินพุตมาตรฐานในรูปแบบ "YYYY MM DD HH MM SS" จากนั้นพิมพ์ความแตกต่างระหว่างเวลาที่ระบุและเวลาปัจจุบันเป็นวัน

mktimeเพียงแปลงเวลา (ในรูปแบบที่กำหนด) เป็นจำนวนวินาทีจากเวลาอ้างอิง (1970-01-01 00:00:00 UTC) systime simple ระบุเวลาปัจจุบันในรูปแบบเดียวกัน ลบสิ่งหนึ่งจากอีกอันหนึ่งและคุณจะได้รู้ว่าพวกมันอยู่ห่างกันเพียงเสี้ยววินาที หารด้วย 86400 (24 * 60 * 60) เพื่อแปลงเป็นวัน


1
ดีมีอย่างไรก็ตามมีปัญหาหนึ่ง: ฉันเดาว่าคุณไม่ต้องการจำนวนวันเป็นทศนิยมแล้วก็หารด้วย 86400 จะไม่ทำงานการปัดเศษที่เป็นไปได้เป็นวิธีแก้ปัญหาให้ผลลัพธ์ที่ไม่ถูกต้องหากคุณอยู่ใกล้ 24 ชั่วโมง
Jacob Vlijm

หมายเหตุฟังก์ชั่นเวลา Awk ไม่ใช่ POSIX
Steven Penny

10

นี่คือรุ่น 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!"

ตัวอย่างการเรียกใช้:

ตัวอย่างการใช้งานสคริปต์ruby ./day-difference.rbให้ไว้ด้านล่าง (สมมติว่าคุณบันทึกเป็นday-difference.rb)

ด้วยวันที่ในอนาคต

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

ด้วยวันที่ผ่าน

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

เมื่อผ่านวันที่วันนี้

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

นี่เป็นเว็บไซต์ที่ดีในการตรวจสอบความแตกต่างของวันที่http://www.timeanddate.com/date/duration.html


! น่ากลัว ง่ายและชัดเจน ดูเหมือนว่าทับทิมจะเป็นภาษาที่ยอดเยี่ยม :)
Zanna

Greatgreat! ยินดีต้อนรับสู่ Ruby :)
Jacob Vlijm

1
@ ซานน่าขอบคุณ มันคือเรื่องจริง ลองที่นี่ถ้าคุณมี 15 นาที :)
Anwar

@JacobVlijm ขอบคุณที่ให้กำลังใจ แม้ว่าฉันจะยังเป็นนักเรียนอยู่ก็ตาม :)
อันวาร์

6

มีdateutilsแพ็คเกจที่สะดวกมากสำหรับการจัดการกับวันที่ อ่านเพิ่มเติมเกี่ยวกับที่นี่github: dateutils

ติดตั้งโดย

sudo apt install dateutils

สำหรับปัญหาของคุณเพียงแค่

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

โดยที่เอาต์พุตสามารถเลือกเป็นวินาที, minues, ชั่วโมง, วัน, สัปดาห์, เดือนหรือปี มันสามารถใช้งานได้อย่างสะดวกในสคริปต์ที่สามารถใช้เอาต์พุตสำหรับงานอื่น ๆ


ตัวอย่างเช่น,

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

ดีมาก :) เรื่องน่ารู้เกี่ยวกับแพ็คเกจนี้
Zanna


0

คำตอบสั้น ๆ หากวันที่ทั้งสองเป็นของปีเดียวกันคือ:

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

ใช้ "% j" - จัดรูปแบบซึ่งส่งคืนตำแหน่งของวันที่เป็นวันในปีเช่น 135 สำหรับวันที่ปัจจุบัน มันหลีกเลี่ยงปัญหาการปัดเศษและจัดการกับวันที่ในอดีตให้ผลลัพธ์เชิงลบ

อย่างไรก็ตามการข้ามพรมแดนปีสิ่งนี้จะล้มเหลว คุณสามารถเพิ่ม (หรือลบ) 365 ด้วยตนเองสำหรับแต่ละปีหรือ 366 สำหรับแต่ละปีอธิกสุรทินถ้าข้ามเดือนสุดท้ายของเดือนกุมภาพันธ์ แต่จะใกล้เคียงกับโซลูชันอื่น ๆ

นี่คือวิธีการทุบตีบริสุทธิ์:

#!/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 เสนอการอ้างอิงสองเท่า แต่สำหรับวันที่เกิน 9999 ปีคุณควรพิจารณาแนวทางที่แตกต่าง ในอดีตมันจะล้มเหลวอย่างเงียบ ๆ สำหรับวันที่ก่อนปี 1970.01.01 ฆ่าเชื้อการป้อนข้อมูลของผู้ใช้ที่เหลือเป็นแบบฝึกหัดให้กับผู้ใช้

ฟังก์ชั่นทั้งสองนี้สามารถปรับให้เป็นหนึ่งเดียว แต่อาจทำให้เข้าใจยากขึ้น

โปรดทราบว่าสคริปต์ต้องการการทดสอบที่ละเอียดถี่ถ้วนเพื่อจัดการกับปีอธิกสุรทินที่ผ่านมาอย่างถูกต้อง ฉันจะไม่เดิมพันมันถูกต้อง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.