Script or function to return how many days from now until a given date
I would like to write a script or function to tell me how many days from now until a given date in the future. What I'm struggling to work out is how to process the given date and compare it with the current date... I'm imagining something like
read -p "enter the date in the format YYYY-MM-DD "
And then I'm assuming I have a string that's meaningless to the shell and I have to do some evaluations like...?? (This is just an example; I guess bc
would be needed)
i=$(($(date +%Y)-${REPLY%%-*}))
j=$(($(date +%m)-${REPLY:5:2}))
k=$(($(date +%d)-${REPLY##*-}))
And then I don't know what to do with those numbers...??
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))
I'm surely making it too hard for myself; probably there's a text processing tool that understands dates and can compare them.
How can I do this?
Solution 1:
Epoch time
In general, calculations on time are easiest if we first convert time into (Unix) epoch time (seconds from 1-1-1970). In python, we have tools to convert time into epoch time, and back into any date format we prefer.
We can simply set a format, like:
pattern = "%Y-%m-%d"
...and define today:
today = "2016-12-07"
and subsequently write a function to do the job:
def convert_toepoch(pattern, stamp):
return int(time.mktime(time.strptime(stamp, pattern)))
Then the output of:
nowepoch = convert_toepoch(pattern, today)
print(nowepoch)
> 1481065200
...which is, as mentioned, the number of seconds since 1-1-1970
Calculating the days between two dates
If we do this on both today and our future date, subsequently calculate the difference:
#!/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))
The output will be calculated by date, since we use the format %Y-%m-%d
. Rounding on seconds would possibly give an incorrect date difference, if we are near 24 hrs for example.
Terminal version
#!/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))
...And the Zenity option
#!/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))
])
And just for fun...
A tiny application. Add it to a shortcut if you use it often.
The script:
#!/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()
- Copy it into an empty file, save it as
orangedays.py
-
Run it:
python3 /path/to/orangedays.py
To wrap it up
Use for the tiny application script above the following .desktop
file:
[Desktop Entry]
Exec=/path/to/orangedays.py
Type=Application
Name=Orange Days
Icon=org.gnome.Calendar
- Copy the code into an empty file, save it as
orangedays.desktop
in~/.local/share/applications
-
In the line
Exec=/path/to/orangedays.py
set the actual path to the script...
Solution 2:
The GNU date
utility is quite good at this sort of thing. It is able to parse a good variety of date formats and then output in another format. Here we use %s
to output the number of seconds since the epoch. It is then a simple matter of arithmetic to subtract $now
from the $future
and divide by 86400 seconds/day:
#!/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"
Solution 3:
You could try doing something in awk
, using the mktime
function
awk '{print (mktime($0) - systime())/86400}'
The awk expects to read date from standard input in the format "YYYY MM DD HH MM SS" and then prints the difference between the time specified and the current time in days.
mktime
simply converts a time (in the specified format) to the number of seconds from a reference time (1970-01-01 00:00:00 UTC); systime simple specifies the current time in the same format. Subtract one from the other and you get how far apart they are in seconds. Divide by 86400 (24 * 60 * 60) to convert to days.
Solution 4:
Here is a Ruby version
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!"
Example Run:
Example run of the script ruby ./day-difference.rb
is given below (assuming you have saved it as day-difference.rb
)
With a future date
$ ruby day-difference.rb
Enter a future date in format YYYY-MM-DD
2021-12-30
That day will come after 1848 days
With a passed date
$ ruby day-difference.rb
Enter a future date in format YYYY-MM-DD
2007-11-12
That day passed 3314 days ago
When passed today's date
$ ruby day-difference.rb
Enter a future date in format YYYY-MM-DD
2016-12-8
Hey! That is today!
Here is an nice website to check the differences of date http://www.timeanddate.com/date/duration.html
Solution 5:
There is a dateutils
package which is very convenient for dealing with dates. Read more about it here github:dateutils
Install it by
sudo apt install dateutils
For your problem, simply,
dateutils.ddiff <start date> <end date> -f "%d days"
where the output can be chosen as seconds, minues, hours, days, weeks, months or years. It can be conveniently used in scripts where the output can be used for other tasks.
For example,
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