Liệu struct tm lưu trữ thông tin múi giờ như là thành viên dữ liệu của nó


8

Hãy xem xét mã C ++ sau đây

#include <ctime>
#include <iostream>

int main()
{
    std::time_t now = std::time(nullptr);
    struct tm local = *std::localtime(&now);
    struct tm gm = *std::gmtime(&now);
    char str[20];
    std::strftime(str, 20, "%Z", &local);
    std::cout << str << std::endl;          // HKT
    std::strftime(str, 20, "%Z", &gm);
    std::cout << str << std::endl;          // UTC

    return 0;
}

Vì vậy, lưu trữ trong nowlà một giá trị không thể thiếu rõ ràng, trong khi localgmđang struct tmlưu trữ thông tin ngày / giờ con người có thể đọc được. Sau đó, tôi in ra thông tin được định dạng (múi giờ) chỉ dựa trên các struct tmđối tượng.

Theo cplusplus tài liệu tham khảo , các thành viên dữ liệu struct tm

tm_sec  
tm_min  
tm_hour 
tm_mday 
tm_mon  
tm_year 
tm_wday 
tm_yday 
tm_isdst

Nếu đó là tất cả những gì struct tmchứa, làm thế nào để chương trình biết rằng thông tin múi giờ từ nó? Đó là, làm thế nào để biết rằng múi giờ HKTdành cho localvà múi giờ UTCdành cho gm?

Nếu đó không phải là tất cả những gì struct tmchứa, vui lòng giải thích cách lưu trữ thông tin múi giờ.

Nhân tiện, mặc dù mã demo là trong C ++, tôi đoán câu hỏi này thực chất cũng là một câu hỏi C hợp pháp.


2
tmkhông chứa thông tin múi giờ. strftimecó được múi giờ bởi voodoo đằng sau hậu trường. Nếu bạn muốn có được múi giờ nói chung, đó là một chút lộn xộn. ( Hiện tại ) không có cách tiêu chuẩn để có được múi giờ. May mắn thay Howard Hinnant là công việc đó ... .
dùng4581602

Cảm ơn bạn @ user4581602 Điều này một phần trả lời câu hỏi của tôi. Nhưng tôi vẫn có những câu hỏi tiếp theo: đưa ra tất cả thông tin được lưu trữ tm, làm thế nào để strftimebiết trả lời theo những cách khác nhau cho hai struct tmđối tượng? Trừ khi tmcó chứa một số thông tin như này tmđược tạo ra bởilocaltime , tmđược tạo ra bởigmtime .
aafulei

Cấu tmtrúc không lưu trữ các múi giờ, điều gì khiến bạn nghĩ nó như vậy? Sự khác biệt là trong các cuộc gọi đến gmtime()localtime().
Ulrich Eckhardt

Trang Man bao gồm cách thu thập thông tin múi giờ trên hệ thống POSIX. Vẫn đang tìm cách strftimenói với những kẻ hút nhau. Nên thêm rằng POSIX để lại những gì xảy ra không xác định.
dùng4581602

1
Thất bại hoàn toàn với gcc 9.2.0 từ MSYS2 trong Windows. Nhìn thấy điều đó nhắc nhở tôi rằng tôi đã thấy những người không chuẩn tmcó thêm thông tin. Đây là một . Lưu ý các const char *tm_zonethành viên. Bạn đang biên dịch cho nền tảng nào? Hãy xem việc tmtriển khai để xem họ đã mở rộng cấu trúc chưa.
dùng4581602

Câu trả lời:


5

Tiêu chuẩn C cho biết trong 7.27.1 Thành phần thời gian:

Các tmcấu trúc phải có ít nhất các thành viên sau đây, theo thứ tự nào. Các ngữ nghĩa của các thành viên và phạm vi bình thường của họ được thể hiện trong các ý kiến. 318)

int tm_sec;    // seconds after the minute — [0, 60]
int tm_min;    // minutes after the hour — [0, 59]
int tm_hour;   // hours since midnight — [0, 23]
int tm_mday;   // day of the month — [1, 31]
int tm_mon;    // months since January — [0, 11]
int tm_year;   // years since 1900
int tm_wday;   // days since Sunday — [0, 6]
int tm_yday;   // days since January 1 — [0, 365]
int tm_isdst;  // Daylight Saving Time flag

