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?
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
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.
#!/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))
#!/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))
])
Une petite application. Ajoutez-le à un raccourci si vous l'utilisez souvent.
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()
orangedays.py
Exécuter:
python3 /path/to/orangedays.py
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
orangedays.desktop
dans ~/.local/share/applications
Dans la ligne
Exec=/path/to/orangedays.py
définir le chemin d'accès au script ...
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"
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.
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!"
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
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
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
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.