2010-03-21 35 views

Trả lời

33

Ví dụ cụ thể mà bạn đã sử dụng cho câu hỏi của mình (năm 1200), những thứ về mặt kỹ thuật sẽ hoạt động.

Nói chung, tuy nhiên, dấu thời gian không thể được sử dụng cho mục đích này. Đầu tiên, giới hạn phạm vi là tùy ý: trong MySQL là ngày 1 tháng 1 năm 1000. Nếu bạn đang làm việc với công cụ thế kỷ 12-13, mọi thứ trở nên tốt đẹp ... nhưng nếu bạn cần thêm thứ gì đó cũ hơn (thế kỷ thứ 10 hoặc cũ hơn)), ngày sẽ phá vỡ một cách thảm hại và việc khắc phục sự cố sẽ yêu cầu định dạng lại tất cả các ngày lịch sử của bạn thành một cái gì đó phù hợp hơn.

Dấu thời gian thường được biểu diễn dưới dạng số nguyên, với "khoảng thời gian đánh dấu" và "điểm epoch", vì vậy con số thực sự là số lượng ve đã trôi qua kể từ kỷ nguyên đến ngày được biểu diễn (hoặc viceversa cho ngày âm). Điều này có nghĩa rằng, giống như với bất kỳ kiểu dữ liệu số nguyên cố định nào, tập hợp các giá trị có thể biểu diễn là hữu hạn. Hầu hết các định dạng dấu thời gian tôi biết về phạm vi hy sinh có lợi cho độ chính xác, chủ yếu là bởi vì các ứng dụng cần phải thực hiện thời gian arithmetics thường cần phải làm như vậy với một độ chính xác khá; trong khi các ứng dụng cần phải làm việc với các ngày lịch sử thì rất hiếm khi cần phải thực hiện các arithmetics nghiêm trọng.

Nói cách khác, dấu thời gian có nghĩa là hiển thị chính xác ngày tháng. Thứ hai (hoặc thậm chí phân số của thứ hai) precission không có ý nghĩa cho ngày lịch sử: bạn có thể cho tôi biết, xuống đến mili giây, khi Henry thứ 8 đăng quang là Vua của nước Anh? Trong trường hợp của MySQL, định dạng vốn được định nghĩa là "4 chữ số năm", vì vậy mọi tối ưu hóa có liên quan đều có thể dựa vào giả định rằng năm sẽ có 4 chữ số hoặc toàn bộ chuỗi sẽ có chính xác 10 ký tự ("yyyy-mm-dd"), v.v. Chỉ là một điều may mắn là ngày bạn đề cập đến tựa đề của bạn vẫn còn phù hợp, nhưng thậm chí dựa vào điều đó vẫn còn nguy hiểm: bên cạnh những gì DB có thể lưu trữ, bạn cần phải nhận thức được những gì phần còn lại của ngăn xếp máy chủ của bạn có thể thao tác. Ví dụ, nếu bạn đang sử dụng PHP để tương tác với cơ sở dữ liệu của bạn, cố gắng xử lý các ngày lịch sử rất có khả năng xảy ra ở một thời điểm nào đó (trên môi trường 32 bit, phạm vi cho dấu thời gian kiểu UNIX là ngày 13 tháng 12 năm 1901 đến 19 tháng 1 năm 2038).

Tóm lại: MySQL sẽ lưu trữ đúng ngày bất kỳ có năm gồm 4 chữ số; nhưng nói chung bằng cách sử dụng dấu thời gian cho các ngày lịch sử gần như được đảm bảo để kích hoạt các vấn đề và đau đầu thường xuyên hơn không. Tôi mạnh mẽ khuyên bạn nên sử dụng như vậy.

Hy vọng điều này sẽ hữu ích.


Chỉnh sửa/bổ sung:

Cảm ơn bạn đã rất insteresting câu trả lời này.Tôi có nên tạo bản ngã của riêng mình cho ngày trước đây hoặc chọn một số khác là db nhưng cái nào? - user284523

Tôi không nghĩ rằng bất kỳ DB nào có quá nhiều hỗ trợ cho loại ngày này: các ứng dụng sử dụng nó thường có đủ với chuỗi/văn bản đại diện. Trên thực tế, cho các ngày vào năm 1 và sau đó, một đại diện văn bản thậm chí sẽ tạo ra các phân loại/so sánh chính xác (miễn là ngày được biểu diễn theo thứ tự độ lớn: y, m, d thứ tự). So sánh sẽ phá vỡ, tuy nhiên, nếu "tiêu cực" ngày cũng tham gia (họ vẫn sẽ so sánh sớm hơn bất kỳ một tích cực, nhưng so sánh hai ngày tiêu cực sẽ mang lại một kết quả đảo ngược).

Nếu bạn chỉ cần Năm 1 và ngày sau đó, hoặc nếu bạn không cần phân loại, bạn có thể làm cho cuộc sống của mình dễ dàng hơn nhiều bằng cách sử dụng chuỗi.