(nhấn mạnh là của tôi)

Đó là, việc triển khai được phép thêm thành viên bổ sung vào tm, như bạn đã tìm thấy glibc/time/bits/types/struct_tm.h. Thông số POSIX có từ ngữ gần như giống hệt nhau.

Kết quả là %Z(hoặc thậm chí %z) không thể được coi là di động strftime. Thông số kỹ thuật %Zphản ánh điều này:

%Zđược thay thế bằng tên múi giờ hoặc tên viết tắt của miền địa phương hoặc không có ký tự nếu không xác định được múi giờ. [tm_isdst]

Đó là, các nhà cung cấp được phép giơ tay và nói đơn giản: "không có múi giờ nào có thể xác định được, vì vậy tôi không xuất ra bất kỳ ký tự nào cả."

Ý kiến ​​của tôi: API thời gian C là một mớ hỗn độn.


Tôi đang cố gắng cải thiện mọi thứ cho tiêu chuẩn C ++ 20 sắp tới trong <chrono>thư viện.

Thông số C ++ 20 thay đổi điều này từ "không có ký tự" thành một ngoại lệ được ném nếu time_zonekhông có chữ viết tắt:

http://eel.is/c++draft/time.format#3

Trừ khi được yêu cầu rõ ràng, kết quả của việc định dạng loại chrono không chứa thông tin viết tắt múi giờ và thông tin bù múi giờ. Nếu thông tin có sẵn, các chỉ định chuyển đổi %Z%zsẽ định dạng thông tin này (tương ứng). [ Lưu ý: Nếu thông tin không có sẵn và một %Zhoặc %z chỉ định chuyển đổi xuất hiện trong chrono-format-spec , một ngoại lệ của loại format_­errorđược ném, như được mô tả ở trên. - lưu ý cuối ]

