简体   繁体   English

boost :: locale :: date_time的陷阱

[英]Pitfalls of boost::locale::date_time

Could you provide the pitfalls you meet in using boost::locale::date_time in boost 1.55 or tell me what's wrong I have made in the following examples? 您能否提供在Boost 1.55中使用boost::locale::date_time遇到的陷阱,或者告诉我在以下示例中我做错了什么? Do I misunderstand boost::locale::date_time API? 我会误解boost::locale::date_time API吗?

day_of_week Example 1: Shift A Date to A Sunday day_of_week示例1:将日期转换为星期日

#include <iostream>
#include <boost/locale.hpp>

int main() {
  using namespace boost::locale;
  generator gen;
  std::locale locale = gen("en_US.UTF-8");
  std::locale::global(locale);
  std::cout.imbue(locale);

  date_time_period_set s;
  s.add(period::year(2013));
  s.add(period::month(2));
  s.add(period::day(5));
  s.add(period::hour(9));
  s.add(period::minute(0));
  s.add(period::second(0));

  // 2013-03-05 is a Tuesday, let's shift it to a Sunday
  s.add(period::day_of_week(1));

  date_time now(s);// why it's not 2013-03-02 or 2013-03-10?
  std::cout << now << std::endl;
}

Output: Mar 30, 2014, 9:00:00 AM , why it's not 2013-03-02, 9:00:00 AM or 2013-03-10, 9:00:00 AM ? 输出: Mar 30, 2014, 9:00:00 AM ,为什么不是2013-03-02, 9:00:00 AM2013-03-10, 9:00:00 AM It's even not a Sunday. 甚至不是星期天。

day_of_week Example 2: Last Saturday of March 2013 day_of_week示例2:2013年3月的最后一个星期六

#include <iostream>
#include <boost/locale.hpp>

int main() {
  using namespace boost::locale;
  generator gen;
  std::locale locale = gen("en_US.UTF-8");
  std::locale::global(locale);
  std::cout.imbue(locale);

  date_time_period_set s;
  s.add(period::year(2013));
  s.add(period::month(2));
  s.add(period::hour(9));
  s.add(period::minute(0));
  s.add(period::second(0));


  // swapping the following 2 lines rusults in a wrong date
  s.add(period::day_of_week(7));
  s.add(period::day_of_week_in_month(-1));


  date_time now(s);// last Saturday of March 2013
  std::cout << now << std::endl;
}

It outputs: Mar 30, 2013, 9:00:00 AM 输出: Mar 30, 2013, 9:00:00 AM

But if you change 2 lines marked in the source code, you might see: Apr 5, 2014, 9:00:00 AM 但是,如果您更改了源代码中标记的2行,则可能会看到: Apr 5, 2014, 9:00:00 AM

Where and How to Use date_time_period boost::locale::period::first_day_of_week(int v) ? 在何处以及如何使用date_time_period boost::locale::period::first_day_of_week(int v)

See date_time_period boost::locale::period::first_day_of_week(int v) . 参见date_time_period boost :: locale :: period :: first_day_of_week(int v)

This is not an answer to this question. 这不是这个问题的答案。 I am not well-versed in boost::locale::date_time . 我不太了解boost::locale::date_time However I am responding anyway with a possible alternative. 但是,无论如何,我正在回应一个可能的选择。 This alternative is not as well packaged as boost::date_time , nor near as complete, especially with respect to locales. 这种选择的打包效果不如boost::date_time ,也没有那么完整,尤其是在语言环境方面。 However it might be a viable alternative for you, because your needs look simple, and this alternative is very simple. 但是,对于您来说,这可能是一个可行的选择,因为您的需求看起来很简单,而且这种选择非常简单。

I am speaking about: 我说的是:

chrono-Compatible Low-Level Date Algorithms 时间兼容的低级日期算法

This is nothing but a paper outlining 10 very low-level building blocks for date (not time) computations, such as those in your question. 这只是一篇概述10个非常低级的构建块(而不是时间)计算的论文,例如您所质疑的那些。 The above paper does not present a library you can download. 上述文件没有提供您可以下载的库。 Instead it just lists and explains the 10 algorithms. 相反,它仅列出并解释了10种算法。 The code for each algorithm is so short, it is quite practical to copy/paste into your own code, and modify for your specific needs. 每种算法的代码都很短,将其复制/粘贴到您自己的代码中并根据您的特定需求进行修改非常实用。 The code is in the public domain, so no worries on copyright issues. 该代码位于公共领域,因此无需担心版权问题。

