2012-06-11 23 views

Trả lời

7

Thời gian được lưu trữ dưới dạng chuỗi nội bộ, ở định dạng YYmmddHHMMSS hoặc YYYYmmddHHMMSS.

Ở cuối chuỗi có chỗ cho phân số giây và múi giờ, nhưng hãy bỏ qua điều đó ngay bây giờ và có một số mã (chưa được kiểm tra).

Note: cũng xem câu trả lời Bryan Olson dưới đây, thảo luận về những hành vi không xác định do sự i++ 's. Ngoài ra xem câu trả lời của Seak mà loại bỏ các hành vi không xác định.

static time_t ASN1_GetTimeT(ASN1_TIME* time) 
{ 
    struct tm t; 
    const char* str = (const char*) time->data; 
    size_t i = 0; 

    memset(&t, 0, sizeof(t)); 

    if (time->type == V_ASN1_UTCTIME) /* two digit year */ 
    { 
     t.tm_year = (str[i++] - '0') * 10 + (str[++i] - '0'); 
     if (t.tm_year < 70) 
     t.tm_year += 100; 
    } 
    else if (time->type == V_ASN1_GENERALIZEDTIME) /* four digit year */ 
    { 
     t.tm_year = (str[i++] - '0') * 1000 + (str[++i] - '0') * 100 + (str[++i] - '0') * 10 + (str[++i] - '0'); 
     t.tm_year -= 1900; 
    } 
    t.tm_mon = ((str[i++] - '0') * 10 + (str[++i] - '0')) - 1; // -1 since January is 0 not 1. 
    t.tm_mday = (str[i++] - '0') * 10 + (str[++i] - '0'); 
    t.tm_hour = (str[i++] - '0') * 10 + (str[++i] - '0'); 
    t.tm_min = (str[i++] - '0') * 10 + (str[++i] - '0'); 
    t.tm_sec = (str[i++] - '0') * 10 + (str[++i] - '0'); 

    /* Note: we did not adjust the time based on time zone information */ 
    return mktime(&t); 
} 
+8

Mã này sai vì nó dựa trên hành vi không xác định có 'i ++' (hoặc '++ i') trong cùng một biểu thức: thứ tự đánh giá là _not_ được bảo đảm. –

1

câu trả lời của Jan chủ yếu làm việc trong tình huống này, tuy nhiên, accumulator i nên luôn sử dụng i++:

static time_t ASN1_GetTimeT(ASN1_TIME* time) 
{ 
    struct tm t; 
    const char* str = (const char*) time->data; 
    size_t i = 0; 

    memset(&t, 0, sizeof(t)); 

    if (time->type == V_ASN1_UTCTIME) /* two digit year */ 
    { 
     t.tm_year = (str[i++] - '0') * 10 + (str[i++] - '0'); 
     if (t.tm_year < 70) 
     t.tm_year += 100; 
    } 
    else if (time->type == V_ASN1_GENERALIZEDTIME) /* four digit year */ 
    { 
     t.tm_year = (str[i++] - '0') * 1000 + (str[i++] - '0') * 100 + (str[i++] - '0') * 10 + (str[i++] - '0'); 
     t.tm_year -= 1900; 
    } 
    t.tm_mon = ((str[i++] - '0') * 10 + (str[i++] - '0')) - 1; // -1 since January is 0 not 1. 
    t.tm_mday = (str[i++] - '0') * 10 + (str[i++] - '0'); 
    t.tm_hour = (str[i++] - '0') * 10 + (str[i++] - '0'); 
    t.tm_min = (str[i++] - '0') * 10 + (str[i++] - '0'); 
    t.tm_sec = (str[i++] - '0') * 10 + (str[i++] - '0'); 

    /* Note: we did not adjust the time based on time zone information */ 
    return mktime(&t); 
} 
+0

i ++ ngụ ý rằng i được tăng lên SAU KHI câu lệnh hoàn tất. Điều này có nghĩa là trong năm, nói năm 2014, kết quả là 322 (2222) bên trong cấu trúc tm, vì vậy câu trả lời đúng là i ++ cho đầu tiên, và ++ i cho mỗi lần tiếp theo. –

+3

Không, mã của bạn là _still_ sai. Bạn chỉ có thể _not_ có nhiều hơn một 'i ++' trong cùng một biểu thức vì thứ tự đánh giá không được đảm bảo (từ trái qua phải so với từ phải sang trái). –

3

tôi phải đồng ý với Jan và Jack. Có người thực sự đã sao chép và sử dụng mã đã cho nơi tôi làm việc, và nó không thành công. Dưới đây là lý do tại sao, từ tiêu chuẩn C99:

