2012-10-25 4 views
15

Hãy tưởng tượng datetime nàyPython - Ceil một datetime để quý tiếp theo của một giờ

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

Tôi muốn Ceil nó vào quý tiếp theo của giờ, để có được

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

tôi tưởng tượng cái gì đó như

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

Nhưng tất nhiên điều này không làm việc

Trả lời

16

Điều này mất một phần triệu giây!

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) 

Giải thích về delta:

  • 900 giây là 15 phút (một phần tư của một giờ sans nhảy giây mà tôi không nghĩ rằng xử lý datetime ...)
  • nsecs/900 là số lượng phần tư giờ đã trôi qua. Lấy số ceil của vòng này lên số lượng phần tư giờ.
  • Nhân số khối phần tư giờ với 900 để tìm ra số giây đã xuất phát kể từ đầu giờ sau khi "làm tròn".
+0

Làm việc tốt. Dường như đơn giản nhất. Cảm ơn –

+0

Tôi đã thử nó với 3600 thay vì 900 và nó không hoạt động. Nó thêm 3600 ngay cả khi thời gian đã được làm tròn. – Anuj

+1

Tôi nghĩ rằng tôi nên sử dụng '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 

này mang đến cho bạn:

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

Đây là một sôi nổi chút vì bạn hoạt động trên 'dt' tại chỗ, nhưng sau đó bạn gửi lại. từ một quan điểm API, Có vẻ như bạn nên hoặc là làm một hoặc khác ... (Ngoài ra, điều này không mất 'micro giâys' vào tài khoản) – mgilson

+0

@ mgilson: Cảm ơn bạn đã bình luận, bạn phải tất nhiên. Tôi đã bỏ qua micro giây vì OP đã xây dựng các datetimes của mình mà không có chúng. Nhưng tất nhiên đó có thể là một vấn đề nếu đó không phải là trường hợp sử dụng trong thế giới thực của anh ta. –

+0

Vâng. Tôi rất hài lòng với nó bây giờ mà bạn đã sửa các công cụ API (+1) – mgilson

2

Bạn chỉ cần tính toán phút chính xác và thêm chúng vào đối tượng datetime phút thứ thiết, giây để zero

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

Nó hoạt động cho tất cả các trường hợp:

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

Đây là một bản sao của câu trả lời của Pierre GM, ngoại trừ bạn sẽ chỉ làm việc như mong đợi trong Python 2.x do việc sử dụng ''/'' over '' // ''. –

+0

@Lattyware Tôi nghĩ rằng chúng tôi đã đăng nó cùng một lúc + bây giờ nó không giống như tôi đã sửa lỗi, và giải pháp của anh ấy có lỗi –

+0

Tuyệt vời, mặc dù tôi muốn lưu ý việc sử dụng ''/'' over '' // '' vẫn còn phụ tối ưu. Ngoài ra, SO thông báo cho bạn khi người khác đăng câu trả lời, nó đáng xem trước khi bạn đăng bài của bạn để kiểm tra xem bạn có đang nhân bản câu trả lời hay không. –

4

@Mark Dickinson suggested công thức tốt nhất cho đến nay:

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

Trong Python 3, cho một đồng bằng thời gian tùy ý (không chỉ là 15 phút):

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

Để tránh nổi trung gian , divmod() có thể được sử dụng:

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

Ví dụ:

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

Nếu bạn đang hạn chế đối với Python 3, thì biểu thức đơn giản 'dt + (datetime.min - dt)% delta' cũng hoạt động. –

+0

@MarkDickinson: vâng. Nó hoạt động trên CPython (cho tất cả các ví dụ trong câu trả lời). Bạn nên đăng: 'def ceil_dt (dt, delta): trả về dt + (datetime.min - dt)% delta' làm câu trả lời. – jfs