Xử lý ngày tháng một cách đúng đắn trong PHP | VietnamWorks InTECH

Bạn hoàn toàn có thể đã nghe rằng API ngày và giờ của PHP hơi lộn xộn và thực sự mọi thứ đều không tuyệt vời và hoàn hảo nhất, nhưng đó không phải là nguyên do để khiến code và logic ứng dụng của bạn trở nên lộn xộn. PHP vẫn đủ mạnh để bất kể ứng dụng nào xử lý ngày và giờ theo cách tiêu chuẩn, thuận tiện duy trì và có được hành vi mà bạn thực sự mong đợi ( không có gì quá bất ngờ với múi giờ của người dùng ngoại lai, thời hạn mùa hè tràn 30/31 tháng, năm nhuận, v.v. ). ) .

Sử dụng những setup tương thích

Đầu tiên, đặt múi giờ mặc định thành UTC .

Bạn cũng có thể thay đổi cài đặt này từ php.ini bằng mục nhập date.timezone. Nếu bạn sử dụng một framework, sẽ có một cấu hình chuyên dụng (trong Laravel, đó là thuộc tính ‘timezone’ trong config/ app.php. Nhưng có một điều chắc chắn là bạn phải thiết lập nó, bạn không nên làm việc với một múi giờ không xác định và cho một khả năng tương thích tốt hơn của ứng dụng của bạn trên bất kỳ máy chủ/ lưu trữ nào, cài đặt ứng dụng ở cấp ứng dụng là an toàn hơn).

Lười biếng và giữ gìn tài liệu

Như thường lệ trong lập trình, bạn nên đợi tích tắc ở đầu cuối để quy đổi tài liệu, thay vì triển khai tổng thể việc làm ngay từ đầu. Về ngày tháng, bạn nên hoãn những quy đổi ( định dạng, quy đổi, bản dịch, v.v. ) tài liệu ngày và giờ miễn là bạn hoàn toàn có thể .

Giải pháp A : Gửi bản thô cho người mua

Trong trường hợp tốt nhất, bạn sẽ trọn vẹn không xử lý nó từ sever của mình và để một số ít người dùng front-end xử lý nó, ví dụ : sử dụng momentjs hoặc đơn thuần là đối tượng người tiêu dùng Date gốc trong JavaScript .
Giả sử bạn nhận được ngày-giờ như ‘ 2018 – 05 – 12 23 : 16 : 46.123456 ‘ từ DB của mình hoặc bất kể thông tin nguồn vào nào ( hoàn toàn có thể có hoặc không có mili giây / micro giây, ngày và giờ hoàn toàn có thể được phân tách bằng dấu cách hoặc chữ “ T ” và nó hoàn toàn có thể được gắn với múi giờ nhưng như đã nói sớm hơn, bạn chỉ cần có UTC trong DB của mình và làm múi giờ mặc định trong PHP ), sau đó bạn hoàn toàn có thể xuất chuỗi JS cần với như sau :

Bạn sẽ nhận được ‘ 2018 – 05 – 12T23 : 16 : 46.123456 Z ’, điều này hoàn toàn có thể được chuyển cho lớp Date JS gốc :

Khi tạo đối tượng người dùng Date, trình duyệt / front-end người dùng sẽ tự động hóa chuyển ngày và giờ từ UTC ( vì “ Z ” có nghĩa là UTC ) sang múi giờ địa phương .
Sau đó ,. toLocaleString sẽ định dạng nó bằng cách sử dụng setup máy khách ( vương quốc / khu vực ) .
Ghi chú nhanh về DateTimeImmutable. PHP phân phối cả DateTime và DateTimeImmutable, thế cho nên bạn hoàn toàn có thể quyết định hành động xem đối tượng người dùng ngày của mình hoàn toàn có thể biến hóa được hay không. Ngày tháng cố định và thắt chặt thường bảo đảm an toàn hơn vì thay vì sửa đổi đối tượng người tiêu dùng hiện tại, bất kể giải pháp sửa đổi nào sẽ tạo ra một đối tượng người dùng mới nên vẫn hoàn toàn có thể sử dụng đối tượng người tiêu dùng mới và đối tượng người tiêu dùng gốc, như trong ví dụ này :