Ngoại trừ đoạn trên không mô tả C strftime, nhưng một formatchức năng mới hoạt động trên std::chronocác loại, không tm. Ngoài ra, có một loại mới: std::chrono::zoned_time( http://eel.is/c++draft/time.zone.zencedtime ) luôn có sẵn time_zonechữ viết tắt (và offset) và có thể được định dạng bằng formatchức năng được đề cập ở trên .

Mã ví dụ:

#include <chrono>
#include <iostream>

int
main()
{
    using namespace std;
    using namespace std::chrono;
    auto now = system_clock::now();
    std::cout << format("%Z\n", zoned_time{current_zone(), now});   // HKT (or whatever)
    std::cout << format("%Z\n", zoned_time{"Asia/Hong_Kong", now}); // HKT or HKST
    std::cout << format("%Z\n", zoned_time{"Etc/UTC", now});        // UTC
    std::cout << format("%Z\n", now);                               // UTC
}

(Tuyên bố miễn trừ trách nhiệm: Cú pháp cuối cùng của chuỗi định dạng trong formathàm có thể hơi khác nhau, nhưng chức năng sẽ ở đó.)

Nếu bạn muốn thử nghiệm bản xem trước của thư viện này, nó là nguồn mở và miễn phí tại đây: https://github.com/HowardHinnant/date

Một số cài đặt được yêu cầu: https://howardhinnant.github.io/date/tz.html#Installation

Trong bản xem trước này, bạn sẽ cần sử dụng tiêu đề "date/tz.h"và nội dung của thư viện nằm trong namespace datethay vì namespace std::chrono.

Thư viện xem trước có thể được sử dụng với C ++ 11 trở lên.

zoned_timeđược tạo mẫu trên std::chrono::durationđó xác định độ chính xác của điểm thời gian và được suy ra trong mã ví dụ ở trên bằng tính năng CTAD của C ++ 17 . Nếu bạn đang sử dụng thư viện xem trước này trong C ++ 11 hoặc C ++ 14, cú pháp sẽ trông giống như:

cout << format("%Z\n", zoned_time<system_clock::duration>{current_zone(), now});

Hoặc có một chức năng nhà máy trợ giúp không được đề xuất cho tiêu chuẩn hóa sẽ thực hiện việc khấu trừ cho bạn:

cout << format("%Z\n", make_zoned(current_zone(), now));

(#CTAD_ Friutions_factory_fifts)


2

Cảm ơn tất cả các ý kiến ​​cho câu hỏi giúp chỉ ra đúng hướng. Tôi đăng một số nghiên cứu của riêng tôi dưới đây. Tôi nói dựa trên một kho lưu trữ của Thư viện GNU C mà tôi tìm thấy trên GitHub. Phiên bản của nó là 2.28.9000.

Trong glibc/time/bits/types/struct_tm.hđó có

struct tm
{
  int tm_sec;           /* Seconds. [0-60] (1 leap second) */
  int tm_min;           /* Minutes. [0-59] */
  int tm_hour;          /* Hours.   [0-23] */
  int tm_mday;          /* Day.     [1-31] */
  int tm_mon;           /* Month.   [0-11] */
  int tm_year;          /* Year - 1900.  */
  int tm_wday;          /* Day of week. [0-6] */
  int tm_yday;          /* Days in year.[0-365] */
  int tm_isdst;         /* DST.     [-1/0/1]*/

# ifdef __USE_MISC
  long int tm_gmtoff;       /* Seconds east of UTC.  */
  const char *tm_zone;      /* Timezone abbreviation.  */
# else
  long int __tm_gmtoff;     /* Seconds east of UTC.  */
  const char *__tm_zone;    /* Timezone abbreviation.  */
# endif
};

Có vẻ như struct tmlưu trữ thông tin múi giờ, ít nhất là trong việc thực hiện này.


1

Một trong những lý do khiến việc lập trình ngày và giờ trở nên khó khăn là về cơ bản, nó ít nhất là một vấn đề khó khăn: "Ba mươi ngày tháng chín", và số học tình dục , múi giờ, và tiết kiệm thời gian ban ngày, và năm nhuận, và thậm chí không nói về giây nhuận.

Nhưng lý do khác khiến nó khó khăn là tất cả quá nhiều thư viện và ngôn ngữ tạo nên một mớ hỗn độn hoàn hảo của nó, và thật không may, cũng không ngoại lệ. (C ++ đang cố gắng làm tốt hơn, như Howard đã đề cập trong câu trả lời của mình.)

Mặc dù mọi người đều biết các biến toàn cục là Xấu, các hàm ngày / giờ của C về cơ bản sử dụng một vài trong số chúng. Trên thực tế, khái niệm "múi giờ hiện tại của hệ thống này" là một biến toàn cục và dữ liệu toàn cầu mô tả múi giờ đó được chia sẻ giữa localtimestrftimevà một số chức năng khác.

Vì vậy, strftimecó thể điền %z%Zdựa trên dữ liệu toàn cầu đó, ngay cả khi nó không được truyền vào như một phần của struct tmgiá trị.

Đó rõ ràng là một sự sắp xếp dưới mức tối ưu, và nó sẽ bắt đầu gây ra vấn đề thực sự nếu có một cách để chương trình thay đổi linh hoạt múi giờ mà nó muốn sử dụng cho localtimephần còn lại. (Và sự sắp xếp này vẫn tồn tại một phần vì thực sự không phải là một cách tiêu chuẩn, di động, tốt để chương trình thay đổi múi giờ địa phương mà nó sử dụng.)

Trong những năm qua, đã có nhiều nỗ lực nửa vời khác nhau để dọn dẹp một số mớ hỗn độn (tất nhiên vẫn giữ được tính tương thích ngược). Một trong những nỗ lực đó liên quan đến các phần mở rộng tm_gmtofftm_zonecác trường bạn đã khám phá trong các phiên bản của một số hệ thống struct tm. Những bổ sung đó là một cải tiến lớn - tôi không thể tưởng tượng được việc lập trình ngày / giờ nghiêm túc trên một hệ thống mà không có chúng - nhưng chúng vẫn không phải là Tiêu chuẩn, và vẫn còn nhiều hệ thống không có chúng (thậm chí không có với cách viết "ẩn" __tm_gmtoff__tm_zone).

Bạn có thể đọc nhiều hơn về lịch sử bẩn thỉu của hỗ trợ ngày / giờ trong C trong bài viết này: Thời gian, Đồng hồ và Lập trình lịch trong C , của Eric Raymond.

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.