Script ou função para retornar quantos dias a partir de agora até uma determinada data

29

Eu gostaria de escrever um script ou uma função para dizer quantos dias a partir de agora até uma determinada data no futuro. O que estou me esforçando para resolver é como processar a data indicada e compará-la com a data atual ... Estou imaginando algo como

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

E então eu estou supondo que eu tenho uma string que é sem sentido para o shell e eu tenho que fazer algumas avaliações como ?? (Este é apenas um exemplo; eu acho que bc seria necessário)

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

E então eu não sei o que fazer com esses números ... ??

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

Estou certamente tornando isso muito difícil para mim; provavelmente há uma ferramenta de processamento de texto que entende as datas e pode compará-las.

Como posso fazer isso?

    
por Zanna 07.12.2016 / 21:09

7 respostas

29

Tempo da época

Em geral, os cálculos no tempo são mais fáceis se primeiro convertermos o tempo em tempo (Unix) epoch (segundos de 1-1-1970). Em python, temos ferramentas para converter o tempo em tempo de época e de volta para qualquer formato de data que preferirmos.

Podemos simplesmente definir um formato como:

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

... e defina hoje:

today = "2016-12-07"

e, posteriormente, escrever uma função para fazer o trabalho:

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

Em seguida, a saída de:

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

> 1481065200

... que é, como mencionado, o número de segundos desde 1-1-1970

Calculando os dias entre duas datas

Se fizermos isso hoje e na data futura, calculemos a diferença:

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

A saída será calculada por data , já que usamos o formato %Y-%m-%d . O arredondamento em segundos possivelmente daria uma diferença de data incorreta, se estivermos próximos a 24 horas, por exemplo.

Versão do 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))

...EaopçãoZenity

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

Esópordiversão...

Umpequenoaplicativo.Adicione-oaumatalhosevocêousarcomfrequência.

Oscript:

#!/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()
  • Copie-o em um arquivo vazio, salve-o como orangedays.py
  • Execute:

    python3 /path/to/orangedays.py
    

Para finalizar

Use para o pequeno script de aplicativo acima do seguinte arquivo .desktop :

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

  • Copie o código em um arquivo vazio, salve-o como orangedays.desktop in ~/.local/share/applications
  • Na linha

    Exec=/path/to/orangedays.py
    

    defina o caminho real para o script ...

por Jacob Vlijm 07.12.2016 / 22:29
23

O Utilitário GNU date é muito bom nesse tipo de coisa. Ele é capaz de analisar uma boa variedade de formatos de data e, em seguida, imprimir em outro formato. Aqui usamos %s para gerar o número de segundos desde a época. É então uma simples questão de aritmética subtrair $now do $future e dividir por 86400 segundos / dia:

#!/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"
    
por Digital Trauma 08.12.2016 / 01:39
10

Aqui está uma versão em 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!"

Exemplo de execução:

Exemplo de execução do script ruby ./day-difference.rb é dado abaixo (supondo que você o salvou como day-difference.rb )

Com uma data futura

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

Com uma data passada

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

Quando passado a data de hoje

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

Aqui está um bom site para verificar as diferenças de data link

    
por Anwar 08.12.2016 / 16:17
10

Você pode tentar fazer algo em awk , usando a função mktime

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

O awk espera ler a data da entrada padrão no formato "AAAA MM DD HH MM SS" e imprime a diferença entre a hora especificada e a hora atual em dias.

mktime simplesmente converte um horário (no formato especificado) para o número de segundos de um tempo de referência (1970-01-01 00:00:00 UTC); systime simple especifica a hora atual no mesmo formato. Subtraia um do outro e você fica a distância em segundos. Divida por 86400 (24 * 60 * 60) para converter para dias.

    
por Nick Sillito 07.12.2016 / 21:51
6

Existe um pacote dateutils que é muito conveniente para lidar com datas. Leia mais sobre isso aqui github: dateutils

Instale por

sudo apt install dateutils

Para o seu problema, simplesmente,

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

onde a saída pode ser escolhida como segundos, minutos, horas, dias, semanas, meses ou anos. Pode ser convenientemente usado em scripts onde a saída pode ser usada para outras tarefas.

Por exemplo,

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
    
por ankit7540 26.12.2016 / 08:19
2

Você pode usar a biblioteca de Velud do awk:

$ 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
    
por Steven Penny 26.12.2016 / 07:45
0

Uma solução curta, se ambas as datas pertencerem ao mesmo ano, é:

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

usando o formato "% j", que retorna a posição da data em dias no ano, ou seja, 135 para a data atual. Evita problemas de arredondamento e lida com datas no passado, dando resultados negativos.

No entanto, ao cruzar as fronteiras do ano, isso falhará. Você pode adicionar (ou subtrair) 365 manualmente para cada ano ou 366 para cada ano bissexto, se o último de fevereiro for cruzado, mas isso será quase tão detalhado quanto outras soluções.

Aqui a solução pura do 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 sugere muitas citações duplas, mas para dias que excedem o ano 9999 você deve considerar uma abordagem diferente. No passado, falhará silenciosamente para datas anteriores a 1970.01.01. A limpeza da entrada do usuário é deixada como um exercício para o usuário.

As duas funções podem ser refatoradas em uma, mas isso pode dificultar a compreensão.

Por favor, note que o script precisa de testes exaustivos para lidar corretamente com anos bissextos no passado. Eu não apostaria que está certo.

    
por 15.05.2019 / 01:12