Using 5 of the 10 algorithm for this paper, combined with the following bit of boiler-plate for outputting a date (I've used std::tuple to hold the date, use whatever you like), I've come up with very simple solutions to your simple problems: 本文使用10个算法中的5个,再结合下面的样板来输出日期(我使用std::tuple来保存日期,使用您喜欢的任何方式),您的简单问题的简单解决方案:

// here is my copy of the 10 algorithms
#include "../date_performance/date_algorithms"
#include <string>
#include <iostream>

// I'm being lazy in defining my date class
using date = std::tuple<int, unsigned, unsigned>;

// I'm being lazy in defining I/O for my date class
std::string
to_string(date const& d)
{
    int year;
    unsigned month;
    unsigned day;
    std::tie(year, month, day) = d;
    std::string r = std::to_string(year) + '-';
    if (month < 10)
        r += '0';
    r += std::to_string(month) + '-';
    if (day < 10)
        r += '0';
    r += std::to_string(day);
    return r;
}

day_of_week Example 1: Shift A Date to A Sunday day_of_week示例1:将日期转换为星期日

Here is how I would construct a date such as Mar. 5, 2013: 这是我如何构造日期(例如2013年3月5日)的方法:

date d(2013, 3, 5);

In order to find the Sunday on or prior to this date, I first need to find out what day of the week this date is on. 为了找到该日期或该日期之前的星期日,我首先需要找出该日期是星期几。 This is a two-step process using these low-level building blocks: 这是使用以下低级构建块的两步过程:

  1. Convert this date to a serial date: A count of days since some epoch. 将此日期转换为序列日期:自某个时期以来的天数。
  2. Convert the serial date to a day of the week. 将序列日期转换为星期几。

This is done with: 这可以通过以下方式完成:

int s = days_from_civil(std::get<0>(d), std::get<1>(d), std::get<2>(d));
unsigned wd = weekday_from_days(s);

Or if you prefer: 或者,如果您愿意:

int s = days_from_civil(2013, 3, 5);
unsigned wd = weekday_from_days(s);

Next it is good practice to name the constants for the days of the week. 接下来,优良作法是命名一周中各天的常数。 This code follows the C and C++ conventions, for example: 此代码遵循C和C ++约定,例如:

constexpr unsigned sun = 0;

Now the day preceding this day that is a Sunday is the serial date, subtracted by the number of days this weekday is past Sunday (which might be 0 if this is a Sunday, or as great as 6 if this weekday is a Saturday): 现在,在这一天的前一天(即星期日)是序列日期,然后减去该工作日在星期日之前的天数(如果这是星期日,则可能是0,如果这个工作日是星期六,则可能是6):

date d1 = civil_from_days(s - weekday_difference(wd, sun));

When I print this out: 当我打印出来时:

std::cout << "The Sunday before was " << to_string(d1) << '\n';

I get: 我得到:

The Sunday before was 2013-03-03

This is very little code to address your first question. 这是很少的代码可以解决您的第一个问题。 If instead you wanted the Sunday after 2013-03-05, that is just as easy: 相反,如果您想要2013-03-05之后的星期日,那也很容易:

date d2 = civil_from_days(s + weekday_difference(sun, wd));
std::cout << "The Sunday after was " << to_string(d2) << '\n';

which outputs: 输出:

The Sunday after was 2013-03-10

You could easily use these low-level algorithms to write your own higher-level algorithm to compute what you want. 您可以轻松地使用这些低级算法来编写自己的高级算法来计算所需的内容。

day_of_week Example 2: Last Saturday of March 2013 day_of_week示例2:2013年3月的最后一个星期六

The first thing you need is find the last day of the month for this year: 您需要做的第一件事是查找本月的最后一天:

unsigned last = last_day_of_month(2013, 3);

In this example we all know that this is the same as: 在此示例中,我们都知道这与以下内容相同:

unsigned last = 31;

But if the year and month were run time values, last_day_of_month comes in handy. 但是,如果年份和月份是运行时间值, last_day_of_month会派上用场。 Now we need the weekday of this last day of the month. 现在我们需要该月最后一天的工作日。 As before: 像以前一样:

unsigned wd_last = weekday_from_days(days_from_civil(2013, 3, last));

And now we just need to subtract from last the number of days this last weekday is past Saturday. 现在,我们只需要从last一个工作日中减去过去一个星期六的天数即可。 Assuming we had set up an informative constant ( sat == 6 ), this looks like: 假设我们已经设置了一个信息常量( sat == 6 ),它看起来像:

unsigned day = last - weekday_difference(wd_last, sat);

day is the day of the month that is the last Saturday of March in 2013. We could print that out with: day是当月的某天,即2013年3月的最后一个星期六。我们可以使用以下方式将其打印出来:

std::cout << "The last Saturday of March 2013 was " << to_string(date(2013, 3, day)) << '\n';

And get: 得到:

The last Saturday of March 2013 was 2013-03-30

And again, it would be easy for you to wrap all this up in a neat function. 同样,将所有这些打包成一个简单的函数将很容易。 Indeed, the paper even has such an example function in it. 确实, 本文甚至具有这样的示例功能。

If these low-level date algorithms are helpful to you, or any other readers, then great, otherwise never mind. 如果这些低级日期算法对您或任何其他读者有帮助,那就太好了,否则不要紧。 :-) :-)

