在C ++ 11/14中处理Julian日期 [英] Handling Julian dates in C++11/14

查看:254
本文介绍了在C ++ 11/14中处理Julian日期的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在C ++中处理 Julian日期的最佳/最简单的方法是什么?我想要能够在朱利安日期和格里高利日期之间进行转换。我有C ++ 11和C ++ 14。 < chrono> 库可以帮助解决这个问题吗?

解决方案

要在 Julian日期 std :: chrono :: system_clock之间进行转换,请执行以下操作:



system_clock

code>没有官方纪​​元,但事实上的标准时期是1970-01-01 00:00:00 UTC(公历)。为方便起见,您可以根据朱利安日期纪元, https://en.wikipedia.org/wiki/Proleptic_Gregorian_calendarrel =nofollow> proleptic Gregorian calendar 。这个日历将当前规则向后延伸,并且包括0年。这使得算术更容易,但是必须注意通过减去1和否定(例如,2BC是年-1)将BC年份转换为负年数。 朱利安日期纪元是-4713-11-24 12:00:00 UTC(粗略地说)



< chrono> 库可以方便地处理此刻度上的时间单位。此外,此日期库可以方便地在公历日期和 system_clock之间进行转换:: time_point 。要找到这两个时期之间的区别是简单的:

  constexpr 
auto
jdiff $ b {
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 ,我们可以使用计时持续时间文本 12h ,而不是 std :: chrono :: hours {12}



如果您不想使用日期库,这只是一个固定的小时数,可以重写为更加隐秘的形式:

  constexpr 
auto
jdiff()
{
使用命名空间std :: chrono_literals;
return 58574100h;
}

无论你写的是什么,效率都是一样的。这只是一个返回常量 58574100 的函数。这也可能是一个 constexpr 全局,但是你必须泄露你的使用声明,或决定不使用它们。



接下来可以创建一个儒略日期时钟( jdate_clock )。因为我们需要处理至少与半天一样细的单位,并且通常将朱利安日期表示为浮点天,我将使 jdate_clock :: time_point

  struct jdate_clock 
{
使用rep =双;
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
{
使用命名空间std :: chrono;
return time_point {duration {system_clock :: now()。time_since_epoch()} + jdiff()};
}
};

实施说明:


我立即将 system_clock :: now()的返回值转换为 duration ,以避免溢出 system_clock :: duration 是纳秒。


jdate_clock 现在是一个完全符合并且完全运行的< chrono> 时钟。例如,我可以找出它现在的时间:

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

只输出:

  2457354.310832 

这是一个类型安全的系统, c> jdate_clock :: time_point system_clock :: time_point 是两种不同的类型,不能意外执行混合算术。从< chrono> 库中获取所有的丰富的好处,例如添加和减去你的 jdate_clock :: time_point

 使用命名空间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;

我会收到如下错误:

 错误:二进制表达式的操作数无效
('std :: chrono :: time_point< std :: chrono :: system_clock,std :: chrono :: duration< long long,
std :: ratio< 1,1000000>>'和'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 p>

但有时候我想将 jdate_clock :: time_point 转换为 system_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(),
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(),
溢出在jdate_to_sys);
const auto d = tp.time_since_epoch() - jdiff();
return time_point< system_clock,decltype(d)> {d};
}

实施注释:



< blockquote>

我添加了静态范围检查,如果你使用纳秒或基于32位的分钟作为源中的持续时间,可能触发 time_point


一般的配方是从时期开始得到 duration duration 是时钟中性),添加或减去历元之间的偏移,然后将 duration 转换为 time_point 。



这些将在两个时钟的 time_point s使用任何精确度,全部以类型安全的方式。如果它编译,它的工作。如果发生编程错误,它会在编译时显示。有效示例使用include:

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

tp jdate :: time_point ,除了它具有任何你的 system_clock :: duration (对于我是微秒)的精度的整数表示。预先警告,如果它是你的纳秒(gcc),这将溢出为纳秒只有+/- 292年的范围。



您可以强制精度因此:

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

现在 tp jdate 时代。



如果您愿意使用这个日期库,可以很容易地使用上面的实用程序将浮点朱利亚日期转换为格里高利日期,具有任何你想要的精度。例如:

 使用命名空间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<< 儒略日<< jtp.time_since_epoch()。count()
<< is<< tp<< UTC\\\
;

