2012-10-25 11 views
15

Lassen Sie uns diese Datumzeit vorstellenPython - Ceil ein Datetime zum nächsten Viertelstunde

>>> import datetime 
>>> dt = datetime.datetime(2012, 10, 25, 17, 32, 16) 

Ich mag würde es auf die nächste Viertelstunde ceil, um

datetime.datetime(2012, 10, 25, 17, 45) 

zu erhalten ich stelle mir so etwas wie

>>> quarter = datetime.timedelta(minutes=15) 
>>> import math 
>>> ceiled_dt = math.ceil(dt/quarter) * quarter 

Aber natürlich nicht

funktioniert

Antwort

16

Dies berücksichtigt Mikrosekunden!

import math 

def ceil_dt(dt): 
    # how many secs have passed this hour 
    nsecs = dt.minute*60 + dt.second + dt.microsecond*1e-6 
    # number of seconds to next quarter hour mark 
    # Non-analytic (brute force is fun) way: 
    # delta = next(x for x in xrange(0,3601,900) if x>=nsecs) - nsecs 
    # analytic way: 
    delta = math.ceil(nsecs/900) * 900 - nsecs 
    #time + number of seconds to quarter hour mark. 
    return dt + datetime.timedelta(seconds=delta) 

t1 = datetime.datetime(2017, 3, 6, 7, 0) 
assert ceil_dt(t1) == t1 

t2 = datetime.datetime(2017, 3, 6, 7, 1) 
assert ceil_dt(t2) == datetime.datetime(2017, 3, 6, 7, 15) 

t3 = datetime.datetime(2017, 3, 6, 7, 15) 
assert ceil_dt(t3) == t3 

t4 = datetime.datetime(2017, 3, 6, 7, 16) 
assert ceil_dt(t4) == datetime.datetime(2017, 3, 6, 7, 30) 

t5 = datetime.datetime(2017, 3, 6, 7, 30) 
assert ceil_dt(t5) == t5 

t6 = datetime.datetime(2017, 3, 6, 7, 31) 
assert ceil_dt(t6) == datetime.datetime(2017, 3, 6, 7, 45) 

t7 = datetime.datetime(2017, 3, 6, 7, 45) 
assert ceil_dt(t7) == t7 

t8 = datetime.datetime(2017, 3, 6, 7, 46) 
assert ceil_dt(t8) == datetime.datetime(2017, 3, 6, 8, 0) 

Erklärung der delta:

  • 900 Sekunden beträgt 15 Minuten (eine Viertelstunde sans Sekunden springen, die ich Datetime-Griffe nicht denken ...)
  • nsecs/900 ist die Anzahl der Viertelstundenbrocken, die verstrichen sind. Die ceil davon zu nehmen, rundet die Anzahl der Viertelstundenbrocken auf.
  • Multiplizieren Sie die Anzahl der Viertelstundenbrocken mit 900, um herauszufinden, wie viele Sekunden seit Beginn der Stunde nach "Runden" verstrichen sind.
+0

Funktioniert gut. Scheint der einfachste zu sein. Danke –

+0

Ich versuchte es mit 3600 anstelle von 900 und es funktioniert nicht. Es fügt 3600 hinzu, auch wenn die Zeit bereits gerundet ist. – Anuj

+1

Ich denke, ich hätte 'delta = math.ceil (nsecs/900.) * 900.' – mgilson

7
def ceil(dt): 
    if dt.minute % 15 or dt.second: 
     return dt + datetime.timedelta(minutes = 15 - dt.minute % 15, 
             seconds = -(dt.second % 60)) 
    else: 
     return dt 

Dies gibt Ihnen:

>>> ceil(datetime.datetime(2012,10,25, 17,45)) 
datetime.datetime(2012, 10, 25, 17, 45) 
>>> ceil(datetime.datetime(2012,10,25, 17,45,1)) 
datetime.datetime(2012, 10, 25, 18, 0) 
>>> ceil(datetime.datetime(2012,12,31,23,59,0)) 
datetime.datetime(2013,1,1,0,0) 
+3

Dies ist ein wenig flippig, weil Sie auf 'dt' an Ort und Stelle arbeiten, aber Sie es dann zurück. Aus einer API-Perspektive scheint es, als ob Sie entweder das eine oder das andere tun sollten ... (Auch das berücksichtigt keine Mikrosekunden) – mgilson

+0

@mgilson: Danke für den Kommentar, du hast natürlich Recht. Ich habe Mikrosekunden ignoriert, weil der OP seine Datumzeiten ohne sie konstruierte. Aber natürlich ist das ein Problem, wenn das nicht sein Anwendungsfall ist. –

+0

Ja. Ich bin jetzt glücklich damit, dass du das API-Zeug repariert hast (+1) – mgilson

2

Sie müssen nur richtig Minuten berechnen und sie in Datetime-Objekt hinzufügen, nach der Einstellung Minuten, Sekunden auf Null

