rust chrono Duration如何获取年数或月数?

3

2
请注意,在编程中表达以月为单位的持续时间并没有太多意义,因为每个月的长度都不同。您可以使用 num_weeks() / 4num_days() / 30 来获取一个近似值。 - Jmb
在C++中,2020y/December + months{1} == 2021y/Januarymonths{1}表示一个月的时间跨度。 - Howard Hinnant
@HowardHinnant 现在试试这个 2020年2月1日 + months{1} == 2020年3月1日。在C++中,months{1}是2629746秒的持续时间或30.437天,这可能会导致意外的结果。 - Jmb
1
2020y/March/1 不是你预期的结果吗?这就是我得到的结果。更多信息请参见:https://dev59.com/46Dia4cB1Zd3GeqPB1Er#43018120 - Howard Hinnant
一年中的日子也可能会有闰秒,因此长度可能不同。chrono Duration 没有考虑到这一点。目前唯一真正的解决方案是回归 ICU - Code4R7
3个回答

1
我也必须解决这个问题。正如其他人所指出的那样,年份和月份的问题在于你需要提供一个起始日期,否则你会得到不同的答案,这取决于月份中的天数和年份中的闰日。chrono::Duration不记得你使用的原始基准日期,因此它无法提供这些答案。
但是,如果你可以访问计算持续时间的两个日期,你可以通过<end_datetime>.years_since(<start_datetime)来获取年份。
Chrono没有任何计算月份的函数。这是因为它很简单吗?对于一个全面的日历包来说太琐碎了吗?我不知道。他们提供其他的月份计算,比如<datetime>.checked_add_months(..)。但是(至少我认为)这是我自己的做法:
type BaseDT = DateTime<FixedOffset>;
type UnitSize = i64;

pub fn signed_month_difference(start: BaseDT, end: BaseDT) -> UnitSize {
    let end_naive = end.date_naive();
    let start_naive = start.date_naive();

    let month_diff = end_naive.month() as UnitSize - start_naive.month() as UnitSize;
    let years_diff = (end_naive.year() - start_naive.year()) as UnitSize;
    if month_diff >= 0 {
        (years_diff * 12) + month_diff
    } else {
        (years_diff - 1) * 12 + (month_diff + 12)
    }
}

0
货箱chronoutil提供RelativeDuration,它"扩展了Chrono的Duration,以添加月份和年份"
来自docs.rs的示例:
let one_day = RelativeDuration::days(1);
let one_month = RelativeDuration::months(1);
let delta = one_month + one_day;
let start = NaiveDate::from_ymd(2020, 1, 30);
assert_eq!(start + delta, NaiveDate::from_ymd(2020, 3, 1));

请注意,仅从持续时间本身无法提取出月份或年份。这些属性可能会因您使用的日历上的时间点而有所不同。
例如,在公历中,并非所有月份长度都相等(28到31天),具体取决于起始日期/时间内有多少个月落在持续时间内。 date_component包将考虑这一点。给定两个日期,它会为您计算月份和年份。
示例(来自文档):
use chrono::prelude::*;
use date_component::date_component;

fn main() {
    let date1 = Utc.ymd(2015, 4, 20).and_hms(0, 0, 0);
    let date2 =  Utc.ymd(2015, 12, 19).and_hms(0, 0, 0);
    let date_interval = date_component::calculate(&date1, &date2);
    println!("{:?}", date_interval);
}
// DateComponent { year: 0, month: 7, week: 4, modulo_days: 1, day: 29, hour: 0, minute: 0, second: 0, interval_seconds: 20995200, interval_minutes: 349920, interval_hours: 5832, interval_days: 243, invert: false }

但是这个库不允许您提取相对持续时间中累积的月数。您可以向给定日期添加N个月(准确地),但是在给定2个日期的情况下,无论是chrono还是此库中我找到的任何内容都无法告诉我这2个日期之间有多少个月。 - BobHy
是的,你说得对。我已经更新了答案。 - Code4R7

0

chrono::Duration提供了“ISO 8601时间持续时间与纳秒精度”的日期和时间持续时间,这意味着它在内部表示持续时间为纳秒数,然后提供方便的方法将其转换为其他持续时间单位,如天、周、毫秒等。

这与实际的ISO 8601持续时间标准有些不符,该标准是一种表示和格式的标准。该标准通过格式P [n] Y [n] M [n] DT [n] H [n] M [n] S表示持续时间-这可能会给您想要的结果。但这不是chronos :: Duration的设计目的。

问题在于,为了表示几个月或几年的持续时间,需要比纳秒数更多的信息。持续时间定义了时间间隔中的介入时间:两个点之间的时间。起始或结束时间很重要,因为月份或年份不是标准的持续时间。有28、29、30、31天的月份,以及365和366天的年份。

如果您要编写自己的算法以年、月、日、小时、分钟、秒等格式格式化持续时间,则必须知道开始日期。此外,时区很重要,因为需要考虑夏令时。您还必须做出关于如何表示月份或年份的决策。例如,一月有31天,二月有28天。从1月1日开始计算持续时间1.75个月意味着什么?这是否意味着1月有31天,然后在2月份中有0.75 * 28天?

或者,您可以以级联单位格式表示从起始日期开始的持续时间:例如,在1970年1月1日12:00Z之后的5年、4个月、3天、2小时、12分钟和3秒钟。就像ISO 8601标准一样。

因此,这不是一个简单的解决方案,这一切都取决于您的要求。我可以理解为什么chronos:Duration的开发人员没有提供num_months()和num_years()!


天数也不是一样的,考虑到闰秒,那是不是也应该排除天数呢?为了定义清晰的struct持有天数、月份、年份等等独立于偏移量的持续时间。只有在使用 Duration 进行计算时才会关注偏移量。现在我们需要调用ICU组件来完成这项工作,因为我没有看到其他的解决方法。 - Code4R7
真实的事实和提出的好问题,但不是答案,也不是指向问题答案的指针。 - BobHy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接