Komut dosyası veya işlev şu andan itibaren belirli bir tarihe kadar kaç gün döndürmek için


28

Gelecekte belirli bir tarihe kadar günlerden kaç gün sonra bana bir komut dosyası veya işlev yazmak istiyorum. Çalışmak için uğraştığım şey, verilen tarihi nasıl işleme koyacağımı ve şu anki tarihle nasıl karşılaştıracağım.

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

Ve sonra kabuğum için anlamsız bir ipe sahip olduğumu ve ... gibi bazı değerlendirmeleri yapmam gerektiğini farz ediyorum. (Bu sadece bir örnek; Sanırım bcgerekli olacak)

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

Ve sonra bu rakamlarla ne yapacağımı bilmiyorum… ??

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

Kesinlikle kendim için çok zor yapıyorum; Muhtemelen tarihleri ​​anlayan ve bunları karşılaştırabilen bir metin işleme aracı var.

Bunu nasıl yapabilirim?


Muhtemelen piton yok mu? Neyse, zamanı
dönme

@JacobVlijm python çözümü tamamen memnuniyetle karşılıyor - bu sonunda nihayet python XD'yi öğrenmeye başladığımda bana yardımcı olacak ve ben de çalışmasını istiyorum :)
Zanna

AHA, bir an ...
Jacob Vlijm

Yanıtlar:


29

Devir zaman

Genel olarak, zamanı (Unix) çağ zamanına (1-1-1970 arası saniye ) dönüştürürsek, zaman hesaplamaları en kolay olanıdır. Python'da, zamanı çağın zamanına ve tekrar istediğimiz herhangi bir tarih biçimine dönüştürecek araçlara sahibiz.

Basitçe bir format ayarlayabiliriz:

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

... ve bugün tanımlayın:

today = "2016-12-07"

ve daha sonra işi yapmak için bir işlev yazın:

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

Sonra çıktısı:

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

> 1481065200

... ki, bahsedildiği gibi, 1-1-1970’den bu yana geçen saniye sayısı

İki tarih arasındaki günleri hesaplama

Bunu hem bugün hem de gelecekteki tarihimiz için yaparsak, daha sonra farkı hesaplayın:

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

Formatı kullandığımızdan dolayı çıktı tarihe göre hesaplanacaktır %Y-%m-%d. Saniyede yuvarlama, örneğin 24 saat civarında olursak yanlış tarih farkı verebilir.

Terminal versiyonu

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

görüntü tanımını buraya girin

... Ve Zenity seçeneği

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

görüntü tanımını buraya girin

görüntü tanımını buraya girin

Ve sadece eğlence için ...

Küçük bir uygulama. Sık kullanıyorsanız kısayola ekleyin.

görüntü tanımını buraya girin

Senaryo:

#!/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()
  • Boş bir dosyaya kopyalayın, olarak kaydedin orangedays.py
  • Çalıştır:

    python3 /path/to/orangedays.py

Sarmak için

Aşağıdaki .desktopdosyanın üstündeki küçük uygulama betiği için kullanın :

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

görüntü tanımını buraya girin

  • Kodu boş bir dosyaya kopyalayın, olduğu gibi kaydedin orangedays.desktop.~/.local/share/applications
  • Çizgide

    Exec=/path/to/orangedays.py

    komut dosyasının asıl yolunu ayarla ...


23

GNU dateyarar bir şey bu tür oldukça iyidir. Çok çeşitli tarih formatlarını ayrıştırıp daha sonra başka bir formatta çıktılar. Burada %sçağdan bu yana geçen saniye sayısını göstermek için kullanıyoruz. Daha sonra çıkarmak için aritmetik basit bir konudur $nowden $future86400 saniye / gündüz ve böl:

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

Yanlış yuvarlama dışında (görünüyor), bu iyi çalışıyor! GNU tarihinin güçlerinden şüphe duyduğum için kendimi aptal hissediyorum :) Teşekkürler :)
Zanna

1
@Zanna - Sanırım yuvarlama sorununa çözüm, farkı almadan önce her iki zaman damgasını da 86400'e kadar tam olarak bölmek olduğunu düşünüyorum. Ama burada özlediğim bazı detaylar olabilir. Ayrıca girilen tarihin yerel saat mi yoksa UTC mi olmasını istersiniz? Eğer UTC ise, -uparametreyi şuna ekleyin date.
Dijital Travma

