2017-07-05 25 views
7

Tôi đang cố gắng thực hiện một số toán học ngàytimedelta và vấp phải điều này.Hành vi đáng ngạc nhiên của ngày Python và phép trừ timedelta

>>> import datetime 
>>> dt = datetime.date(2000, 4, 20) 
>>> td = datetime.timedelta(days=1) 
>>> dt - td 
datetime.date(2000, 4, 19) 
>>> -(td) + dt 
datetime.date(2000, 4, 19) 
>>> dt - td == dt + (-td) 
True 

Cho đến nay rất tốt, nhưng khi timedelta cũng bao gồm một số giờ nó trở nên thú vị.

>>> td = datetime.timedelta(days=1, hours=1) 
>>> dt - td 
datetime.date(2000, 4, 19) 
>>> -(td) + dt 
datetime.date(2000, 4, 18) 

hoặc trong một so sánh:

>>> dt - td == dt + (-td) 
False 

tôi lại có thể ngờ rằng a - b == a + (-b), nhưng điều này dường như không làm việc cho ngàytimedelta. Theo như tôi đã có thể theo dõi điều đó, điều này xảy ra vì việc cộng/trừ ngàytimedelta chỉ xem trường ngày là timedelta, điều này có thể đúng. Tuy nhiên, việc phủ nhận timedelta sẽ xem xét tất cả các trường và cũng có thể thay đổi trường ngày.

>>> -datetime.timedelta(days=1) 
datetime.timedelta(-1) 
>>> -datetime.timedelta(days=1, hours=1) 
datetime.timedelta(-2, 82800) 

Như có thể thấy trong ví dụ thứ hai, ngày = -2 sau khi phủ định, và do đó ngày + timedelta sẽ thực sự trừ 2 ngày.

Điều này có nên được coi là lỗi trong mô-đun python datetime không? Hay liệu đây có phải là hành vi 'bình thường' mà cần được tính đến khi làm những việc như thế không?

Bên trong datetime mô-đun tạo ra một mới timedelta, chỉ với những ngày lĩnh vực bản gốc timedelta đối tượng thông qua tại, khi đã trừ một timedelta đến một ngày đối tượng. Mà tương đương với sau, mã mà có vẻ là khá kỳ quặc.

>>> dt + datetime.timedelta(-(-(-dt).days)) 
datetime.date(2000, 4, 18) 

tôi có thể không thực sự biển một lý do để chỉ sử dụng các lĩnh vực ngày phủ nhận khi làm ngày - timedelta bớt.

Edit:

Đây là con đường mã có liên quan trong python datetime mô-đun:

class date: 

    ... 

    def __sub__(self, other): 
     """Subtract two dates, or a date and a timedelta.""" 
     if isinstance(other, timedelta): 
      return self + timedelta(-other.days) 
     ... 

Nếu nó sẽ chỉ cần vượt qua trên -Các sau đó điều kiện a - b = = a + (-b) sẽ đúng. (Nó sẽ thay đổi hành vi hiện tại mặc dù).

class date: 

    ... 

    def __sub__(self, other): 
     """Subtract two dates, or a date and a timedelta.""" 
     if isinstance(other, timedelta): 
      return self - other # timedelta.__rsub__ would take care of negating other 
     ... 
+1

Tôi nghĩ rằng nó có giá trị hỏi ý nghĩa của việc thêm/bớt _hours_ đến một 'date'. Nếu giờ là 24, thì bạn sẽ dễ dàng quyết định rằng bạn phải thêm 1 ngày. Nếu chưa đầy 24 giờ, bạn có nâng cấp kết quả thành 'datetime' khi bạn bắt đầu với' ngày' hay bạn tiếp tục làm việc với 'date' và cắt ngắn? – mgilson

+0

