Làm thế nào chúng ta có thể phát triển các phương pháp mã hóa được thiết kế để bảo vệ khỏi các lỗi của năm nhuận? [đóng cửa]


80

Microsoft vừa thông báo rằng một lỗi phần mềm trong việc tính toán ngày tháng (theo năm nhuận) đã gây ra sự cố ngừng hoạt động lớn trong Windows Azure vào tuần trước.

Đó có thực sự là một lỗi đơn giản trong việc phán đoán xảy ra DateTime.Now.AddYears(1)vào một năm nhuận không?

Những thực hành mã hóa nào có thể đã ngăn chặn điều này?

CHỈNH SỬA Như dcstraw đã chỉ ra DateTime.Now.AddYears(1)vào một năm nhuận trên thực tế trả về ngày chính xác trong .NET. Vì vậy, nó không phải là một lỗi khung, mà rõ ràng là một lỗi trong tính toán Ngày.


7
Vì không có câu trả lời 'một' và đây là một câu hỏi thảo luận nhiều hơn, tôi nghĩ nó nên được chuyển sang các lập trình viên. Ngoài ra: Luôn kiểm tra đơn vị mã của bạn. Luôn luôn.
George Stocker

2
Các vấn đề về DateTime là một vấn đề phức tạp và phức tạp. Người ta có thể nói điều gì đó như làm mọi thứ trong UTC nhưng sẽ luôn có một số điểm dịch ... và khi điều đó xảy ra, số lượng hoán vị bạn sẽ phải tính để tránh tất cả các lỗi đang gây ra. Hầu hết chúng ta sẽ không bao giờ làm việc trên một hệ thống phải quan tâm.
Josh

9
Tôi nghĩ câu hỏi này quá thú vị để được đóng lại :-)
Steven

11
BỐN phiếu bầu để đóng? Tôi hy vọng điều này khiến điều này kết thúc vui vẻ, nóng nảy chỉ là một giai đoạn trong quá trình phát triển SO. Tất cả chỉ là một chút quá đáng-hơn-bạn.
Iain Holder vào

7
@IainMH đặc biệt phải đồng ý với bạn vì gần như tất cả những người đã đóng nó chưa bao giờ đặt câu hỏi với nhiều hơn một vài phiếu bầu.
Lloyd

Câu trả lời:


95

Không biết xấu hổ cắm:

Sử dụng API ngày và giờ tốt hơn

Thư viện ngày và giờ .NET tích hợp sẵn rất khó sử dụng đúng cách. Họ làm cho phép bạn làm tất cả mọi thứ bạn cần, nhưng bạn không thể hiện rõ bản thân mình thông qua hệ thống loại. DateTimelà một mớ hỗn độn , DateTimeOffsetcó thể ru bạn nghĩ rằng bạn đang thực sự lưu giữ thông tin múi giờ khi bạn không có, và TimeZoneInfokhông buộc bạn phải suy nghĩ về mọi thứ bạn nên xem xét.

Không có cách nào trong số này cung cấp cách nói hay "chỉ một thời gian trong ngày" hoặc "chỉ một ngày", cũng như không phân biệt rõ ràng giữa "giờ địa phương" và "thời gian trong một múi giờ cụ thể". Và nếu bạn muốn sử dụng lịch khác với lịch Gregorian, bạn cần phải xem qua cả Calendarlớp.

Tất cả những điều này là lý do tại sao tôi đang xây dựng Noda Time - một thư viện ngày và giờ thay thế được xây dựng trên một cổng của "engine" Joda Time nhưng với một API mới (và gọn gàng hơn) ở trên cùng.