Nếu không, cách tiếp cận tốt nhất là sử dụng một số loại số và xác định "khoảng thời gian đánh dấu" và "điểm epoch" của riêng bạn. Một khoảng thời gian tốt có thể là ngày (trừ khi bạn thực sự cần precission hơn nữa, nhưng ngay cả sau đó bạn có thể dựa vào "thực" (floating-point) số thay vì số nguyên); và một thời đại hợp lý có thể là ngày 1 tháng 1, 1. Vấn đề chính sẽ là chuyển những giá trị này thành biểu diễn văn bản của họ, và ngược lại. Bạn cần lưu ý các chi tiết sau:

  • Năm nhuận có thêm một ngày.
  • Quy tắc cho năm nhuận là "bất kỳ bội số nào của 4" cho đến 1582, khi nó thay đổi từ lịch Julian sang Gregorian và trở thành "bội số của 4 ngoại trừ số bội số 100 trừ khi chúng cũng là bội số 400".
  • Ngày cuối cùng của lịch Julian là ngày 4 tháng 10 năm 1582. Ngày hôm sau, đầu tiên của lịch Gregorian, là ngày 15 tháng 10 năm 1582. 10 ngày đã bị bỏ qua để làm cho lịch mới khớp lại với các phần. Như đã nêu trong các ý kiến, hai quy tắc trên khác nhau theo quốc gia: Các tiểu bang và một số quốc gia công giáo đã áp dụng lịch mới vào ngày đã nêu, nhưng nhiều quốc gia khác mất nhiều thời gian hơn để làm như vậy (người cuối cùng là Turley vào năm 1926) . Điều này có nghĩa là bất kỳ ngày nào giữa con bò cái trong năm 1582 và việc nhận con nuôi cuối cùng vào năm 1926 sẽ không rõ ràng nếu không có bối cảnh địa lý và thậm chí còn phức tạp hơn để xử lý.
  • Không có "năm 0": năm trước năm 1 là năm -1 hoặc năm 1 TCN.

Tất cả điều này đòi hỏi phải phân tích cú pháp phức tạp và chức năng định dạng, nhưng ngoài nhiều trường hợp vi phạm có thực sự không quá phức tạp (nó sẽ được tẻ nhạt để mã, nhưng khá thẳng về phía trước) . Việc sử dụng các số làm biểu diễn cơ bản đảm bảo sắp xếp/so sánh chính xác cho bất kỳ cặp giá trị nào.

Biết điều này, bây giờ là lựa chọn của bạn để có cách tiếp cận phù hợp hơn với nhu cầu của bạn.

+1

Cảm ơn bạn đã trả lời rất insteresting này. Tôi có nên tạo bản ngã của riêng mình cho ngày lịch sử hoặc chọn một db khác không? – user310291

+2

Tôi muốn SO cho phép một số lượng nhỏ +2 upvotes cho câu trả lời đặc biệt tốt. Cảm ơn không chỉ các chi tiết rất hữu ích mà còn làm cho nó trở nên thú vị. Tôi có một câu hỏi nhỏ, mà tôi nghĩ rằng nói chung "BCE" được ưa thích hơn "BC". – eyelidlessness

+1

"Ngày cuối cùng của lịch Julian là ngày 4 tháng 10 năm 1582" Điều đó chỉ đúng ở một số quốc gia Châu Âu. Ngày cho việc chấp nhận lịch Gregorian thay đổi rất nhiều. Xem https://en.wikipedia.org/wiki/Gregorian_calendar#Adoption –

11

Từ documentation:

NGÀY

Một ngày. Phạm vi được hỗ trợ là '1000-01-01' đến '9999-12-31'.

+0

Cảm ơn bạn. Vì vậy, ngày dưới 1000 không được hỗ trợ. – user310291

+1

@ user284523 Ngày trước năm 1000 không được hỗ trợ bằng cách sử dụng kiểu dữ liệu ngày hoặc datetime. Tất nhiên, bạn có thể lưu trữ các ngày trước bằng cách sử dụng mã hóa của riêng bạn dưới dạng một kiểu dữ liệu khác (ví dụ như một varchar). –

+0

Nếu tôi thực hiện mã hóa riêng của mình, điều đó có nghĩa là tôi cũng sẽ phải tạo các hàm ngày tháng? Không ai có nhu cầu đó trước khi tôi ngạc nhiên khi có thể tìm thấy bất cứ điều gì? – user310291

4

Đối với bất kỳ giá trị nào, tôi thấy rằng trường MySQL DATE không hỗ trợ ngày < 1000 trong thực tế, mặc dù tài liệu nói khác đi. Ví dụ: tôi có thể nhập 325 và cửa hàng đó là 0325-00-00. Tìm kiếm WHERE table.date < 1000 cũng cho kết quả chính xác.