Giữa điểm chuỗi trước và bên cạnh một đối tượng có trách nhiệm đã giá trị được lưu trữ của nó biến đổi cùng lúc nhiều nhất bởi việc đánh giá của một khái niệm " - ISO/IEC 9899.: Năm 1999, "Ngôn ngữ lập trình - C", mục 6.5, khoản 1.

Khi biên dịch mã nhất định, gcc (phiên bản 4.1.2) cho biết, chín lần,

cảnh báo: hoạt động trên ' tôi có thể không xác định ed2

Mã có hành vi không xác định. Lỗi mà tôi thực sự thấy là năm "13" được đọc là 11. Đó là vì:

Kết quả của toán tử postfix ++ là giá trị của toán hạng. Sau khi thu được kết quả, giá trị của toán hạng được tăng lên. [...] Tác dụng phụ của việc cập nhật giá trị được lưu trữ của toán hạng phải xảy ra giữa điểm trước đó và điểm tiếp theo. - Ibid, mục 6.5.2.4, khoản 2.

Cả hai trường hợp của str [i ++] trong:

t.tm_year = (str [i ++] - '0') * 10 + (str [i + +] - '0');

đọc '1' trong "13", vì cả hai đều xảy ra trước khi cập nhật i. Tất cả các dòng cập nhật i nhiều lần đều có cùng một vấn đề.

Sửa lỗi dễ dàng là loại bỏ 'i' và thay thế tất cả các dòng đó bằng một lệnh gọi tới sscanf().

Ngay cả khi sửa chữa, tôi sẽ không thích mã.Ngoài việc bỏ qua hậu tố múi giờ, nó không kiểm tra lỗi hoặc các giá trị không mong muốn. Chứng chỉ là một cơ chế bảo mật và mã bảo mật có các yêu cầu nghiêm ngặt đối với sự vững mạnh. Các trường hợp góc mà chương trình của bạn không xử lý chính xác là những kẻ tấn công của bạn điền vào nguồn cấp dữ liệu đó.

+0

* "Sửa lỗi dễ dàng là loại bỏ 'i' và thay thế tất cả các dòng đó bằng một lệnh gọi tới sscanf()" * - có lẽ bạn nên cung cấp ví dụ vì nó dễ sử dụng 'sscanf' không chính xác. Không có nghĩa là nó kinh doanh một lỗi cho người khác. – jww

6

Vâng, tôi không biết phần còn lại, nhưng mã đó chỉ là sai đối với các trường hợp ASN1_TIME ở định dạng UTCTime: YYMMDDHHMMSSZ.

Tôi đã thử và trả về giá trị sai, ngay cả với hiệu chỉnh từ ++ i sang i ++, tuy nhiên ... mã không phải là một ví dụ về mã hóa tốt.

tôi quản lý để sửa chữa nó, đó là số tiền của các loại char:

static time_t ASN1_GetTimeT(ASN1_TIME* time){ 
    struct tm t; 
    const char* str = (const char*) time->data; 
    size_t i = 0; 

    memset(&t, 0, sizeof(t)); 

    if (time->type == V_ASN1_UTCTIME) {/* two digit year */ 
     t.tm_year = (str[i++] - '0') * 10; 
     t.tm_year += (str[i++] - '0'); 
     if (t.tm_year < 70) 
      t.tm_year += 100; 
    } else if (time->type == V_ASN1_GENERALIZEDTIME) {/* four digit year */ 
     t.tm_year = (str[i++] - '0') * 1000; 
     t.tm_year+= (str[i++] - '0') * 100; 
     t.tm_year+= (str[i++] - '0') * 10; 
     t.tm_year+= (str[i++] - '0'); 
     t.tm_year -= 1900; 
    } 
    t.tm_mon = (str[i++] - '0') * 10; 
    t.tm_mon += (str[i++] - '0') - 1; // -1 since January is 0 not 1. 
    t.tm_mday = (str[i++] - '0') * 10; 
    t.tm_mday+= (str[i++] - '0'); 
    t.tm_hour = (str[i++] - '0') * 10; 
    t.tm_hour+= (str[i++] - '0'); 
    t.tm_min = (str[i++] - '0') * 10; 
    t.tm_min += (str[i++] - '0'); 
    t.tm_sec = (str[i++] - '0') * 10; 
    t.tm_sec += (str[i++] - '0'); 

    /* Note: we did not adjust the time based on time zone information */ 
    return mktime(&t); 
} 
+0

