Skrip atau fungsi untuk mengembalikan berapa hari dari sekarang hingga tanggal yang ditentukan

28

Saya ingin menulis naskah atau fungsi untuk memberi tahu saya berapa hari dari sekarang sampai tanggal tertentu di masa depan. Yang saya perjuangkan adalah bagaimana memproses tanggal yang diberikan dan membandingkannya dengan tanggal saat ini ... Saya membayangkan sesuatu seperti

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

Dan kemudian saya berasumsi saya memiliki string yang tidak berarti bagi shell dan saya harus melakukan beberapa evaluasi seperti ... ?? (Ini hanya sebuah contoh; Saya kira bcakan dibutuhkan)

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

Dan kemudian saya tidak tahu apa yang harus dilakukan dengan angka-angka itu ... ??

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

Saya pasti membuatnya terlalu sulit untuk diri saya sendiri; mungkin ada alat pemrosesan teks yang memahami tanggal dan dapat membandingkannya.

Bagaimana saya bisa melakukan ini?

Zanna
sumber
Mungkin tidak ada python? Bagaimanapun, ubah waktu menjadi waktu (bisa dalam format apa pun), maka itu mudah :)
Jacob Vlijm
@JacobVlijm solusi python benar-benar menyambut - yang akan membantu saya ketika saya akhirnya belajar python XD dan saya hanya ingin itu bekerja juga :)
Zanna
AHA, sebentar ...
Jacob Vlijm

Jawaban:

29

Waktu zaman

Secara umum, perhitungan waktu paling mudah jika kita pertama kali mengubah waktu menjadi (Unix) waktu zaman (detik dari 1-1-1970). Dengan python, kita memiliki alat untuk mengubah waktu menjadi waktu, dan kembali ke format tanggal yang kita inginkan.

Kami cukup mengatur format, seperti:

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

... dan tentukan hari ini:

today = "2016-12-07"

dan kemudian menulis fungsi untuk melakukan pekerjaan:

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

Maka output dari:

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

> 1481065200

... yang, sebagaimana disebutkan, jumlah detik sejak 1-1-1970

Menghitung hari antara dua tanggal

Jika kita melakukan ini pada hari ini dan tanggal masa depan kita, kemudian hitung selisihnya:

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

Output akan dihitung berdasarkan tanggal , karena kami menggunakan format %Y-%m-%d. Membulatkan detik mungkin akan memberikan perbedaan tanggal yang salah, jika kita hampir 24 jam misalnya.

Versi terminal

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

masukkan deskripsi gambar di sini

... Dan opsi 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))
         ])

masukkan deskripsi gambar di sini

masukkan deskripsi gambar di sini

Dan hanya untuk bersenang-senang ...

Aplikasi kecil. Tambahkan ke jalan pintas jika Anda sering menggunakannya.

masukkan deskripsi gambar di sini

Naskah:

#!/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()
  • Salin ke file kosong, simpan sebagai orangedays.py
  • Menjalankannya:

    python3 /path/to/orangedays.py

Untuk membungkusnya

Gunakan untuk skrip aplikasi kecil di atas .desktopfile berikut :

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

masukkan deskripsi gambar di sini

  • Salin kode ke file kosong, simpan seperti orangedays.desktopdalam~/.local/share/applications
  • Dalam barisan

    Exec=/path/to/orangedays.py

    setel jalur aktual ke skrip ...

Yakub Vlijm
sumber
23

The GNU dateutilitas cukup baik di hal semacam ini. Ia mampu mem-parsing beragam format tanggal yang baik dan kemudian output dalam format lain. Di sini kita gunakan %suntuk menampilkan jumlah detik sejak zaman. Maka masalah aritmatika sederhana untuk dikurangkan $nowdari $futuredan dibagi dengan 86400 detik / hari:

#!/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"
Trauma Digital
sumber
terlepas dari pembulatan yang salah (tampaknya), ini bekerja dengan baik! Saya merasa konyol karena meragukan kekuatan tanggal GNU :) Terima kasih :)
Zanna
1
@Zanna - Saya pikir solusi untuk masalah pembulatan adalah dengan integer-bagi kedua cap waktu dengan 86400, sebelum mengambil perbedaan. Tapi mungkin ada beberapa detail yang saya lewatkan di sini. Anda juga ingin tanggal yang dimasukkan adalah waktu setempat atau UTC? Jika UTC, lalu tambahkan -uparameter ke date.
Digital Trauma
Hari yang beralih antara waktu normal dan waktu musim panas, mungkin berbeda untuk +/- 1 jam dan jarang ada detik koreksi yang ditempatkan pada hari-hari tertentu. Namun dalam praktiknya, ini mungkin tidak penting dalam sebagian besar kasus.
pengguna tidak diketahui
10

