지금까지 주어진 날짜까지 며칠을 반환하는 스크립트 또는 함수


28

지금부터 앞으로 주어진 날짜까지 며칠을 알려주는 스크립트 나 함수를 작성하고 싶습니다. 내가 해결하기 위해 고군분투하는 것은 주어진 날짜를 처리하고 현재 날짜와 비교하는 방법입니다 ... 나는 뭔가를 상상하고 있습니다.

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

그리고 쉘에 무의미한 문자열이 있다고 가정하고 다음과 같은 몇 가지 평가를 수행해야합니다 ... ?? (이것은 단지 예일뿐입니다. bc)

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

그리고 나는 그 숫자로 무엇을 해야할지 모르겠다.

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

나는 그것을 너무 힘들게 만들고있다. 아마도 날짜를 이해하고 비교할 수있는 텍스트 처리 도구가있을 것입니다.

어떻게해야합니까?


아마도 파이썬이 없습니까? 어쨌든, 시대의 시간으로 변환 시간은, 그것은 :) 쉬운 (어떤 형식 일 수 있습니다)
야곱 Vlijm

@JacobVlijm 파이썬 솔루션은 완전히 환영 - 내가 마지막으로 파이썬 XD 학습에 주위에 도착하면 그 날 도움이 될 것입니다 난 그냥 너무 작업 할 :)
ZANNA

AHA, 순간 ...
Jacob Vlijm 21

답변:


29

에포크 시간

일반적으로 시간을 (Unix) 에포크 시간 (1-1-1970에서 초 )으로 변환하면 시간 계산이 가장 쉽습니다 . 파이썬에는 시간을 에포크 시간으로 변환하고 원하는 날짜 형식으로 되돌릴 수있는 도구가 있습니다.

다음과 같은 형식을 간단히 설정할 수 있습니다.

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

... 오늘 정의하십시오.

today = "2016-12-07"

그런 다음 작업을 수행하는 함수를 작성하십시오.

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

그런 다음 출력 :

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

> 1481065200

... 이것은 1-1-1970 이후의 초 수입니다.

두 날짜 사이의 날짜 계산

오늘과 미래의 날짜 에이 작업을 수행하면 차액을 계산하십시오.

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

출력은 형식을 사용하므로 날짜 별로 계산됩니다 %Y-%m-%d. 예를 들어 24 시간이 지나면 초를 반올림 하면 날짜 차이가 잘못 될 수 있습니다.

터미널 버전

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

여기에 이미지 설명을 입력하십시오

... 그리고 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))
         ])

여기에 이미지 설명을 입력하십시오

여기에 이미지 설명을 입력하십시오

그리고 그냥 재미를 위해 ...

작은 응용 프로그램. 자주 사용하는 경우 바로 가기에 추가하십시오.

여기에 이미지 설명을 입력하십시오

스크립트 :

#!/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
  • 그것을 실행 :

    python3 /path/to/orangedays.py

마무리

다음 .desktop파일 위의 작은 응용 프로그램 스크립트에 사용하십시오 .

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

여기에 이미지 설명을 입력하십시오

  • 빈 파일에 코드를 복사하여 다음과 같이 저장하십시오 orangedays.desktop.~/.local/share/applications
  • 라인에서

    Exec=/path/to/orangedays.py

    스크립트의 실제 경로를 설정하십시오 ...


23

GNU의 date유틸리티는 이런 종류의 꽤 좋다. 다양한 날짜 형식을 구문 분석 한 다음 다른 형식으로 출력 할 수 있습니다. 여기서 우리 %s는 에포크 이후 초 수를 출력하는 데 사용 합니다. 그런 다음 뺄 연산의 간단한 문제입니다 $now으로부터 $future8만6천4백초 / 일에 의해 분할 :

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

잘못된 반올림과는 별도로 (이것은 잘 보입니다)! 나는 GNU 날짜의 힘을 의심하는 것에 대해 바보 같은 느낌을 느낀다 :) : :
Zanna