Một số điểm bạn có thể muốn suy nghĩ và dễ bỏ qua nếu bạn không biết về chúng:

  • Ánh xạ một ngày / giờ địa phương đến một trong một múi giờ cụ thể không đơn giản như bạn nghĩ. Một ngày / giờ địa phương cụ thể có thể xảy ra một lần, hai lần (không rõ ràng) hoặc 0 lần (bị bỏ qua) do chuyển đổi tiết kiệm ánh sáng ban ngày
  • Múi giờ thay đổi theo lịch sử - nhiều hơn những gì TimeZoneInfonói chung sẵn sàng tiết lộ, một cách thẳng thắn. (Nó không hỗ trợ múi giờ có ý tưởng về "giờ chuẩn" thay đổi theo thời gian hoặc chuyển thành giờ tiết kiệm ánh sáng ban ngày vĩnh viễn.)
  • Ngay cả với cơ sở dữ liệu múi giờ, ID múi giờ không nhất thiết phải ổn định. (CLDR giải quyết vấn đề này; cuối cùng tôi hy vọng sẽ hỗ trợ được điều gì đó trong Noda Time.)
  • Biểu thị ngày và giờ bằng văn bản là một cơn ác mộng, không chỉ về thứ tự, mà còn là dấu phân cách ngày, dấu phân cách thời gian và những thứ kỳ quặc như tên tháng thiên tài
  • Thời gian bắt đầu một ngày không phải lúc nào cũng là nửa đêm - ví dụ: ở Brazil, quá trình chuyển đổi tiết kiệm ánh sáng ban ngày vào mùa xuân di chuyển đồng hồ treo tường từ 11:59:59 tối sang 1 giờ sáng
  • Trong một số trường hợp (tốt, một điều mà tôi biết), múi giờ có thể buộc bỏ qua cả ngày - ngày 30 tháng 12 năm 2011 không xảy ra ở Samoa! Tôi nghi ngờ rằng hầu hết các nhà phát triển có thể bỏ qua điều này, nhưng ...
  • Nếu bạn định sử dụng lịch khác với lịch Gregorian, hãy cẩn thận và đảm bảo rằng bạn thực sự biết cách bạn mong đợi lịch hoạt động.

Theo thực tiễn phát triển cụ thể:

  • Nghĩ về những gì bạn đang thực sự cố gắng thể hiện. Tôi hy vọng lợi ích cốt lõi của Noda Time là buộc các nhà phát triển phải lựa chọn giữa nhiều loại khác nhau để đại diện cho dữ liệu của họ. Làm đúng và mọi thứ khác đơn giản hơn.
  • Unit test mọi thứ bạn có thể nghĩ ra. Tất nhiên, điều đó sẽ phụ thuộc vào chính xác những gì hệ thống của bạn thực hiện, nhưng hãy đặc biệt xem xét các múi giờ khác nhau, những gì xảy ra trong quá trình chuyển đổi tiết kiệm ánh sáng ban ngày và tất nhiên là các năm nhuận.
  • Tôi khuyên bạn nên tiêm một "giao diện giống như đồng hồ" - một dịch vụ để cho biết thời gian hiện tại - thay vì gọi DateTime.Nowhoặc DateTime.UtcNow; nó giúp dễ dàng hơn (khả thi!) để kiểm tra đơn vị
  • Nếu bạn đang thực hiện nhiều thao tác với "bây giờ", hãy lấy ngày / giờ đó một lần và ghi nhớ nó, thay vì liên tục yêu cầu "bây giờ" - nếu không giá trị có thể thay đổi theo những cách không may giữa các lần gọi.
  • "Làm mọi thứ theo giờ UTC" không phải lúc nào cũng là câu trả lời - nếu tôi muốn biết "chính xác thì 'hai tuần kể từ bây giờ' xảy ra khi nào trong múi giờ địa phương của tôi?" thì tôi cần lưu trữ ngày / giờ địa phương cũng như múi giờ.

2
@flq: Một lần nữa, bạn cần phải xác định chính xác ý mình là "an toàn năm nhuận". Tôi nghi ngờ rằng đó là một lỗi khung đã gây ra sự cố trong Azure - tôi cho rằng đó là do việc sử dụng khung không tốt.
Jon Skeet

10
Mười ba gạch đầu dòng của quảng cáo mà không có một gạch đầu dòng nào về chủ đề năm nhuận (tức là chủ đề). Và sau đó đăng trên Twitter. Tôi nghĩ bạn đã có những khoảnh khắc tuyệt vời hơn, Jon.
Will Dean

