返回从现在到给定日期多少天的脚本或函数


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吗?无论如何,将时间转换为纪元时间(可以是任何格式),这很容易:)
Jacob Vlijm

完全欢迎@JacobVlijm python解决方案-当我终于开始学习python XD并且我也希望它也起作用时,这将对我有所帮助:)
Zanna 2016年

AHA,请稍等...
Jacob Vlijm '16

Answers:


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日以来的秒数

计算两个日期之间的天数

如果我们在今天和将来的日期都这样做,请随后计算差异:

#!/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
@Zanna-我认为解决舍入问题的方法只是简单地将两个时间戳除以86400,然后再取其差值。但是这里可能缺少一些细节。您还希望输入的日期是当地时间还是UTC?如果是UTC,则将-u参数添加到date
Digital Trauma

在正常时间和夏令时之间切换的日期可能会相差+/- 1小时,并且在某些日子中很少设置校正秒。但是实际上,在大多数情况下这可能并不重要。
未知用户

10

您可以尝试awk使用mktime函数在中进行操作

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

awk希望从标准输入中以“ YYYY MM DD HH MM SS”格式读取日期,然后以天为单位显示指定时间与当前时间之间的时差。

mktime只需将时间(以指定格式)转换为参考时间(1970年1月1日00:00:00 UTC)的秒数;systime simple以相同格式指定当前时间。彼此相减,您可以在几秒钟之内得到它们之间的距离。除以86400(24 * 60 * 60)即可转换为天数。


1
很好,但是有一个问题:我想您不希望将天数作为浮动日期,然后简单地除以86400将不起作用,可能的舍入是一种解决方案,如果您在24小时附近会给出错误的输出
Jacob Vlijm

note注释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似乎是一种很棒的语言:)
Zanna

很棒很棒!欢迎使用Ruby :)
Jacob Vlijm,2013年

1
@Zanna谢谢。真的是 如果您有15分钟的时间,请尝试tryruby。:)
Anwar

@JacobVlijm感谢您的鼓励。虽然我仍然是学生:)
Anwar

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

太好了:)很高兴知道这个套餐。
赞纳

2

您可以使用awk Velor库

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

要么:

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

0

如果两个日期都属于同一年,则一个简短的解决方案是:

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

使用“%j”格式,该格式返回以天为单位的日期位置,即当前日期为135。它避免了舍入问题并处理了过去的日期,从而产生了负面结果。

但是,跨年边界将失败。如果2月的最后一天已过,您可以每年手动添加(或减去)365,或每个leap年添加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

Shellcheck建议使用很多双引号,但是对于超过9999年的日期,您应该考虑使用其他方法。对于过去,它将在1970.01.01之前的日期默默失败。清理用户输入作为练习留给用户。

可以将这两个功能重构为一个功能,但这可能会使它更难理解。

请注意,该脚本需要进行详尽的测试才能正确处理过去的leap年。我敢打赌这是对的。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.