1
@ 잔나-반올림 문제에 대한 해결책은 단순히 차이를 취하기 전에 두 타임 스탬프를 86400으로 정수 나누는 것입니다. 그러나 여기서 누락 된 세부 사항이있을 수 있습니다. 또한 입력 한 날짜를 현지 시간 또는 UTC로 하시겠습니까? UTC이면 -u매개 변수를에 추가하십시오 date.
디지털 외상

정상 시간과 일광 절약 시간 사이를 전환하는 요일은 +/- 1 시간 동안 다를 수 있으며, 특정 요일에는 수정 시간이 거의 없습니다. 그러나 실제로 이것은 대부분의 경우 중요하지 않을 수 있습니다.
사용자 알 수 없음

10

함수를 awk사용하여에서 무언가를 시도해 볼 수 mktime있습니다.

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

awk는 "YYYY MM DD HH MM SS"형식으로 표준 입력에서 날짜를 읽은 다음 지정된 시간과 현재 시간의 차이를 일 단위로 인쇄합니다.

mktime단순히 지정된 형식의 시간을 참조 시간 (1970-01-01 00:00:00 UTC)에서 초 수로 변환합니다. systime simple은 현재 시간을 동일한 형식으로 지정합니다. 하나를 다른 것에서 빼면 몇 초 안에 얼마나 떨어져 있는지 알 수 있습니다. 86400 (24 * 60 * 60)으로 나누어 일로 변환합니다.


1
니스, 그러나 한 가지 문제가 있습니다. 유일한 일 수를 부동 소수점으로 원하지 않으면 간단히 86400으로 나누면 작동하지 않습니다 .24 시간이
지나면

note Awk 시간 함수는 POSIX가 아닙니다
Steven Penny

10

여기에 루비 버전이 있습니다

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

실행 예 :

스크립트 실행 예 ruby ./day-difference.rb는 다음과 같습니다 (로 저장했다고 가정 day-difference.rb).

미래의 날짜로

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

날짜가 지났습니다

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

오늘 날짜가 지났을 때

$ 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


대박! 매우 간단하고 명확합니다. Ruby는 훌륭한 언어 인 것 같습니다 :)
Zanna

좋아 좋아! Ruby에 오신 것을 환영합니다 :)
Jacob Vlijm

1
@ 잔나 감사합니다. 정말입니다. 15 분의 민트가 있다면 여기에 루비를 시도 하십시오. :)
Anwar

@JacobVlijm 격려해 주셔서 감사합니다. 나는 여전히 학생이지만 :)
Anwar

6

dateutils날짜를 처리하는 매우 편리합니다 패키지. 여기에 대해 더 자세히 읽으십시오 github : dateutils

에 의해 설치

sudo apt install dateutils

간단히 말해서,

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

여기서 출력은 초, 분,시, 일, 주, 월 또는 년으로 선택할 수 있습니다. 다른 작업에 출력을 사용할 수있는 스크립트에서 편리하게 사용할 수 있습니다.


예를 들어

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

우수 :)이 패키지에 대해 잘 알고 있습니다.
잔나

2

awk Velor 라이브러리를 사용할 수 있습니다 .

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

또는:

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

0

두 날짜가 같은 해에 속하면 간단한 해결책은 다음과 같습니다.

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

"% j"형식을 사용하여 연도를 기준으로 날짜 위치를 반환합니다 (예 : 현재 날짜의 경우 135). 반올림 문제를 피하고 과거의 날짜를 처리하여 부정적인 결과를 낳습니다.

그러나 연도 경계를 넘어 서면 실패합니다. 2 월 마지막이 지난 경우 매년 수동으로 365를 추가하거나 빼기 연도별로 366을 추가 (또는 빼기) 할 수 있지만 다른 솔루션과 거의 비슷합니다.

순수한 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은 많은 따옴표를 제안하지만 9999 년을 초과하는 날에는 다른 접근법을 고려해야합니다. 과거에는 1970.01.01 이전의 날짜에 대해서는 자동으로 실패합니다. 사용자 입력 삭제는 사용자에게 연습으로 남습니다.

두 기능을 하나로 리팩토링 할 수 있지만 이해하기 어려울 수 있습니다.

스크립트는 과거의 윤년을 올바르게 처리하기 위해 철저한 테스트가 필요합니다. 나는 그것이 옳다는 것을 내기하지 않을 것입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.