rfc 5280 nói rằng 1- thời gian đầu vào là trong UTC và do đó 'mktime()' có thể trả về một kết quả sai ở đây ('mktime()' hy vọng thời gian đầu vào trong múi giờ địa phương). 2- 'YY> = 50' sẽ được hiểu là' 19YY' 3- '99991231235959Z' là một giá trị đặc biệt. Dưới đây là [ví dụ về cách các vấn đề này có thể được khắc phục] (https://stackoverflow.com/a/47015958/4279). – jfs

4

Từ mã openssl, nó có vẻ là một ý tưởng tồi:

/* 
* FIXME: mktime assumes the current timezone 
* instead of UTC, and unless we rewrite OpenSSL 
* in Lisp we cannot locally change the timezone 
* without possibly interfering with other parts 
* of the program. timegm, which uses UTC, is 
* non-standard. 
* Also time_t is inappropriate for general 
* UTC times because it may a 32 bit type. 
*/ 

Lưu ý rằng bạn có thể sử dụng ASN1_TIME_diff() để nhận số ngày/giây giữa hai ASN1_TIME *. Nếu bạn vượt qua NULL như ASN1_TIME * từ, bạn có thể nhận được sự khác biệt từ thời gian hiện tại.

1

time_t có thể có phạm vi hẹp hơn ASN1_TIME và do đó chức năng ASN1_TIME_* có thể là lựa chọn thay thế mạnh mẽ hơn. Ví dụ: để so sánh thời gian, bạn có thể sử dụng ASN1_TIME_diff() (điều này tránh các vấn đề bảo mật có thể xảy ra với tràn nếu sử dụng time_t). Để in trong một định dạng có thể đọc được con người, gọi ASN1_TIME_print() vv

Cho đến nay không ai trong số các câu trả lời theo rfc 5280 mà xác định rằng lần đầu vào đang UTC (mktime() hy vọng thời gian trong tức là múi giờ địa phương, câu trả lời là không đúng nếu múi giờ địa phương không phải là UTC). Also:

hệ thống Phù hợp PHẢI giải thích các lĩnh vực năm (YY) như sau: đâu YY là lớn hơn hoặc bằng 50, năm SẼ được hiểu là 19YY; và Trong đó YY nhỏ hơn 50, năm S SH được diễn giải là 20YY.

tức là, if (tm_year < 70) tm_year += 100; vi phạm rfc. Câu trả lời này sử dụng year += year < 50 ? 2000 : 1900.

Ngoài ra, 99991231235959Z in the input có nghĩa là chứng chỉ không có ngày hết hạn được xác định rõ ràng (hàm sẽ trả lại (time_t)-1 - lỗi).

Để chuyển đổi UTCTime hoặc GeneralizedTime dây (ASN1_TIME*) để seconds since Epoch (time_t):

typedef unsigned U; 

time_t ASN1_TIME_to_posix_time(const ASN1_TIME* time) { 
    if(!time) return -1; 
    const char *s = (const char*)time->data; 
    if (!s) return -1; 

    U two_digits_to_uint() // nested function: gcc extension 
    { 
    U n = 10 * (*s++ - '0'); 
    return n + (*s++ - '0'); 
    } 
    U year, month, day, hour, min, sec; 
    switch(time->type) { 
    // https://tools.ietf.org/html/rfc5280#section-4.1.2.5.1 
    case V_ASN1_UTCTIME: // YYMMDDHHMMSSZ 
    year = two_digits_to_uint(); 
    year += year < 50 ? 2000 : 1900; 
    break; 
    case V_ASN1_GENERALIZEDTIME: // YYYYMMDDHHMMSSZ 
    year = 100 * two_digits_to_uint(); 
    year += two_digits_to_uint(); 
    break; 
    default: 
    return -1; // error 
    } 
    month = two_digits_to_uint(); 
    day = two_digits_to_uint(); 
    hour = two_digits_to_uint(); 
    min = two_digits_to_uint(); 
    sec = two_digits_to_uint(); 
    if (*s != 'Z') return -1; 
    if (year == 9999 && month == 12 && day == 31 && hour == 23 && min == 59 
     && sec == 59) // 99991231235959Z rfc 5280 
    return -1; 
    return posix_time(year, month, day, hour, min, sec); 
} 

nơi posix_time() được sử dụng để chuyển đổi gãy xuống thời gian tính theo giờ UTC đến thời điểm lịch. Seconds Since the Epoch:

time_t posix_time(U year, U month, U day, U hour, U min, U sec) 
{ 
    if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 
     || hour > 23 || min > 59 || sec > 60) 
    return -1; 

    // days upto months for non-leap years 
    static const U month_day[13] = 
    {-1, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; 
    year -= 1900; 
    // number of Februaries since 1900 
    const U year_for_leap = (month > 2) ? year + 1 : year; 
    // XXX may overflow 
    return sec + min*60 + hour*3600 + (month_day[month] + day - 1)*86400 + 
    (year-70)*31536000 + ((year_for_leap-69)/4)*86400 - 
    ((year_for_leap-1)/100)*86400 + ((year_for_leap+299)/400)*86400; 
} 

month_dayyear_for_leap là từ @DTiedy's answer.

Các vấn đề liên quan