在 C# 中舍入 FILETIME 以适应 FAT 舍入

Rounding FILETIME in C# to accommodate FAT rounding

我有一个 Windows FILETIME :

A 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601 (UTC))

我需要将它四舍五入到最接近的偶数秒,如 here 所述。

我目前的代码:

        var originalDt = DateTime.FromFileTimeUtc(input);

        // round it UP to the nearest Second
        var newDt = originalDt.AddMilliseconds(1000 - originalDt.Millisecond);

        // then if our new Second isn't even
        if (newDt.Second % 2 != 0)
        {
            // add one second to it, then it'll be even
            newDt = newDt.AddSeconds(1);
        }

        return newDt.ToFileTimeUtc();

不太管用...它把 130790247821478763 变成了 130790247820008763,我在找 130790247800000000。

数学不是我最强的科目...我可以安全地将最后四位数字归零吗?或者我应该忘记上面的代码并将最后八位数字完全归零吗?或者……换个方式?

与其纠结于 DateTime 对象,不如只做原始数学运算更容易:

如果input是100纳秒的个数,那么:

/10为微秒数;
/10,000为毫秒数;
/10,000,000为秒数;
/20,000,000 为 'two-seconds' 个数;

所以:

input = input / 20000000 * 20000000;

除法会将数字向下舍入到最后一个偶数秒,然后乘法会将其再次恢复到正确的大小。

但是你说你希望它四舍五入:

input = (input / 20000000 + 1) * 20000000;

在再次分解之前将小数加一 'two-second'。

迂腐地说,如果 input 恰好 两秒标记,那么这将增加两秒。要解决这个问题:

if (input % 20000000!=0) {
    input = (input / 20000000 + 1) * 20000000;
} // if

在决定提高它之前检查是否有小数 'two-second'。我会留给你是否添加这个额外的支票...

@Matthew Watson 指出,程序员解决上述问题的常用技巧是预先添加 不完全 足以滚动 input 到下一个 'two-second',然后继续进行先除后乘。如果 input 超过了最小值,那么它会滚动:

    const long twoSeconds = 20000000;
    ...
    input = (input + twoSeconds - 1) / twoSeconds * twoSeconds;

使用原始报价,然后将其四舍五入为两秒的间隔。这比尝试在逗号后添加或删除内容更简单。

const long twoSecondsInTicks = 20000000;    // 20 million
long twoSecondIntervals = originalDt.Ticks / twoSecondsInTicks;
if (originalDt.Ticks % twoSecondsInTicks != 0) ++twoSecondIntervals;
var newDt = new DateTime(twoSecondIntervals * twoSecondsInTicks);

您的问题出在四舍五入到最接近的秒数行:

// round it UP to the nearest Second
var newDt = originalDt.AddMilliseconds(1000 - originalDt.Millisecond);

你保持完整毫秒的分数(因为originalDt.Millisecond是一个整数值),- 和 nano- 秒;应该是

// round it UP to the nearest Second
var newDt = originalDt.AddTicks( - (originalDt.Ticks % TimeSpan.TicksPerSecond));

当使用 ticks 时,最小的 可能的日期时间单位,你会得到预期的 130790247820000000 而没有 纳秒 (...8763)