web-dev-qa-db-fra.com

Script ou fonction pour retourner combien de jours à partir de maintenant jusqu'à une date donnée

J'aimerais écrire un script ou une fonction pour me dire combien de jours à compter de maintenant jusqu'à une date donnée dans le futur. Ce que j'ai du mal à comprendre, c'est comment traiter la date donnée et la comparer avec la date actuelle ... J'imagine quelque chose comme:

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

Et puis je suppose que j'ai une chaîne qui n'a pas de sens pour Shell et je dois faire des évaluations comme ... ?? (Ceci est juste un exemple; je suppose que bc serait nécessaire)

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

Et puis je ne sais pas quoi faire avec ces chiffres ... ??

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

Je le fais sûrement trop dur pour moi-même; il existe probablement un outil de traitement de texte qui comprend les dates et peut les comparer.

Comment puis-je faire ceci?

28
Zanna

Époque de l'époque

En général, les calculs sur le temps sont plus faciles si nous convertissons d'abord le temps en (Unix) Epoch temps (secondes de 1-1-1970). En python, nous disposons d’outils permettant de convertir l’heure en heure d’une époque, puis de revenir au format de date de notre choix.

Nous pouvons simplement définir un format, comme:

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

... et définir aujourd'hui:

today = "2016-12-07"

et ensuite écrire une fonction pour faire le travail:

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

Puis la sortie de:

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

> 1481065200

... qui est, comme mentionné, le nombre de secondes depuis le 1-1-1970

Calculer les jours entre deux dates

Si nous faisons cela à la fois aujourd'hui et à notre date future, calculons ensuite la différence:

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

La sortie sera calculée par date , car nous utilisons le format %Y-%m-%d. Arrondir sur secondes donnerait peut-être une différence de date incorrecte, si nous approchons des 24 heures par exemple.

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

enter image description here

... et l'option 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))
         ])

enter image description here

enter image description here

Et juste pour le plaisir ...

Une petite application. Ajoutez-le à un raccourci si vous l'utilisez souvent.

enter image description here

Le 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()
  • Copiez-le dans un fichier vide, enregistrez-le sous orangedays.py
  • Exécuter:

    python3 /path/to/orangedays.py
    

Pour envelopper

Utilisez pour le petit script d’application situé au-dessus du fichier .desktop suivant:

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

enter image description here

  • Copiez le code dans un fichier vide, enregistrez-le sous le nom orangedays.desktop dans ~/.local/share/applications
  • Dans la ligne

    Exec=/path/to/orangedays.py
    

    définir le chemin d'accès au script ...

29
Jacob Vlijm

Le tilitaire GNU date est très bon pour ce genre de chose. Il est capable d’analyser une grande variété de formats de date, puis de le sortir dans un autre format. Ici, nous utilisons %s pour sortir le nombre de secondes depuis l’époque. C’est alors une simple question d’arithmétique de soustraire $now du $future et de le diviser par 86400 secondes/jour:

#!/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"
23
Digital Trauma

Vous pouvez essayer de faire quelque chose dans awk, en utilisant la fonction mktime

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

L'awk s'attend à lire la date à partir de l'entrée standard au format "AAAA MM JJ HH MM SS", puis imprime la différence entre l'heure spécifiée et l'heure actuelle en jours.

mktime convertit simplement une heure (au format spécifié) en nombre de secondes à partir d'une heure de référence (1970-01-01 00:00:00 UTC); systime simple spécifie l'heure actuelle dans le même format. Soustrayez les uns des autres et vous obtenez leur distance en quelques secondes. Divisez par 86400 (24 * 60 * 60) pour convertir en jours.

10
Nick Sillito

Voici une version 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!"

Exemple Run:

Un exemple d’exécution du script Ruby ./day-difference.rb est donné ci-dessous (en supposant que vous l’ayez sauvegardé sous le nom day-difference.rb)

Avec une date future

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

Avec une date passée

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

Quand passé la date d'aujourd'hui

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

Voici un site de Nice pour vérifier les différences de date http://www.timeanddate.com/date/duration.html

10
Anwar

Il existe un package dateutils qui est très pratique pour traiter les dates. En savoir plus à ce sujet ici github: dateutils

Installez le par

Sudo apt install dateutils

Pour votre problème, simplement,

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

où la sortie peut être choisie en secondes, minutes, heures, jours, semaines, mois ou années. Il peut être facilement utilisé dans des scripts où la sortie peut être utilisée pour d'autres tâches.


Par exemple,

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
6
ankit7540

Vous pouvez utiliser la commande awk Velour library :

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

Ou:

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

Une solution courte, si les deux dates appartiennent à la même année, est la suivante:

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

en utilisant le format "% j", qui renvoie la position de la date en jours de l’année, c’est-à-dire 135 pour la date du jour. Il évite les problèmes d'arrondi et traite les dates du passé, donnant des résultats négatifs.

Cependant, au-delà des années, cela échouera. Vous pouvez ajouter (ou soustraire) 365 manuellement pour chaque année ou 366 pour chaque année bissextile, si le dernier mois de février est franchi, mais ce sera presque aussi détaillé que les autres solutions.

Voici la solution pure 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 suggère beaucoup de doubles cotations, mais pour les jours dépassant l'année 9999, vous devriez envisager une approche différente. Pour le passé, il échouera silencieusement pour les dates antérieures à 1970.01.01. La désinfection de la saisie utilisateur est laissée à l’exercice.

Les deux fonctions peuvent être refactorisées en une seule, mais cela peut rendre la compréhension plus difficile.

Veuillez noter que le script nécessite des tests exhaustifs pour gérer correctement les années bissextiles dans le passé. Je ne parierais pas que c'est juste.

0
user unknown