Normal süre ile gün ışığından yararlanma saati arasında geçiş yapan günler +/- 1 saat arasında değişebilir ve nadiren belirli günlerde yapılan düzeltme saniyeleri vardır. Ancak pratikte, bu çoğu durumda önemsiz olabilir.
kullanıcı bilinmeyen,

10

İşlevi awkkullanarak bir şey yapmayı deneyebilirsinizmktime

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

Awk, "YYYY AA GG GG AA SS SS" biçimindeki standart girdiden tarih okumayı bekler ve ardından belirtilen süre ile gün içindeki geçerli saat arasındaki farkı yazdırır.

mktimebir zamanı (belirtilen formatta) bir referans zamandan (1970-01-01 00:00:00 UTC) saniye sayısına dönüştürür; systime simple, geçerli saati aynı biçimde belirtir. Birini diğerinden çıkarın ve saniyeler içinde ne kadar uzakta olduklarını anlayın. Günlere dönüştürmek için 86400'e (24 * 60 * 60) bölün.


1
Güzel, Ancak bir sorun var: Sanırım gün sayısının bir şamandıra olmasını istemiyorsunuz, o zaman 86400'e
bölünmek

not Awk zaman işlevleri POSIX değil
Steven Penny

10

İşte bir Ruby versiyonu

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!"

Örnek Çalıştırma:

Komut dizisinin örnek çalışması ruby ./day-difference.rbaşağıda verilmiştir (olarak kaydettiğiniz varsayılarak day-difference.rb)

Gelecekteki bir tarih ile

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

Geçen tarih ile

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

Bugünün tarihi geçtiğinde

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

Http://www.timeanddate.com/date/duration.html tarihinin farklılıklarını kontrol etmek için güzel bir web sitesi.


Müthiş! Çok basit ve net. Ruby harika bir dil gibi görünüyor :)
Zanna

Güzel güzel!
Ruby'ye

1
@Zanna teşekkürler. Gerçekten öyle. 15 nane şekeri varsa burdan . :)
Anwar

@JacobVlijm Teşvik için teşekkür ederiz. Yine de öğrenciysem :)
Anwar

6

dateutilsTarihlerle uğraşmak için çok uygun bir paket var. Bu konuda daha fazla bilgi burada github: dateutils

Tarafından yükleyin

sudo apt install dateutils

Sorununuz için, basitçe

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

Çıkışın saniye, dakika, saat, gün, hafta, ay veya yıl olarak seçilebildiği Çıktının diğer görevler için kullanılabileceği betiklerde rahatlıkla kullanılabilir.


Örneğin,

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

Mükemmel :) Bu paket hakkında bilmek güzel.
Zanna

2

Awk Velor kütüphanesini kullanabilirsiniz :

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

Veya:

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

0

Her iki tarih de aynı yıla aitse, kısa bir çözüm:

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

gün içindeki tarihin konumunu döndüren "% j" biçimini kullanarak, örneğin geçerli tarih için 135. Yuvarlama sorunlarını önler ve geçmişte tarihleri ​​ele alarak olumsuz sonuçlar verir.

Bununla birlikte, sınırları aşmak bu başarısız olacaktır. Şubat ayının sonları geçerse, her yıl için el ile 365 veya her bir artık yıl için 366 ekleyebilir (veya çıkarabilirsiniz), ancak bu, neredeyse diğer çözümler kadar ayrıntılı olacaktır.

İşte saf bash çözümü:

#!/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 birçok çift alıntı önerir, ancak 9999 yılını geçen günler için farklı bir yaklaşım düşünmelisiniz. Geçmiş için, 1970.01.01 tarihinden önceki tarihler için sessizce başarısız olacaktır. Dezenfekte edici kullanıcı girişi, kullanıcıya bir egzersiz olarak bırakılmıştır.

Bu iki işlev bir tanesine yeniden yansıtılabilir, ancak bu anlaşılmasını zorlaştırabilir.

Lütfen, betiğin geçmişte artık yılların doğru şekilde atılması için kapsamlı testlere ihtiyacı olduğunu unutmayın. Bunun doğru olduğuna iddiaya girmedim.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.