Giải pháp B : Định dạng nó bằng PHP

Vì nhiều nguyên do, bạn hoàn toàn có thể không vận dụng giải pháp A. Đầu tiên là nếu bạn không có front-end ( gửi e-mail, kết xuất CLI, v.v. )
Trong trường hợp này, bạn sẽ cần chọn múi giờ và định dạng / ngôn từ hiển thị với PHP, nó hoàn toàn có thể được đọc từ thiết lập trình duyệt, được người dùng chọn theo cách bằng tay thủ công trải qua biểu mẫu, nhưng vì phần này không phải là chủ đề chính, nhưng tất cả chúng ta có chúng trong những biến .

Bạn hoàn toàn có thể nghĩ rằng những setup hỗn hợp Hoa Kỳ và Châu Âu là xích míc nhưng trong thực tiễn không phải vậy. Ta đã cố ý chọn chúng để làm rõ rằng bạn không hề đoán được cái này từ cái còn lại. $ lang là “ Người dùng muốn bạn trò chuyện với họ bằng ngôn từ nào ? ”, USD timezone là “ Người dùng ở đâu ? ” vì thế nó vấn đáp những câu hỏi khác nhau .
Cụ thể, ta sẽ sử dụng $ lang để dịch những từ nếu cần ( Thứ Hai / Tháng Một / “ st ” ) và chọn định dạng : DD / MM / YYYY trong fr_FR, MM / DD / YYYY trong en_US, DD.MM.YYYY trong en_UK, phần này hầu hết nhờ vào vào khu vực ngôn từ chứ không phụ thuộc vào vào khu vực bạn đang ở, nếu bạn sống ở Chicago và đi du lịch một tuần đến London, bạn hoàn toàn có thể sẽ không đổi khác thiết lập máy tính của mình để hiển thị ngày dưới dạng DD.MM.YYYY bạn vẫn sẽ nhận được ngày hiển thị trong en_US .
Tuy nhiên, bạn sẽ nhận được thời hạn theo múi giờ Châu Âu / Luân Đôn .
Đầu tiên hãy xem cách xử lý nó với vanilla PHP :

