Nó phụ thuộc vào loại cuộc hẹn.
Có hai loại cuộc hẹn:
- Một khoảnh khắc, một điểm cụ thể trên tiến trình, bỏ qua mọi thay đổi đối với quy tắc múi giờ.
Ví dụ:Khởi động tên lửa. - Ngày và giờ trong ngày có thể điều chỉnh các thay đổi đối với quy tắc múi giờ.
Ví dụ:Khám sức khỏe / nha khoa.
Khoảnh khắc
Ví dụ:nếu chúng ta đặt trước cho việc phóng tên lửa, chúng ta không quan tâm đến ngày và giờ trong ngày. Chúng tôi chỉ quan tâm đến thời điểm khi (a) các tầng trời sắp xếp, và (b) chúng tôi mong đợi thời tiết thuận lợi.
Nếu trong thời gian can thiệp, các chính trị gia thay đổi các quy tắc về múi giờ đang được sử dụng tại địa điểm ra mắt của chúng tôi hoặc tại văn phòng của chúng tôi, điều đó không ảnh hưởng đến việc bổ nhiệm ra mắt của chúng tôi. Nếu các chính trị gia quản lý trang web khởi chạy của chúng tôi áp dụng Giờ tiết kiệm ánh sáng ban ngày (DST) , thời điểm ra mắt của chúng tôi vẫn giữ nguyên. Nếu các chính trị gia quản lý văn phòng của chúng tôi quyết định đổi đồng hồ sớm hơn nửa tiếng vì có quan hệ ngoại giao với một quốc gia láng giềng, thời điểm ra mắt của chúng tôi vẫn giữ nguyên.
Đối với một cuộc hẹn như vậy, vâng, cách tiếp cận của bạn sẽ đúng. Bạn sẽ ghi lại cuộc hẹn trong UTC
sử dụng một cột thuộc loại TIMESTAMP WITH TIME ZONE
. Sau khi truy xuất, hãy điều chỉnh thành bất kỳ múi giờ nào mà người dùng thích.
Cơ sở dữ liệu như Postgres
sử dụng bất kỳ thông tin múi giờ nào kèm theo thông tin đầu vào để điều chỉnh theo UTC, sau đó loại bỏ thông tin múi giờ đó. Khi bạn truy xuất giá trị từ Postgres, nó sẽ luôn đại diện cho một ngày với thời gian trong ngày như được thấy trong UTC. Hãy cẩn thận, một số công cụ hoặc phần mềm trung gian có thể có tính năng chống lại việc áp dụng một số múi giờ mặc định giữa việc truy xuất từ cơ sở dữ liệu và gửi cho bạn lập trình viên. Nhưng hãy rõ ràng:Postgres luôn lưu và truy xuất các giá trị thuộc loại TIMESTAMP WITH TIME ZONE
trong UTC, luôn là UTC và offset-from-UTC
không giờ-phút-giây.
Đây là một số mã Java mẫu.
LocalDate launchDateAsSeenInRome = LocalDate.of( 2021 , 1 , 23 ) ;
LocalTime launchTimeAsSeenInRome = LocalTime.of( 21 , 0 ) ;
ZoneId zoneEuropeRome = ZoneId.of( "Europe/Rome" ) ;
// Assemble those three parts to determine a moment.
ZonedDateTime launchMomentAsSeenInRome = ZonedDateTime.of( launchDateAsSeenInRome , launchTimeAsSeenInRome , zoneEuropeRome ) ;
Để xem khoảnh khắc tương tự ở UTC, hãy chuyển đổi sang Instant
. Một Instant
đối tượng luôn đại diện cho một thời điểm như được thấy trong UTC.
Instant launchInstant = launchMomentAsSeenInRome.toInstant() ; // Adjust from Rome time zone to UTC.
Z
ở cuối ví dụ chuỗi trên là ký hiệu chuẩn cho UTC và được phát âm là "Zulu".
Rất tiếc, nhóm JDBC 4.2 đã bỏ qua việc yêu cầu hỗ trợ cho Instant
hoặc ZonedDateTime
các loại. Vì vậy, trình điều khiển JDBC
của bạn có thể có hoặc không thể đọc / ghi các đối tượng đó vào cơ sở dữ liệu của bạn. Nếu không, chỉ cần chuyển đổi thành OffsetDateTime
. Cả ba loại này đều thể hiện một thời điểm, một điểm cụ thể trên dòng thời gian. Nhưng OffsetDateTime
được hỗ trợ bởi JDBC
4.2
vì những lý do khiến tôi trốn tránh.
OffsetDateTime odtLaunchAsSeenInRome = launchMomentAsSeenInRome.toOffsetDateTime() ;
Ghi vào cơ sở dữ liệu.
myPreparedStatement.setObject( … , odtLaunchAsSeenInRome ) ;
Truy xuất từ cơ sở dữ liệu.
OffsetDateTime launchMoment = myResultSet.getObject( … , OffsetDateTime.class ) ;
Điều chỉnh theo múi giờ New York mà người dùng của bạn mong muốn.
ZoneId zoneAmericaNewYork = ZoneId.of( "America/New_York" ) ;
ZonedDateTime launchAsSeenInNewYork = launchMoment.atZoneSameInstant( zoneAmericaNewYork ) ;
Bạn có thể thấy tất cả mã trên chạy trực tiếp tại IdeOne.com .
Nhân tiện, theo dõi các sự kiện trong quá khứ cũng được coi là một thời điểm. Khi nào bệnh nhân thực sự đến hẹn, khi nào khách hàng thanh toán hóa đơn, khi nào người thuê mới ký vào tài liệu của họ, khi nào máy chủ bị sập… tất cả những điều này đều được theo dõi trong thời gian ngắn theo giờ UTC. Như đã thảo luận ở trên, thông thường đó sẽ là Instant
, mặc dù ZonedDateTime
&OffsetDateTime
cũng đại diện cho một thời điểm. Đối với cơ sở dữ liệu, hãy sử dụng TIMESTAMP WITH TIME ZONE
(không phải WITHOUT
).
Thời gian trong ngày
Tôi cho rằng hầu hết các ứng dụng hướng đến doanh nghiệp đều tập trung vào các cuộc hẹn thuộc loại khác, nơi chúng tôi nhắm đến một cuộc hẹn với thời gian trong ngày hơn là một thời điểm cụ thể.
Nếu người dùng đặt lịch hẹn với nhà cung cấp dịch vụ chăm sóc sức khỏe của họ để xem xét kết quả của một bài kiểm tra, họ sẽ làm như vậy vào một thời gian cụ thể trong ngày vào ngày đó. Nếu trong thời gian chờ đợi, các chính trị gia thay đổi các quy tắc về múi giờ của họ, di chuyển đồng hồ trước hoặc sau một giờ hoặc nửa giờ hoặc bất kỳ khoảng thời gian nào khác, ngày và giờ của cuộc hẹn khám bệnh đó vẫn giữ nguyên. Trên thực tế, điểm của lịch trình của cuộc hẹn ban đầu sẽ được thay đổi sau khi các chính trị gia thay đổi múi giờ, chuyển sang điểm sớm hơn / muộn hơn trên dòng thời gian.
Đối với cuộc hẹn như vậy, chúng tôi không lưu trữ ngày và giờ trong ngày như trong UTC. Chúng tôi không sử dụng loại cột cơ sở dữ liệu TIMESTAMP WITH TIME ZONE
.
Đối với các cuộc hẹn như vậy, chúng tôi lưu trữ ngày với thời gian trong ngày mà không liên quan đến múi giờ. Chúng tôi sử dụng một cột cơ sở dữ liệu thuộc loại TIMESTAMP WITHOUT TIME ZONE
(thông báo WITHOUT
thay vì WITH
). Loại đối sánh trong Java là LocalDateTime
.
LocalDate medicalApptDate = LocalDate.of( 2021 , 1 , 23 ) ;
LocalTime medicalApptTime = LocalTime.of( 21 , 0 ) ;
LocalDateTime medicalApptDateTime = LocalDateTime.of( medicalApptDate , medicalApptTime ) ;
Ghi điều đó vào cơ sở dữ liệu.
myPreparedStatement.setObject( … , medicalApptDateTime ) ;
Hãy làm rõ điều này:a LocalDateTime
đối tượng không không đại diện cho một thời điểm, không phải là không một điểm cụ thể trên dòng thời gian. Một LocalDateTime
đối tượng đại diện cho một phạm vi có thể các khoảnh khắc dọc theo khoảng 26-27 giờ của dòng thời gian (phạm vi múi giờ trên toàn cầu). Để mang lại ý nghĩa thực sự cho LocalDateTime
, chúng ta phải liên kết một múi giờ dự định.
Đối với múi giờ dự định đó, hãy sử dụng cột thứ hai để lưu trữ mã định danh vùng. Ví dụ:chuỗi Europe/Rome
hoặc America/New_York
. Xem danh sách tên vùng
.
ZoneId zoneEuropeRome = ZoneId.of( "Europe/Rome" ) ;
Viết nó vào cơ sở dữ liệu dưới dạng văn bản.
myPreparedStatement.setString( … , zoneEuropeRome ) ;
Truy xuất. Lấy tên vùng dưới dạng văn bản và khởi tạo một ZoneId
đối tượng.
LocalDateTime medicalApptDateTime = myResultSet.getObject( … , LocalDateTime.class ) ;
ZoneId medicalApptZone = ZoneId.of( myResultSet.getString( … ) ) ;
Đặt hai phần đó lại với nhau để xác định thời điểm được biểu thị dưới dạng ZonedDateTime
sự vật. Thực hiện động tác này khi bạn cần sắp xếp lịch. Nhưng đ ừng lưu trữ khoảnh khắc. Nếu các chính trị gia xác định lại (các) múi giờ trong tương lai, thì thời điểm đó phải được tính toán.
ZonedDateTime medicalApptAsSeenInCareProviderZone = ZonedDateTime.of( medicalApptDateTime , medicalApptZone ) ;
Người dùng đang đi du lịch đến New York Hoa Kỳ. Họ cần biết khi nào nên gọi cho nhà cung cấp dịch vụ chăm sóc sức khỏe ở Milan Ý theo đồng hồ trên tường ở vị trí tạm thời của họ ở New York. Vì vậy, hãy điều chỉnh từ múi giờ này sang múi giờ khác. Cùng một khoảnh khắc, thời gian trên đồng hồ treo tường khác nhau.
ZoneId zoneAmericaNewYork = ZoneId.of( "America/New_York" ) ;
ZonedDateTime medicalApptAsSeenInNewYork = medicalApptAsSeenInCareProviderZone.withZoneSameInstant( zoneAmericaNewYork ) ;
tzdata
Lưu ý rằng nếu các quy tắc về múi giờ mong muốn của bạn có thể thay đổi, bạn phải cập nhật bản sao định nghĩa múi giờ trên máy tính của mình.
Java chứa bản sao của riêng nó của tzdata , công cụ cơ sở dữ liệu Postgres cũng vậy. Và cả hệ điều hành máy chủ của bạn. Mã cụ thể được hiển thị ở đây chỉ yêu cầu Java phải được cập nhật. Nếu bạn sử dụng Postgres để điều chỉnh múi giờ, thì tzdata của nó cũng phải được cập nhật. Và để ghi nhật ký và như vậy, hệ điều hành máy chủ của bạn phải được cập nhật. Để người dùng xem đồng hồ thích hợp, hệ điều hành của máy khách của họ cũng phải được cập nhật.
Hãy cẩn thận:Các chính trị gia trên khắp thế giới đã thể hiện xu hướng thay đổi múi giờ của họ với tần suất đáng ngạc nhiên và thường ít có thông báo trước.
Giới thiệu về java.time
java.time
khung công tác được xây dựng trong Java 8 trở lên. Các lớp này thay thế cho kế thừa
cũ rắc rối các lớp date-time chẳng hạn như java.util.Date
, Calendar
& SimpleDateFormat
.
Để tìm hiểu thêm, hãy xem Hướng dẫn Oracle . Và tìm kiếm Stack Overflow để có nhiều ví dụ và giải thích. Đặc điểm kỹ thuật là JSR 310 .
Joda-Time dự án, hiện đang ở chế độ bảo trì , khuyên bạn nên di chuyển sang java.time các lớp học.
Bạn có thể trao đổi java.time đối tượng trực tiếp với cơ sở dữ liệu của bạn. Sử dụng trình điều khiển JDBC
tuân thủ JDBC 4.2
hoặc sau đó. Không cần chuỗi, không cần java.sql.*
các lớp học. Hỗ trợ Hibernate 5 &JPA 2.2 java.time .
Lấy các lớp java.time ở đâu?
- Java SE 8
, Java SE 9
, Java SE 10
, Java SE 11
và sau này - Một phần của API Java tiêu chuẩn với một triển khai đóng gói.
- Java 9 đã mang lại một số tính năng nhỏ và các bản sửa lỗi.
- Java SE 6
và Java SE 7
- Hầu hết java.time chức năng được chuyển ngược sang Java 6 &7 trong ThreeTen-Backport .
- Android
- Các phiên bản mới hơn của Android (26+) triển khai gói java.time lớp học.
- Đối với Android cũ hơn (<26), quá trình được gọi là Gỡ lỗi API
mang lại một tập hợp con của java.time
chức năng ban đầu không được tích hợp vào Android.
- Nếu việc gỡ rối không cung cấp những gì bạn cần, ThreeTenABP dự án điều chỉnh ThreeTen-Backport (đã đề cập ở trên) sang Android. Xem Cách sử dụng ThreeTenABP… .