Nhưng tôi do dự phải dựa vào ngày < 1000 khi chúng không được hỗ trợ chính thức, cộng với đôi khi tôi cần năm BCE với hơn 4 chữ số (ví dụ: 10000 BCE). Vì vậy, các lĩnh vực INT riêng biệt cho năm, tháng và ngày (như đề xuất ở trên) dường như là sự lựa chọn duy nhất.

Tôi muốn loại NGÀY (hoặc có thể là loại HISTDATE mới) đã hỗ trợ đầy đủ các ngày lịch sử - sẽ tốt hơn nếu kết hợp ba trường thành một và chỉ sắp xếp theo ngày thay vì phải sắp xếp theo year, month, day.

+0

Bạn đã thực hiện một điểm thú vị về hỗ trợ "không chính thức" cho những ngày này. Đề xuất của bạn "sắp xếp theo CE, ngày để đạt được thứ tự thời gian" sẽ không hoạt động và có thể bỏ lỡ những người có thể sử dụng ý tưởng của bạn: thứ tự bạn nhận được sẽ là tất cả các ngày BCE từ mới đến cũ nhất, tiếp theo là tất cả CE ngày từ cũ nhất đến mới nhất. Vấn đề đó là điều duy nhất khiến tôi không cho bạn một +1, vì vậy có lẽ bạn nên xem xét sửa chữa nó. –

+0

Bạn hoàn toàn đúng, cảm ơn bạn. Tôi đã chỉnh sửa câu trả lời của mình cho phù hợp. – Holly

2

Sử dụng SMALLINT cho năm, vì vậy năm nay sẽ chấp nhận từ -32.768 (BC) đến 32.768 (AD) Như trong nhiều tháng và ngày, sử dụng TINYINT UNSIGNED

Hầu hết các sự kiện lịch sử không có tháng và ngày, do đó bạn có thể truy vấn như thế này:

SELECT events FROM history WHERE year='-4990' 

Kết quả: 'Noah Ark'

Hoặc: SELECT events FROM history WHERE year='570' AND month='4' AND day='20' trở lại: "Muhammad PBUH được sinh ra"

Tùy theo yêu cầu, bạn cũng có thể thêm DATETIME cột và làm cho nó NULL cho ngày trước 1000 và ngược lại (do đó tiết kiệm một số byte)

2

Đây là một vấn đề quan trọng và thú vị trong đó có một giải pháp khác.

Thay vì dựa vào nền tảng cơ sở dữ liệu để hỗ trợ số ngày có thể vô hạn với độ chính xác mili giây, dựa vào trình biên dịch ngôn ngữ lập trình hướng đối tượng và thời gian chạy để xử lý chính xác ngày và giờ. Có thể thực hiện điều này bằng cách sử dụng Máy ảo Java (JVM), trong đó thời gian được tính bằng mili giây so với nửa đêm, ngày 1 tháng 1 năm 1970 UTC (Epoch), bằng cách duy trì giá trị yêu cầu như một cơ sở dữ liệu dài (bao gồm các giá trị âm) và thực hiện chuyển đổi/tính toán được yêu cầu trong lớp thành phần sau khi truy xuất.

Ví dụ:

Date d = new Date(Long.MIN_VALUE); 
DateFormat df = new SimpleDateFormat("EEE, d MMM yyyy G HH:mm:ss Z"); 
System.out.println(df.format(d)); 

Nên chứng tỏ: Sun, 02 Tháng mười hai 292.269.055 BC 16:47:04 +0000

này cũng cho phép độc lập của phiên bản cơ sở dữ liệu và các nền tảng như nó tóm tắt tất cả các ngày và số học thời gian cho thời gian chạy JVM, tức là thay đổi trong các phiên bản và nền tảng cơ sở dữ liệu sẽ ít có khả năng yêu cầu thực hiện lại, nếu có.

+0

Đây là một câu trả lời thú vị, nhưng với một báo trước: mặc dù cách tiếp cận này sẽ loại bỏ sự phụ thuộc vào hệ thống db, nó giới thiệu sự phụ thuộc vào môi trường Java; vì vậy đây là một sự cân bằng hơn là một lợi ích ròng. Ngoài ra, hãy nhớ rằng sự phụ thuộc vào db không được loại bỏ hoàn toàn: bạn sẽ cần một kiểu trường khớp với 'long' của Java; và tên cho loại như vậy có thể hơi khác một chút trên các hệ thống db khác nhau. –

2

Tôi gặp sự cố tương tự và tôi muốn tiếp tục chuyển tiếp trên các trường ngày trong DB để cho phép tôi sử dụng tìm kiếm phạm vi ngày với độ chính xác lên đến một ngày cho các giá trị lịch sử. (DB của tôi bao gồm ngày tháng năm sinh và số ngày của các hoàng đế La Mã ...)

Giải pháp là để thêm một năm liên tục (ví dụ: 3000) vào tất cả các ngày trước khi thêm chúng vào DB và trừ cùng số trước khi hiển thị kết quả truy vấn cho người dùng.

Nếu bạn DB đã có một số giá trị ngày trong đó, hãy nhớ cập nhật giá trị thoát với số const mới.