我们使用 jdate_clock code> jdate_clock :: time_point 。然后我们使用 jdate_to_sys 转换函数将 jtp 转换为 system_clock :: time_point 。这将表示双倍和几小时的时间。这不是真的重要,虽然。 重要的是将它转换为您想要的任何表示和精确度。我已经用 floor< seconds> 完成了上面的操作。我也可以使用 time_point_cast< seconds> ,它也会做同样的事情。 floor 来自日期库



这将输出:

  Julian日期2457354.310832是2015-11-27 19:27:35 UTC 

如果我想舍入到最接近的第二个而不是floor,那将是:

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

我想要到最近的毫秒:

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


What is the best/easiest way to deal with Julian dates in C++? I want to be able to convert between Julian dates and Gregorian dates. I have C++11 and C++14. Can the <chrono> library help with this problem?

解决方案

To convert between a Julian date and std::chrono::system_clock::time_point the first thing one needs to do is find out the difference between the epochs.

The system_clock has no official epoch, but the de facto standard epoch is 1970-01-01 00:00:00 UTC (Gregorian calendar). For convenience, it is handy to state the Julian date epoch in terms of the proleptic Gregorian calendar. This calendar extends the current rules backwards, and includes a year 0. This makes the arithmetic easier, but one has to take care to convert years BC into negative years by subtracting 1 and negating (e.g. 2BC is year -1). The Julian date epoch is -4713-11-24 12:00:00 UTC (roughly speaking).

The <chrono> library can conveniently handle time units on this scale. Additionally, this date library can conveniently convert between Gregorian dates and system_clock::time_point. To find the difference between these two epochs is simply:

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

This returns a std::chrono::duration with a period of hours. In C++14 this can be constexpr and we can use the chrono duration literal 12h instead of std::chrono::hours{12}.

If you don't want to use the date library, this is just a constant number of hours and can be rewritten to this more cryptic form:

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

Either way you write it, the efficiency is identical. This is just a function that returns the constant 58574100. This could also be a constexpr global, but then you have to leak your using declarations, or decide not to use them.

Next it is handy to create a Julian date clock (jdate_clock). Since we need to deal with units at least as fine as a half a day, and it is common to express julian dates as floating point days, I will make the jdate_clock::time_point a count of double-based days from the epoch:

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

Implementation note:

I converted the return from system_clock::now() to duration immediately to avoid overflow for those systems where system_clock::duration is nanoseconds.

jdate_clock is now a fully conforming and fully functioning <chrono> clock. For example I can find out what time it is now with:

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

which just output:

2457354.310832

This is a type-safe system in that jdate_clock::time_point and system_clock::time_point are two distinct types which one can not accidentally perform mixed arithmetic in. And yet you can still get all of the rich benefits from the <chrono> library, such as add and subtract durations to/from your jdate_clock::time_point.

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

But if I accidentally said:

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

I would get an error such as this:

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

In English: You can't subtract a jdate_clock::time_point from a std::chrono::system_clock::time_point.

But sometimes I do want to convert a jdate_clock::time_point to a system_clock::time_point or vice-versa. For that one can easily write a couple of helper functions:

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

Implementation note:

I've added static range checking which is likely to fire if you use nanoseconds or a 32bit-based minute as a duration in your source time_point.

The general recipe is to get the duration since the epoch (durations are "clock neutral"), add or subtract the offset between the epochs, and then convert the duration into the desired time_point.

These will convert among the two clock's time_points using any precision, all in a type-safe manner. If it compiles, it works. If you made a programming error, it shows up at compile time. Valid example uses include:

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

tp is a jdate::time_point except that it has integral representation with the precision of whatever your system_clock::duration is (for me that is microseconds). Be forewarned that if it is nanoseconds for you (gcc), this will overflow as nanoseconds only has a range of +/- 292 years.

You can force the precision like so:

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

And now tp is an integral count of hours since the jdate epoch.

If you are willing to use this date library, one can easily use the utilities above to convert a floating point julian date into a Gregorian date, with any accuracy you want. For example:

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

We use our jdate_clock to create a jdate_clock::time_point. Then we use our jdate_to_sys conversion function to convert jtp into a system_clock::time_point. This will have a representation of double and a period of hours. That isn't really important though. What is important is to convert it into whatever representation and precision you want. I've done that above with floor<seconds>. I also could have used time_point_cast<seconds> and it would have done the same thing. floor comes from the date library, always truncates towards negative infinity, and is easier to spell.

This will output:

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

If I wanted to round to the nearest second instead of floor, that would simply be:

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

Or if I wanted it to the nearest millisecond:

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

这篇关于在C ++ 11/14中处理Julian日期的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