Thật ra, nội dung ngày và giờ không phải là điểm mạnh của PHP. Bạn hoàn toàn có thể làm tốt hơn một chút ít bằng cách setup phần lan rộng ra intl và sử dụng lớp IntlDateFormatter ( http://php.net/manual/en/class.intldateformatter.php ). Không thì bạn sẽ đương đầu với code kiểu rất thủ tục này .
Nó sẽ tìm kiếm ngôn từ và định dạng tương thích ( % x là định dạng ngày ưa thích và % X là định dạng thời hạn ưa thích ) trong những gói ngôn từ được setup trên máy, sau đó nó hiển thị với múi giờ tương thích .
Nhưng có một điểm yếu : ở đây ta thử en_US. UTF-8, en_US. utf8, en_US rồi sau cuối là en vì tên ngôn từ nhờ vào vào gói thiết lập ( tương hỗ OS / utf8 ) và mỗi máy hoàn toàn có thể có sẵn nhiều ngôn từ khác nhau. Vì vậy, chúng tôi không chắc mình sẽ nhận được ngôn từ đúng mực nào và ngay cả khi có ngôn từ nào có trong số đó hay không .

Đã có Carbon

Để có được một bản quốc tế hóa đáng an toàn và đáng tin cậy, bạn sẽ cần phải nhúng những bản dịch vào mọi ngôn từ mà bạn muốn tương hỗ và để có được ngay trong đối tượng người dùng ngày tháng mà bạn sẽ cần để lan rộng ra lớp DateTime ( Immutable ). Rất may, bạn không cần phải khởi đầu lại mọi thứ, có những thư viện nhúng bản dịch và định dạng để hoàn toàn có thể xử lý hầu hết những nhu yếu về ngày tháng. Ta sẽ lấy Carbon, được sử dụng thoáng đãng, có sẵn trong Laravel. Ngày tháng mà bạn nhận được từ những quy mô của mình thực sự là những mẫu của Carbon. Ngoài ra, bạn hoàn toàn có thể thuận tiện setup nó với trình soạn ( https://getcomposer.org/ ) .
Tham khảo tài liệu để thiết lập hoặc tăng cấp lên phiên bản mới nhất : https://carbon.nesbot.com/
Nếu bạn có phiên bản Laravel < 5.8, bạn đang sử dụng phiên bản Carbon 1 theo mặc định, nhưng bạn hoàn toàn có thể tìm thấy trong trang chính của tài liệu hướng dẫn cách làm cho Carbon 2 hoạt động giải trí với phiên bản Laravel của bạn . Bây giờ, giả sử bạn có Carbon 2.10 trở lên, bạn hoàn toàn có thể sử dụng cả hai lớp \ Carbon \ Carbon và \ Carbon \ CarbonImmutable cho cả DateTime và DateTimeImmutable tương ứng. Như bất kể tên lớp PHP nào có khoảng trống tên, bạn hoàn toàn có thể sử dụng Carbon \ Carbon ; và / hoặc sử dụng Carbon \ CarbonImmutable ; ở đầu tệp và sau đó chỉ cần gọi Carbon và CarbonImmutable sau trong tệp của bạn . Xử lý carbon chuỗi JSON để ví dụ tiên phong của ta trở thành :

Và ví dụ json_encode với nhiều ngày tháng trong đó đơn giản trở thành:

Sau đó, giờ đây đến quốc tế hóa và múi giờ, ta sẽ dựa vào bản dịch Carbon nội bộ ( xem toàn bộ những ngôn từ có sẵn tại đây : https://carbon.nesbot.com/docs/#api-localization ) :

Bạn có nhiều cách nhưng calender là một cách rất thân thiện với người dùng để hiển thị ngày và giờ, nếu thời hạn là ngày hiện tại, bạn sẽ nhận được “ Hôm nay lúc 3 : 54 chiều ”, nếu đó là tuần hiện tại và trong quá khứ “ Thứ Hai lúc 11 : 30 sáng ”, nếu xa hơn, chỉ cần “ 01/01/2022 ” và toàn bộ những từ và định dạng sẽ khớp với biến USD lang .
Sau đó, nếu bạn có giao diện người dùng JS, bạn hoàn toàn có thể hiển thị nó theo cách tựa như nhờ vào moment.js ( https://momentjs.com/ ) :

Vì vậy, bạn nhận được cùng một hiển thị ngày của bạn trong mọi đầu ra .
Từ phía PHP, bạn vẫn hoàn toàn có thể rút ngắn mọi thứ một chút ít bằng cách sử dụng macro ( ) .

Trong mọi trường hợp, bạn nhận được nó với USD timezone và USD lang. Trong ví dụ này, ta giả sử những biến đó đã được đặt, nếu chúng hoàn toàn có thể là giá trị rỗng, bạn phải cung ứng giá trị dự trữ, ví dụ :

Muốn mặt trước giống nhau ? Moment. js cũng hoàn toàn có thể làm điều đó :

Đầu vào của người dùng có gì ?

Giờ ta đã biết cách gửi và hiển thị ngày tháng từ sever đến máy khách, hãy bàn về việc gửi ngày và giờ từ máy khách đến sever .
Bạn hoàn toàn có thể hỏi ngày bằng nhiều công cụ biểu mẫu ( nhập văn bản không tính tiền, bộ chọn năm-tháng-ngày ) hoặc sử dụng nguồn vào HTML5 ( type = ” date ” và type = ” time ” ) được tương hỗ bởi những trình duyệt thực bị thiếu trong Internet Explorer, Safari và Opera Mini. Bạn cũng hoàn toàn có thể trả google công cụ chọn ngày ( + bất kể thư viện nào bạn sử dụng nếu có ) và tìm thấy rất nhiều bộ chọn siêu vừa lòng .
Vì vậy, tùy thuộc vào công cụ của bạn, bạn hoàn toàn có thể truy xuất số, chuỗi hoặc đối tượng người tiêu dùng Ngày. Trước tiên, những chuỗi và số không thích hợp để được gửi thô đến sever. Khi người dùng nhập ngày và giờ, nghĩa là anh ta ( ngay cả khi anh ta không nhận ra ) đang nhập ngày giờ này trong múi giờ của chính mình. Vì vậy, sever sẽ phải lý giải những chuỗi và số đó bằng cách sử dụng múi giờ của người dùng mà anh ta sẽ phải đoán / hỏi / phát hiện. Đừng làm vậy. Chỉ cần nói với sever của bạn bằng ngôn từ thời hạn chung ( UTC ). Vì vậy, từ front-end của bạn, lấy những nguồn vào đó và quy đổi chúng thành đối tượng người tiêu dùng Date. Giống như ta đã làm trước đó nhưng lần này sẽ không chỉ định múi giờ ( không có chữ Z ở cuối ) để trình duyệt tạo ra những gì ta cần chính là Date địa phương ( với múi giờ hiện tại của thiết bị ) .

Bây giờ tất cả chúng ta có Date địa phương, bạn chỉ cần lấy chuỗi ISO từ nó với date. toISOString ( ). Lấy tài liệu ở trên ( 2018 – 01 – 25 12 : 30 ), nó sẽ phân phối cho bạn “ 2018 – 01 – 25T04 : 30 : 00.000 Z ” nếu bạn ở Chicago, “ 2018 – 01 – 25T11 : 30 : 00.000 Z ” nếu bạn đang ở Paris, v.v. Và đó là điều ta muốn, ta cần chăm sóc đến múi giờ của người dùng. Lưu ý rằng toISOString cũng sống sót vào thời gian hiện tại, thế cho nên bạn sẽ nhận được cùng một hiệu quả bằng cách sử dụng moment thay vì new Date .
Đầu ra chuỗi đó chỉ cần được gửi đến PHP ( vẫn sử dụng UTC làm múi giờ nhập mặc định ) và nếu bạn muốn lưu nó ở đâu đó ( giả sử là cơ sở tài liệu ), bạn hoàn toàn có thể chỉ cần sử dụng định dạng :

Xóa. u nếu bạn không cần tàng trữ mili / micro giây. Sau đó, bạn sẵn sàng chuẩn bị sẵn chuỗi của mình để được tàng trữ trong cột SQL DATETIME. Bạn hoàn toàn có thể sử dụng Carbon hoặc CarbonImmutable thay vì DateTimeImmutable vì giải pháp định dạng giống nhau .

Trường hợp đặc biệt quan trọng

Một chú ý quan tâm nhỏ về 1 số ít trường hợp ngoại lệ .
Đôi khi bạn muốn người dùng chỉ chọn một ngày ( năm, tháng, ngày ) nhưng không chọn giờ / phút. Trong trường hợp này, bạn phải rõ ràng về ý nghĩa của ngày này. Ví dụ : nếu bạn chọn 2018 – 01 – 24 là ngày nghỉ trong lịch của mình, nghĩa là bạn sẽ không ở văn phòng cả ngày ( từ 00:00:00 đến 23:59:59, múi giờ văn phòng ), như thường ngày, ý nói đến cả ngày trong một múi giờ nhất định ( thường là múi giờ của trình duyệt ). Nếu văn phòng của bạn ở San Fransisco, so với ai đó ở Sidney, anh ta sẽ không hề gọi cho bạn từ 2018 – 01 – 24 19:00:00 đến 2018 – 01 – 25 18:59:59, nếu bạn thêm giờ Open, bạn hoàn toàn có thể thấy rằng so với một đồng nghiệp ở xa ở Sidney, bạn hoàn toàn có thể liên hệ trong ngày 24 nhưng không phải ngày 25, so với một người ở Paris, bạn sẽ ở đó vào buổi sáng nhưng không phải buổi chiều. Vì vậy, chỉ một ngày trong múi giờ của bạn là khoảng chừng giữa 2 thời gian so với phần còn lại của quốc tế. Đề xuất cho bạn trong trường hợp này là lưu đầu khoanh vùng phạm vi này 2018 – 01 – 24 00 : 00 ở San Fransisco, thế cho nên bạn tiết kiệm chi phí 2018 – 01 – 24 09 : 00 ( UTC ) trong DB của bạn. Sau đó, bạn hoàn toàn có thể thuận tiện truy xuất thời gian này và thêm 1 ngày ( sử dụng -> modify ( ‘ 1 day ’ ) trong PHP ) để nhận được phần cuối của khoanh vùng phạm vi, sau đó sử dụng múi giờ của người dùng để hiển thị phạm vi này theo cách họ hoàn toàn có thể hiểu được .
Trong trường hợp khác, bạn chọn một ngày chỉ vì thời hạn đã được biết trước ( như chọn ngày ăn trưa ), bạn hoàn toàn có thể chỉ cần giả sử thời hạn là 12 : 00 ( giờ địa phương ) và thêm nó để nhận hàng loạt chuỗi ngày-giờ của bạn .
Đôi khi người dùng phải chọn một ngày không nằm trong múi giờ của chính mình. Nếu bạn muốn đặt một khách sạn ở Tokyo, bạn sẽ chọn ngày đến ( và / hoặc thời hạn ) của mình bằng múi giờ Tokyo. Trong trường hợp này, bạn hoàn toàn có thể ” xóa ” múi giờ của người dùng khi tạo đối tượng người tiêu dùng Date :
Vì vậy, khi bạn gọi toISOString ( ), về cơ bản bạn sẽ nhận được đúng chuẩn như một phương pháp khởi tạo ngày .
Sau đó, bạn hoàn toàn có thể cắt múi giờ này và buộc một múi giờ khác ở phía sever :

Tất nhiên, bạn cũng sẽ phải hiển thị ngày này với múi giờ Tokyo (nó có thể được xác định rõ ràng với một đề cập như “giờ Tokyo”).

Trường hợp ở đầu cuối : múi giờ không xác lập. Sẽ rất hiếm khi xảy ra, hầu hết khi bạn nghĩ rằng mình đang ở trong trường hợp này, nhưng trên thực tiễn, bạn hoàn toàn có thể tìm thấy múi giờ bằng 1 số ít nghiên cứu và phân tích kinh doanh thương mại. Nhưng ngày sinh là một ví dụ nổi bật. Nếu bạn sinh năm 1970 – 01 – 01 01 : 23 ở Rome, khi bạn của bạn ở Thành Phố New York, bạn nên nói “ Tôi sinh năm 1969 – 12 – 31 19 : 23 ” hoặc bạn nên nói đúng chuẩn là tôi sinh ra ở Rome. Thông thường, bạn chỉ nói ngày ( thực tiễn là ước tính 24 giờ ) và bạn không nói khu vực ( một ước tính khác là 26 giờ khi múi giờ trên Trái đất đi từ – 12 h đến + 14 h ), vì thế bạn đang đưa ra một thông tin rất không đúng mực ( 1970 – 01 – 01 hoàn toàn có thể từ ngày này là nửa đêm theo giờ GMT-12 đến 23 : 59 GMT + 14 ) và bất chấp sự mơ hồ lê dài 50 giờ này, mọi người đều hoàn toàn có thể gật đầu ngày sinh của bạn như hiện tại và hiển thị nó khi bạn được cho phép mọi người được xem. Trong trường hợp này, ta sẽ lấy giả sử đó là UTC ( như trong ví dụ về khách sạn ở Tokyo ), sau đó ta sẽ coi nó như cũ mà không có sự đổi khác múi giờ ở phía sever và vẫn lưu nó mà không biến hóa. Cuối cùng, ta sẽ hiển thị ngày này theo múi giờ UTC .

Tổng hợp việc làm IT – Software trên VietnamWorks
VietnamWorks InTECH
Theo Kylekatarnls