2015-11-27 17 views

Trả lời

22

Để chuyển đổi giữa một số Julian datestd::chrono::system_clock::time_point điều đầu tiên cần làm là tìm ra sự khác biệt giữa các kỷ nguyên.

system_clock không có kỷ nguyên chính thức, nhưng kỷ nguyên tiêu chuẩn thực tế là 1970-01-01 00:00:00 UTC (lịch Gregorian). Để thuận tiện, bạn có thể nêu rõ kỷ nguyên Julian date theo số proleptic Gregorian calendar. Lịch này mở rộng quy tắc hiện tại ngược lại và bao gồm một năm 0. Điều này làm cho số học dễ dàng hơn, nhưng người ta phải cẩn thận để chuyển đổi năm BC thành năm âm bằng cách trừ 1 và phủ nhận (ví dụ: 2BC là năm -1). Biên tập Julian date là -4713-11-24 12:00:00 UTC (nói gần).

Thư viện <chrono> có thể thuận tiện xử lý các đơn vị thời gian trên thang đo này. Ngoài ra, this date library có thể chuyển đổi thuận tiện giữa các ngày Gregorian và system_clock::time_point. Để tìm sự khác biệt giữa hai thời đại này chỉ đơn giản là:

constexpr 
auto 
jdiff() 
{ 
    using namespace date; 
    using namespace std::chrono_literals; 
    return sys_days{jan/1/1970} - (sys_days{nov/24/-4713} + 12h); 
} 

Điều này trả về một std::chrono::duration với một khoảng thời gian. Trong C++ 14, điều này có thể là constexpr và chúng tôi có thể sử dụng thời lượng chrono theo thứ tự 12h thay vì std::chrono::hours{12}.

Nếu bạn không muốn sử dụng date library, đây chỉ là một hằng số giờ và có thể được viết lại để hình thành khó hiểu hơn này:

constexpr 
auto 
jdiff() 
{ 
    using namespace std::chrono_literals; 
    return 58574100h; 
} 

Dù bằng cách nào bạn viết nó, hiệu quả là giống hệt nhau . Đây chỉ là một hàm trả về hằng số 58574100. Đây cũng có thể là constexpr toàn cầu, nhưng sau đó bạn phải rò rỉ các khai báo sử dụng của bạn hoặc quyết định không sử dụng chúng.

Tiếp theo, bạn có thể tạo đồng hồ ngày Julian (jdate_clock). Vì chúng ta cần phải xử lý các đơn vị ít nhất là nửa ngày, và phổ biến để biểu thị ngày tháng julian là ngày trôi nổi, tôi sẽ làm cho số ngày jdate_clock::time_point từ ngày kỷ nguyên:

struct jdate_clock 
{ 
    using rep  = double; 
    using period  = std::ratio<86400>; 
    using duration = std::chrono::duration<rep, period>; 
    using time_point = std::chrono::time_point<jdate_clock>; 

    static constexpr bool is_steady = false; 

    static time_point now() noexcept 
    { 
     using namespace std::chrono; 
     return time_point{duration{system_clock::now().time_since_epoch()} + jdiff()}; 
    } 
}; 

Thực hiện lưu ý:

tôi chuyển sự trở lại system_clock::now()-duration ngay lập tức để tránh tràn đối với những hệ thống mà system_clock::duration là nano giây.

jdate_clock hiện là đồng hồ hoàn toàn phù hợp và đầy đủ chức năng <chrono>. Ví dụ tôi có thể tìm ra thời gian những gì nó bây giờ là với:

std::cout << std::fixed; 
std::cout << jdate_clock::now().time_since_epoch().count() << '\n'; 

mà chỉ ra:

2457354.310832 

Đây là một hệ thống kiểu an toàn ở chỗ jdate_clock::time_pointsystem_clock::time_point hai loại khác nhau mà người ta có thể không vô tình thực hiện số học hỗn hợp trong.Tuy nhiên, bạn vẫn có thể nhận được tất cả các lợi ích phong phú từ thư viện <chrono>, chẳng hạn như cộng và trừ khoảng thời gian đến/từ số jdate_clock::time_point của bạn.

using namespace std::chrono_literals; 
auto jnow = jdate_clock::now(); 
auto jpm = jnow + 1min; 
auto jph = jnow + 1h; 
auto tomorrow = jnow + 24h; 
auto diff = tomorrow - jnow; 
assert(diff == 24h); 

Nhưng nếu tôi vô tình nói:

auto tomorrow = system_clock::now() + 24h; 
auto diff = tomorrow - jnow; 

tôi sẽ nhận được một lỗi như thế này:

error: invalid operands to binary expression 
    ('std::chrono::time_point<std::chrono::system_clock, std::chrono::duration<long long, 
    std::ratio<1, 1000000> > >' and 'std::chrono::time_point<jdate_clock, std::chrono::duration<double, 
    std::ratio<86400, 1> > >') 
auto diff = tomorrow - jnow; 
      ~~~~~~~~^~~~~ 

Trong tiếng Anh: Bạn không có thể trừ một jdate_clock::time_point từ một std::chrono::system_clock::time_point.

Nhưng đôi khi tôi làm muốn chuyển đổi jdate_clock::time_point thành system_clock::time_point hoặc ngược lại. Cho rằng người ta có thể dễ dàng viết một vài chức năng helper:

template <class Duration> 
constexpr 
auto 
sys_to_jdate(std::chrono::time_point<std::chrono::system_clock, Duration> tp) noexcept 
{ 
    using namespace std::chrono; 
    static_assert(jdate_clock::duration{jdiff()} < Duration::max(), 
        "Overflow in sys_to_jdate"); 
    const auto d = tp.time_since_epoch() + jdiff(); 
    return time_point<jdate_clock, decltype(d)>{d}; 
} 

template <class Duration> 
constexpr 
auto 
jdate_to_sys(std::chrono::time_point<jdate_clock, Duration> tp) noexcept 
{ 
    using namespace std::chrono; 
    static_assert(jdate_clock::duration{-jdiff()} > Duration::min(), 
        "Overflow in jdate_to_sys"); 
    const auto d = tp.time_since_epoch() - jdiff(); 
    return time_point<system_clock, decltype(d)>{d}; 
} 

Thực hiện lưu ý:

Tôi đã thêm kiểm tra phạm vi tĩnh mà có khả năng bắn nếu bạn sử dụng nano giây hoặc một phút 32bit-based như một khoảng thời gian trong nguồn của bạn time_point.

Công thức chung là để có được những duration kể từ khi kỷ nguyên (duration s là "đồng hồ trung lập"), thêm hoặc trừ đi bù đắp giữa các thời kỳ, và sau đó chuyển đổi duration vào mong muốn time_point.

Chúng sẽ chuyển đổi giữa hai đồng hồ của time_point s sử dụng bất kỳ chính xác nào, tất cả đều an toàn kiểu. Nếu nó biên dịch, nó hoạt động. Nếu bạn đã thực hiện một lỗi lập trình, nó xuất hiện tại thời gian biên dịch. Có giá trị sử dụng ví dụ bao gồm:

auto tp = sys_to_jdate(system_clock::now()); 

tp là một jdate::time_point ngoại trừ việc nó có đại diện không thể thiếu với độ chính xác của bất cứ điều gì system_clock::duration của bạn là (đối với tôi đó là micro). Hãy cảnh báo rằng nếu nó là nano giây cho bạn (gcc), điều này sẽ tràn ra vì nano giây chỉ có khoảng +/- 292 năm.

Bạn có thể buộc chính xác như vậy:

auto tp = sys_to_jdate(time_point_cast<hours>(system_clock::now())); 

Và bây giờ tp là một số không thể thiếu trong giờ kể từ khi jdate thời đại.

Nếu bạn sẵn sàng sử dụng this date library, người ta có thể dễ dàng sử dụng các tiện ích ở trên để chuyển đổi ngày tháng julian nổi thành ngày Gregorian, với bất kỳ độ chính xác nào bạn muốn. Ví dụ:

using namespace std::chrono; 
using namespace date; 
std::cout << std::fixed; 
auto jtp = jdate_clock::time_point{jdate_clock::duration{2457354.310832}}; 
auto tp = floor<seconds>(jdate_to_sys(jtp)); 
std::cout << "Julian date " << jtp.time_since_epoch().count() 
      << " is " << tp << " UTC\n"; 

Chúng tôi sử dụng jdate_clock để tạo jdate_clock::time_point. Sau đó, chúng tôi sử dụng hàm chuyển đổi jdate_to_sys của chúng tôi để chuyển đổi jtp thành một số system_clock::time_point. Điều này sẽ có một đại diện của đôi và một khoảng thời gian giờ. Điều đó không thực sự quan trọng. Điều quan trọng là chuyển đổi nó thành bất kỳ đại diện và độ chính xác nào mà bạn muốn. Tôi đã thực hiện điều đó ở trên với floor<seconds>.Tôi cũng có thể đã sử dụng time_point_cast<seconds> và nó sẽ làm điều tương tự. floor xuất phát từ the date library, luôn cắt ngắn theo hướng vô cùng tiêu cực và dễ đánh vần hơn.

Sản lượng này sẽ:

Julian date 2457354.310832 is 2015-11-27 19:27:35 UTC 

Nếu tôi muốn làm tròn đến thứ hai gần thay vì sàn, mà sẽ chỉ đơn giản là:

auto tp = round<seconds>(jdate_to_sys(jtp)); 
Julian date 2457354.310832 is 2015-11-27 19:27:36 UTC 

Hoặc nếu tôi muốn nó trong vòng miligiây gần nhất:

auto tp = round<milliseconds>(jdate_to_sys(jtp)); 
Julian date 2457354.310832 is 2015-11-27 19:27:35.885 UTC 

cập nhật

Các chức năng floorround được đề cập ở trên như một phần của Howard Hinnant's date library hiện cũng có sẵn trong không gian tên std::chrono như một phần của C++ 17.

+0

+1 cho câu hỏi. Tôi đã không cho câu trả lời vì nó nhớ lại cho tôi một số đặc điểm của tài trợ. Thực tế là bạn cũng cung cấp một phiên bản C++ thuần khiết làm tăng xếp hạng trung bình. Chỉ quan điểm khách quan của tôi. – edmz

+1

@black: Không sao cả. Fwiw, thư viện ngày (tài trợ) là miễn phí, mã nguồn mở, giấy phép MIT, kho lưu trữ github. Và đây là các thuật toán miền công cộng, thư viện ngày dựa trên: http://howardhinnant.github.io/date_algorithms.html (trong trường hợp bạn muốn xây dựng thư viện ngày của riêng bạn). –