Anda dapat mencoba melakukan sesuatu awk, menggunakan mktimefungsinya

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

Awk mengharapkan untuk membaca tanggal dari input standar dalam format "YYYY MM DD HH MM SS" dan kemudian mencetak perbedaan antara waktu yang ditentukan dan waktu saat ini dalam beberapa hari.

mktimecukup mengonversi waktu (dalam format yang ditentukan) ke jumlah detik dari waktu referensi (1970-01-01 00:00:00 UTC); systime simple menentukan waktu saat ini dalam format yang sama. Kurangi satu dari yang lain dan Anda mendapatkan seberapa jauh mereka dalam hitungan detik. Bagi dengan 86400 (24 * 60 * 60) untuk dikonversi menjadi hari.

Nick Sillito
sumber
1
Bagus, Namun ada satu masalah: Saya kira Anda tidak ingin jumlah hari sebagai pelampung, maka hanya membagi dengan 86400 tidak akan bekerja, mungkin pembulatan sebagai solusi memberikan hasil yang salah jika Anda berada di dekat 24 jam
Jacob Vlijm
catatan Fungsi waktu awk tidak POSIX
Steven Penny
10

Ini adalah versi 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!"

Contoh Jalankan:

Contoh menjalankan skrip ruby ./day-difference.rbdiberikan di bawah ini (dengan asumsi Anda telah menyimpannya sebagai day-difference.rb)

Dengan tanggal di masa depan

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

Dengan tanggal yang dilewati

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

Ketika melewati tanggal hari ini

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

Berikut adalah situs web yang bagus untuk memeriksa perbedaan tanggal http://www.timeanddate.com/date/duration.html

Anwar
sumber
Luar biasa! Sangat sederhana dan jelas. Ruby sepertinya bahasa yang luar biasa :)
Zanna
Hebat! Selamat datang di Ruby :)
Jacob Vlijm
1
@Zanna terima kasih. Memang benar. tryruby di sini jika Anda mendapat 15 permen. :)
Anwar
@JacobVlijm Terima kasih atas dorongannya. Meskipun saya masih mahasiswa :)
Anwar
6

Ada dateutilspaket yang sangat nyaman untuk berurusan dengan tanggal. Baca lebih lanjut di sini github: dateutils

Instal dengan

sudo apt install dateutils

Untuk masalah Anda, cukup,

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

di mana output dapat dipilih sebagai detik, menit, jam, hari, minggu, bulan atau tahun. Ini dapat dengan mudah digunakan dalam skrip di mana output dapat digunakan untuk tugas-tugas lain.


Sebagai contoh,

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
ankit7540
sumber
Luar biasa :) Baik untuk mengetahui tentang paket ini.
Zanna
2

Anda dapat menggunakan perpustakaan Velk awk :

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

Atau:

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

Solusi singkat, jika kedua tanggal tersebut berasal dari tahun yang sama, adalah:

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

menggunakan format "% j", yang mengembalikan posisi tanggal dalam hari-hari dalam setahun, yaitu 135 untuk tanggal saat ini. Ini menghindari masalah pembulatan dan menangani tanggal di masa lalu, memberikan hasil negatif.

Namun, melewati batas tahun, ini akan gagal. Anda bisa menambahkan (atau mengurangi) 365 secara manual untuk setiap tahun atau 366 untuk setiap tahun kabisat, jika yang terakhir bulan Februari dilewati, tetapi itu hampir sama dengan solusi lainnya.

Di sini solusi bash murni:

#!/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 menyarankan banyak mengutip ganda, tetapi untuk hari-hari melebihi tahun 9999 Anda harus mempertimbangkan pendekatan yang berbeda. Untuk masa lalu, akan gagal secara diam-diam untuk tanggal sebelum 1970.01.01. Membersihkan input pengguna dibiarkan sebagai latihan bagi pengguna.

Kedua fungsi tersebut dapat di refactored menjadi satu, tetapi itu mungkin membuatnya lebih sulit untuk dipahami.

Harap dicatat, bahwa skrip membutuhkan pengujian lengkap untuk menangani tahun kabisat di masa lalu dengan benar. Saya tidak akan bertaruh itu benar.

Pengguna tidak diketahui
sumber