現在から指定された日付までの日数を返すスクリプトまたは関数


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

私は間違いなく自分のためにそれを難し​​くしすぎています。おそらく、日付を理解して比較できるテキスト処理ツールがあります。

これどうやってするの?


おそらくPythonはありませんか?とにかく、時間をエポック時間に変換すると(任意の形式にできます)、簡単です:)
ジェイコブVlijm

@JacobVlijm pythonソリューションは大歓迎-ついにpython XDの学習に取り掛かり、それも機能したいときに役立ちます:)
Zanna

AHA、瞬間...
ジェイコブVlijm

回答:


29

エポックタイム

一般に、時間を最初に(Unix)に変換すると、時間の計算が最も簡単になります エポック時間(1-1-1970からの秒ます。Pythonには、時間をエポック時間に変換し、好みの日付形式に戻すツールがあります。

次のような形式を設定するだけです。

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

...前述のように、1970年1月1日から1秒までの秒数です。

2つの日付間の日を計算する

今日と将来の日付の両方でこれを行う場合、続いて差を計算します。

#!/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し、$future86400秒/日で割るのは簡単な算術です:

#!/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 dateの力を疑うのは馬鹿げている:)ありがとう:)
Zanna

1
@Zanna-丸めの問題の解決策は、差分を取る前に両方のタイムスタンプを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
ニース、しかし、1つの問題があります。日数を浮動小数点数にしたくないので、単純に86400で除算しても機能しません。24時間近い場合、ソリューションとしての丸めは不正確な出力になります
Jacob Vlijm

注Awk時間関数はPOSIXではない
スティーブンペニー

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


驚くばかり!とてもシンプルで明確。Rubyは素晴らしい言語のようです:)
ザンナ

すごい!Rubyへようこそ:)
ジェイコブVlijm

1
@Zannaありがとう。本当にそうです。tryrubyあなたは15 mintuesを得た場合、ここ。:)
アンワー

@JacobVlijm励ましてくれてありがとう。私はまだ学生ですが:)
アンワー

6

dateutils日付を扱うのに非常に便利なパッケージがあります。詳細はこちらgithub:dateutils

でインストールする

sudo apt install dateutils

あなたの問題については、単純に、

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

出力は、秒、分、時間、日、週、月、または年として選択できます。出力を他のタスクに使用できるスクリプトで便利に使用できます。


例えば、

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

すばらしい:)このパッケージについて知っておくと良い。
ザンナ


0

両方の日付が同じ年に属する場合の簡単な解決策は次のとおりです。

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

「%j」形式を使用します。これは、年の日数、つまり現在の日付の135で日付の位置を返します。丸めの問題を回避し、過去の日付を処理して、否定的な結果をもたらします。

ただし、年の境界を超えると、これは失敗します。2月の最後を超える場合は、各年に手動で365、うるう年に366を手動で追加(または減算)できますが、それは他のソリューションと同じくらい冗長になります。

ここで純粋なbashソリューション:

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

シェルチェックは多くの二重引用符を推奨していますが、9999年を超える日については、別のアプローチを検討する必要があります。過去の場合、1970.01.01より前の日付については黙って失敗します。ユーザー入力のサニタイズは、ユーザーへの課題として残されています。

2つの関数は1つにリファクタリングできますが、そのため理解が難しくなる可能性があります。

過去のうるう年を正しく処理するには、スクリプトが徹底的なテストを必要とすることに注意してください。私はそれが正しいとは思わないでしょう。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.