Vâng, đó là một câu hỏi hay và câu hỏi không thể trả lời dễ dàng. Nhưng điều tôi muốn nhấn mạnh là, hoạt động toán học bình thường như * a - b == a + (-b) * không hoạt động như mong đợi. – gweis

+0

Có, đã khắc phục điều đó. cảm ơn. – gweis

Trả lời

2

Điều này có nên được coi là lỗi trong mô-đun ngày giờ trăn không?Hoặc là hành vi thay vì 'bình thường' này cần được đưa vào tài khoản khi thực hiện những việc như vậy?

Không, đây không phải là lỗi. A date không theo dõi trạng thái của nó theo giờ, phút và giây, đó là điều cần thiết để nó hoạt động theo cách bạn đề xuất.

Tôi sẽ xem xét mã bạn đã trình bày là lỗi: lập trình viên đang sử dụng sai datatype cho công việc mà họ đang cố gắng thực hiện. Nếu bạn muốn theo dõi thời gian theo ngày, giờ, phút và giây, thì bạn cần một đối tượng datetime. (Mà hạnh phúc sẽ cung cấp cho bạn một date khi bạn đã thực hiện tất cả các số học bạn quan tâm để làm)

+0

Đồng ý. Tuy nhiên, sử dụng datetime sẽ là một lựa chọn khó. Bạn nên chọn thời gian nào trong ngày? (Có rất nhiều cuộc thảo luận về điều này ở đây trên Stack Overflow). Có lẽ các hoạt động date/timedelta nên ném một lỗi nếu bất cứ điều gì, nhưng ngày (trong timedalta) là số không? Đó là sử dụng một loại sai sau khi tất cả. – gweis

+0

Tôi đồng ý rằng nó khó, nhưng khó theo nghĩa "bạn phải tìm hiểu ý nghĩa của bạn", không khó theo nghĩa "nó hoạt động không chính xác". Nếu bạn đang trừ đi thời gian từ ngày, bạn thực sự cần phải cho tôi biết thời gian bạn bắt đầu từ ngày đó. –

0

này là do cách làm thế nào tiêu cựctimedelta s được biểu diễn.

import datetime 
td = datetime.timedelta(days=1, hours=1) 
print (td.days, td.seconds) 
# prints 1 3600 
minus_td = -td 
print (minus_td.days, minus_td.seconds) 
# prints -2 82800 

Tôi hy vọng bạn hiểu rõ hơn lý do tại sao days bị ảnh hưởng.

Giây trong một timedelta luôn bình thường đến một số lượng tích cực giữa 0 và 86.399:

>>> print (datetime.timedelta(seconds=-10).seconds) 
86390 
+0

Có lẽ tôi đang thiếu một cái gì đó, nhưng vấn đề dường như không phải là về việc liệu 'timedelta' là tiêu cực hay tích cực. Lưu ý rằng khi minuend là 'datetime' thay vì' date', số học 'timedelta' hoạt động như mong đợi với cả giá trị dương và âm, và hành vi bất ngờ được quan sát thấy khi' timedelta 's giá trị dương và âm được thêm vào hoặc trừ khỏi 'ngày'. –

+0

Câu trả lời này cho biết thêm một số rõ ràng lý do tại sao nó xảy ra, nhưng nó cũng cho thấy rằng việc thực hiện datetime.timedelta là nguyên nhân cho hành vi không lường trước được này. – gweis

+0

@JonKiparsky: như bạn đã đề cập đúng, một loại không khớp ngữ nghĩa, cùng với việc gõ vịt và (khả năng) các tính toán tương thích ngược dẫn đến 'đối tượng' ngày chấp nhận 'timedelta' với thời gian khác 0 và kết quả không mong muốn. Theo cùng một mã thông báo, '1 <'1'' trong Python 2. Python 3 bằng cách nào đó làm giảm bớt điều này, và' 1 <' 1'' trở thành một 'TypeError'; hành vi 'date' và' timedelta' vẫn như cũ. – 9000

Các vấn đề liên quan