import datetime 

def quarter_datetime(dt): 
    minute = (dt.minute//15+1)*15 
    return dt.replace(minute=0, second=0)+datetime.timedelta(minutes=minute) 

for minute in [12, 22, 35, 52]: 
    print quarter_datetime(datetime.datetime(2012, 10, 25, 17, minute, 16)) 

Es für alle Fälle funktioniert:

2012-10-25 17:15:00 
2012-10-25 17:30:00 
2012-10-25 17:45:00 
2012-10-25 18:00:00 
+0

Dies ist eine duplizierte Antwort von Pierre GM, außer dass deine nur wie erwartet in Python 2.x funktionieren wird aufgrund der Verwendung von "/" über "//". –

+0

@Lattyware Ich denke, wir haben es zur gleichen Zeit gepostet + jetzt ist es nicht das gleiche wie ich einen Fehler behoben habe, und seine Lösung hat defekt –

+0

Großartig, obwohl ich die Verwendung von '' '' '' '' // '' ist immer noch suboptimal. Außerdem benachrichtigt SO Sie, wenn jemand anders eine Antwort posten kann. Es lohnt sich, sich vor der Veröffentlichung zu erkundigen, ob Sie nicht eine Antwort duplizieren. –

4

@Mark Dickinson suggested die beste Formel so weit:

def ceil_dt(dt, delta): 
    return dt + (datetime.min - dt) % delta 

In Python 3 für eine beliebige Zeitdelta (nicht nur 15 Minuten):

#!/usr/bin/env python3 
import math 
from datetime import datetime, timedelta 

def ceil_dt(dt, delta): 
    return datetime.min + math.ceil((dt - datetime.min)/delta) * delta 

print(ceil_dt(datetime(2012, 10, 25, 17, 32, 16), timedelta(minutes=15))) 
# -> 2012-10-25 17:45:00 

Zwischen Schwimmer zu vermeiden divmod() könnte verwendet werden:

def ceil_dt(dt, delta): 
    q, r = divmod(dt - datetime.min, delta) 
    return (datetime.min + (q + 1)*delta) if r else dt 

Beispiel:

>>> ceil_dt(datetime(2012, 10, 25, 17, 32, 16), timedelta(minutes=15)) 
datetime.datetime(2012, 10, 25, 17, 45) 
>>> ceil_dt(datetime.min, datetime.resolution) 
datetime.datetime(1, 1, 1, 0, 0) 
>>> ceil_dt(datetime.min, 2*datetime.resolution) 
datetime.datetime(1, 1, 1, 0, 0) 
>>> ceil_dt(datetime.max, datetime.resolution) 
datetime.datetime(9999, 12, 31, 23, 59, 59, 999999) 
>>> ceil_dt(datetime.max, 2*datetime.resolution) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", line 3, in ceil_dt 
OverflowError: date value out of range 
>>> ceil_dt(datetime.min+datetime.resolution, datetime.resolution) 
datetime.datetime(1, 1, 1, 0, 0, 0, 1) 
>>> ceil_dt(datetime.min+datetime.resolution, 2*datetime.resolution) 
datetime.datetime(1, 1, 1, 0, 0, 0, 2) 
>>> ceil_dt(datetime.max-datetime.resolution, datetime.resolution) 
datetime.datetime(9999, 12, 31, 23, 59, 59, 999998) 
>>> ceil_dt(datetime.max-datetime.resolution, 2*datetime.resolution) 
datetime.datetime(9999, 12, 31, 23, 59, 59, 999998) 
>>> ceil_dt(datetime.max-2*datetime.resolution, datetime.resolution) 
datetime.datetime(9999, 12, 31, 23, 59, 59, 999997) 
>>> ceil_dt(datetime.max-2*datetime.resolution, 2*datetime.resolution) 
datetime.datetime(9999, 12, 31, 23, 59, 59, 999998) 
>>> ceil_dt(datetime.max-timedelta(1), datetime.resolution) 
datetime.datetime(9999, 12, 30, 23, 59, 59, 999999) 
>>> ceil_dt(datetime.max-timedelta(1), 2*datetime.resolution) 
datetime.datetime(9999, 12, 31, 0, 0) 
>>> ceil_dt(datetime.min, datetime.max-datetime.min) 
datetime.datetime(1, 1, 1, 0, 0) 
>>> ceil_dt(datetime.max, datetime.max-datetime.min) 
datetime.datetime(9999, 12, 31, 23, 59, 59, 999999) 
+3

Wenn Sie auf Python 3 beschränken, funktioniert auch der einfachere Ausdruck 'dt + (datetime.min - dt)% delta'. –

+0

@MarkDickinson: ja. Es funktioniert auf CPython (für alle Beispiele in der Antwort). Du solltest posten: 'def ceil_dt (dt, delta): gib dt + (datetime.min - dt)% delta' als Antwort zurück. – jfs

Verwandte Themen