Script or function to return how many days from now until a given date

5,467

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

enter image description here

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

enter image description here

enter image description here

And just for fun...

A tiny application. Add it to a shortcut if you use it often.

enter image description here

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

enter image description here

  • 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

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 4

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 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
Share:
5,467

Related videos on Youtube

Zanna
Author by

Zanna

Updated on September 18, 2022

Comments

  • Zanna
    Zanna over 1 year

    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?

    • Jacob Vlijm
      Jacob Vlijm over 7 years
      Probably no python? Anyway, convert time to epoch time (can be in any format), then it is easy :)
    • Zanna
      Zanna over 7 years
      @JacobVlijm python solution totally welcome - that will help me when I finally get around to learning python XD and I just want it to work too :)
    • Jacob Vlijm
      Jacob Vlijm over 7 years
      AHA, a moment...
  • Jacob Vlijm
    Jacob Vlijm over 7 years
    Nice, There is however one issue: I guess you don't want the number of days as a float, then simply dividing by 86400 will not work, possible rounding as a solution gives an incorrect output if you are near 24 hrs
  • Zanna
    Zanna over 7 years
    apart from incorrect rounding (it seems), this works well! I feel silly for doubting the powers of GNU date :) Thanks :)
  • Rabbi Kaii
    Rabbi Kaii over 7 years
    note Awk time functions are not POSIX
  • Zanna
    Zanna over 7 years
    Awesome! So simple and clear. Ruby seems like a great language :)
  • Jacob Vlijm
    Jacob Vlijm over 7 years
    Greatgreat! Welcome to Ruby :)
  • Anwar
    Anwar over 7 years
    @Zanna thanks. It really is. tryruby here if you got 15 mintues. :)
  • Anwar
    Anwar over 7 years
    @JacobVlijm Thank your for the encouragement. Though I'm still a student :)
  • Digital Trauma
    Digital Trauma over 7 years
    @Zanna - I think the solution to the rounding problem is simply to integer-divide both timestamps by 86400, before taking the difference. But there might be some detail I'm missing here. Also do you want the entered date to be local time or UTC? If its UTC, then add the -u parameter to date.
  • Zanna
    Zanna over 7 years
    Excellent :) Good to know about this package.
  • datacarl
    datacarl almost 5 years
    Days which switch between normal time and daylight saving times, might differ for +/- 1 hour and seldomly there are correction seconds placed in certain days. But in practice, this might be unimportant in most of the cases.