8
@WillDean: Cũng lưu ý rằng câu hỏi đã được chỉnh sửa bởi George Stocker. Tiêu đề ban đầu là "lập trình phòng thủ chống lại DateTime lỗi" - ở trường hợp này, tôi nghĩ rằng bạn sẽ đồng ý đăng bài của tôi hoàn toàn phù hợp. (Tôi chỉ nhận thấy rằng tiêu đề đã được thay đổi. Nó cho biết DateTime khi tôi bắt đầu trả lời ...)
Jon Skeet

2
Jon, xin lỗi, tôi chưa xem tiêu đề trước! Có lẽ nó cũng nên có trách nhiệm về việc ai đó thay đổi tiêu đề để chỉnh sửa tất cả các câu trả lời ...
Will Dean

3
@WillDean: Có ... Tôi muốn quay lại thay đổi tiêu đề hoặc khiến nó kết thúc bằng "Lỗi DateTime (ví dụ: năm nhuận)"
Jon Skeet

25

Cần lưu ý rằng lỗi có thể không phải do một dòng như bạn đã đăng:

DateTime.Now.AddYears(1)

Điều đó không tạo ra một ngày không hợp lệ. Nếu bạn chạy:

(new DateTime(2012, 2, 29)).AddYears(1)

bạn nhận được ngày 28 tháng 2 năm 2013. Tôi không biết tác nhân khách của Azure được viết bằng gì nhưng nó phải là một cuộc gọi khác không thành công. Một cách tồi tệ để thực hiện điều này trong .NET sẽ là:

new DateTime(today.Year + 1, today.Month, today.Day)

Điều đó ném ra một ngoại lệ nếu today là ngày nhuận. Tuy nhiên, blog của Microsoft về vấn đề Azure nói rằng họ đã tạo một ngày không hợp lệ là ngày 29 tháng 2 năm 2013, điều mà tôi không chắc là có thể thực hiện được DateTimetrong .NET.

Tôi không nói điều đó DateTimeDateTimeOffsetkhông dễ xảy ra lỗi, chỉ là tôi không nghĩ rằng họ sẽ gây ra vấn đề cụ thể này.


Tôi đoán nó đã được thực hiện trong C ++
PhilPursglove

2
@PhilPursglove: Điều gì khiến bạn nghĩ như vậy? Hệ điều hành Windows được viết chủ yếu bằng C và C ++ và xử lý các ngày nhuận đúng cách. Nó có thể liên quan nhiều hơn đến lỗi liên quan đến ngày tháng trong quá trình tạo chứng chỉ chuyển giao.
In silico

Nó có thể được phân tích cú pháp một ngày từ một chuỗi?
Fixer

Và vâng, bạn đã đúng về AddYears (1) như bạn đã chỉ ra. Tôi không chắc những gì đã xảy ra trong nội bộ. Nó chỉ là một nỗ lực để cung cấp cho câu hỏi một chút quan điểm, mà không đi quá sâu vào bài đăng trên blog.
Fixer

Nó có ý nghĩa. Tôi thành thật không biết nếu đây là trường hợp, nhưng đáng tin cậy. Tôi đã thấy mã khó chịu từ các công ty lớn, bao gồm MS. Nhưng một lần nữa, chúng tôi thực sự không thể làm gì nhiều ngoài suy đoán vào thời điểm này.
Alpha

2

Làm thế nào chúng ta có thể phát triển các phương pháp mã hóa được thiết kế để bảo vệ khỏi các lỗi của năm nhuận? Những thực hành mã hóa nào có thể đã ngăn chặn điều này?

Kiểm tra đơn vị ngày cụ thể như John đã đề cập là một thực hành mã sẽ hỗ trợ tuy nhiên không có gì đánh bại những gì tôi định nghĩa là 'kiểm tra tích hợp thủ công'

thay đổi đồng hồ trên máy chủ phát triển / thử nghiệm của bạn và xem điều gì xảy ra khi thời gian trôi qua.

Đừng sa lầy vào các chi tiết cụ thể cho dù đây có phải là 'thực hành mã hóa' - Rõ ràng là bạn không thể làm điều này cho mọi ngày trên lịch - hãy chọn những ngày bạn quan tâm, đó là ngày 29 tháng 2, cuối tháng ngày hoặc ngày chuyển đổi tiết kiệm ánh sáng ban ngày.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.