2015-11-27 19 views

答えて

22

Julian datestd::chrono::system_clock::time_pointの間で変換するには、まず最初にエポックの違いを調べる必要があります。

system_clockには公式エポックはありませんが、事実上の標準エポックは1970-01-01 00:00:00 UTC(グレゴリオ暦)です。便宜上、proleptic Gregorian calendarの点でJulian dateエポックを記述すると便利です。このカレンダーは、現在のルールを後方に拡張し、年0を含みます。これは算術を簡単にしますが、1を減算して無効にすることでBC年を負の年に変換する必要があります(2BCは年-1など)。 Julian dateエポックは、-4713-11-24 12:00:00 UTC(おおよそのこと)です。

<chrono>ライブラリは、このスケールで時間単位を扱うのに便利です。さらに、this date libraryは、グレゴリオ暦の日付とsystem_clock::time_pointとの間の変換に便利です。これら二つのエポックの差を検索するには、単純です:

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

これは時間の周期でstd::chrono::durationを返します。 C++ 14では、これはconstexprとなり、std::chrono::hours{12}ではなく、クロノ継続時間リテラル12hを使用できます。

あなたはdate libraryを使用したくない場合は、これは時間のちょうど一定の数であり、この多くの不可解な形に書き換えることができます。

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

あなたはそれを書くいずれかの方法で、効率が同じです。これは、定数58574100を返す関数に過ぎません。これはまたconstexprグローバルかもしれませんが、使用する宣言を漏らしたり、それらを使用しないことを決めなければなりません。

次は、ユリウス日時計(jdate_clock)を作成すると便利です。私たちは少なくとも半日ほどの単位で対処する必要があり、ユリウス日を浮動小数点日として表現するのが一般的ですから、jdate_clock::time_pointはエポックから2倍の日数をカウントします:

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()}; 
    } 
}; 

実装上の注意:

私はsystem_clock::durationはナノ秒であるそれらのシステムのためのオーバーフローを避けるために、すぐにsystem_clock::now()からdurationへの復帰を変換します。

jdate_clockは完全に適合し、完全に機能するクロックである<chrono>になりました。これは、そのjdate_clock::time_pointでタイプセーフなシステムであり、system_clock::time_pointはどちらができる2つの別個のタイプです

2457354.310832 

:これだけの出力

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

:たとえば、私はそれをして、今あるものを時間を見つけることができます誤って混在算術を実行することはありません。それでも、jdate_clock::time_pointへの/からの継続時間の加算や減算など、<chrono>ライブラリの豊富な利点をすべて引き続き得ることができます。

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); 

しかし、私は誤って言った場合:

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

私はこのようなエラーが出るでしょう:英語で

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; 
      ~~~~~~~~^~~~~ 

を:あなたはstd::chrono::system_clock::time_pointからjdate_clock::time_pointを減算することはできません。

しかし、時には私がjdate_clock::time_pointsystem_clock::time_pointにまたはその逆に変換します。

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}; 
} 

実装上の注意:1つは簡単にヘルパー関数のカップルを書くことができますについて

私はあなたがナノ秒または32ビットベースの分を使用した場合に発生する可能性がある静的な範囲チェックを追加しましたあなたのソースの期間としてtime_point

一般的なレシピは、エポックdurationを得ることである(duration sは「クロックニュートラル」)、追加またはエポックの間のオフセットを減算し、次に所望time_pointdurationを変換します。

これらは、すべてのタイプセーフな方法で、にどんな精度を使用して、2つのクロックのtime_point sの間で変換します。それがコンパイルされていれば動作します。プログラミングエラーが発生した場合は、コンパイル時に表示されます。有効な例の用途は、次のとおりです

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

tpは、それが何であれ、あなたのsystem_clock::durationの精度で積分表示を持っていることを除いてjdate::time_pointです(私にとってはそれがマイクロ秒である)です。それがナノ秒(gcc)の場合、ナノ秒は+/- 292年の範囲しかないので、これはオーバーフローすることに注意してください。

あなたはそうのような精度を強制することができます。

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

そして今tpjdateエポックからの時間の整数カウントです。

this date libraryを使用したい場合は、上のユーティリティを使用して、浮動小数点ジュリアン日付をグレゴリオ暦の日付に簡単に変換できます。たとえば:

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"; 

我々はjdate_clock::time_pointを作成するために、私たちのjdate_clockを使用しています。次にjdate_to_sys変換関数を使用してjtpsystem_clock::time_pointに変換します。これには、倍数と時間の表現があります。それは本当に重要ではありません。どのような重要ですどのような表現と精度に変換することですが必要です。私はfloor<seconds>でこれを行いました。私もtime_point_cast<seconds>を使っていた可能性があり、同じことをしていたでしょう。 floorthe date libraryに由来し、常に負の無限大に切り詰められ、綴りが容易です。

この意志出力:私ではなく、床の最も近い秒に丸めたい場合

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

、それは単純に次のようになります。

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

それとも私が最も近いミリ秒にそれを望んでいた場合:

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

更新

Howard Hinnant's date libraryの一部として前述のfloorroundの関数は、C++ 17の一部としてネームスペースstd::chronoでも利用できるようになりました。

+0

+1に質問してください。私はスポンサーシップのいくつかの特徴を私に思い出させるので、答えはしていない。あなたが純粋なC++バージョンを提供しているという事実は、平均格付けをも引き上げています。ただ私の客観的な見解です。 – edmz

+1

@black:問題ありません。 Fwiw、日付ライブラリ(スポンサーシップ)は無料のオープンソース、MITライセンス、githubリポジトリです。次に、日付ライブラリが基づいているパブリックドメインアルゴリズムhttp://howardhinnant.github.io/date_algorithms.html(独自の日付ライブラリを作成する場合)を示します。 –

関連する問題