It's rather an old question, but I leave my findings here for the record. 这是一个比较老的问题,但我将我的发现留在这里记录。

Looking up the source, the only place in date_time class that actually uses first_day_of_week is date_time::get() function. 查找源, date_time类中唯一实际使用first_day_of_weekdate_time::get()函数。 You cannot not use it for building date_time , or other fancy things. 您不能将其用于构建date_time或其他奇特的东西。 The only place you are allowed to use it is when you do something like mydt.get(period::first_day_of_week) , which returns a int in the range [1,7] . 唯一允许使用它的地方是当您执行mydt.get(period::first_day_of_week) ,该操作返回范围为[1,7]的int。 It is essentially the same as calendar::first_day_of_week() . 它本质上与calendar::first_day_of_week()

Note that int v in boost::locale::period::first_day_of_week(int v) will be effectivly ignored. 需要注意的是int vboost::locale::period::first_day_of_week(int v)将用途不同忽视。 int v sets amount of date_time_period , but no API will respect amount value of first_day_of_week . int v设置date_time_period金额,但是没有API会遵守first_day_of_week金额值。 It is just auto-generated API, I guess. 我猜这只是自动生成的API。

It is helpful to look into ICU documentation rather than Boost.locale documentation, because Boost.locale is really a thin layer on top of ICU. 查看ICU文档而不是Boost.locale文档会很有帮助,因为Boost.locale实际上是ICU上的一薄层。 You will find documentation of ICU calendar class that is used inside Boost.locale here . 您可以在此处找到Boost.locale内部使用的ICU日历类的文档。

Your example 1 leads to what ICU documentation refers to "Inconsistent information". 您的示例1导致ICU文档中提到的“信息不一致”。 You specified day and day-in-week that is inconsistent, and ICU will not try to correct day using provided day-in-week. 您指定的日期和星期几不一致,ICU将不会尝试使用提供的星期几来更正日期。 It just consider it inconsistent, thus showing strange behavior you mentioned. 它只是认为它不一致,因此显示您提到的奇怪行为。 You will need to specify week-in-month instead of day to avoid this inconsistency. 您将需要指定一周中的某天而不是某天,以避免这种不一致。

Your example 2 is much more interesting. 您的示例2更有趣。 You got it right when you specified the day-of-week first, and got it wrong when you set day-of-week-in-month first. 当您首先指定星期几时正确,而当您首先设置星期几时错误。 I guess the logic is like this: 我猜逻辑是这样的:

When you first set day-of-week-in-month to -1, ICU has insufficient information. 当您首次将每月的星期几设置为-1时,ICU信息不足。 So as in documentation, it tries to fill remaining field(day-of-week) to default, ie 1. The calendar is set to Mar 31, 2013 now, which is last sunday in 2013 March. 因此,如文档中所示,它会尝试将剩余的字段(星期几)设置为默认值,即1。日历现在设置为Mar 31, 2013 ,即2013年3月的最后一个星期日。 Then you set day-of-week to 7, so it travels to saturday of THAT WEEK, which is apr...6? 然后,您将星期几设置为7,这样它就会传播到那周的星期六,即4月... 6? I don't know why exactly you got Apr 5, 2014, 9:00:00 AM , but I believe the logic process is similar to this. 我不知道您为什么确切地在Apr 5, 2014, 9:00:00 AM ,但我相信逻辑过程与此类似。 Always set day-of-week first to avoid this weird behavior. 始终首先设置星期几以避免这种奇怪的行为。

I think ICU calendars are unintuitive a bit. 我认为ICU日历有点不直观。 Java also used DateTime API very similar to ICU calendar until SE 7, and Java programmers considered the standard DateTime library a nightmare. Java在SE 7之前一直使用与ICU日历非常相似的DateTime API,Java程序员认为标准DateTime库是一场噩梦。 (On release of SE 8, they replaced the ICU-like API with better one .) (在SE 8发行时,他们用更好的 API代替了类似ICU的API。)

You can consider switching to Howard Hinnant's Date library , which has far better API. 您可以考虑切换到Howard Hinnant的Date库 ,该具有更好的API。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM