Search code examples
c#datetimemathfiletime

Rounding FILETIME in C# to accommodate FAT rounding


I have a Windows FILETIME :

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

and I need it rounded UP to the nearest even second, as described here.

The code I have so far:

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

doesn't quite work... it turns 130790247821478763 into 130790247820008763, I'm after 130790247800000000.

Maths isn't my strongest subject... can I just zero those last four digits safely? Or should I forget the above code and just zero the last eight digits completely? Or... another way?


Solution

  • Rather than struggling with the DateTime object, you could perhaps more easily just do the raw mathematics:

    If input is the number of 100 nanoseconds, then:

    /10 for the number of microseconds;
    /10,000 for the number of milliseconds;
    /10,000,000 for the number of seconds;
    /20,000,000 for the number of 'two-seconds';

    So:

    input = input / 20000000 * 20000000;
    

    The division will round the number DOWN to the last even second, then the multiply will get it back into the right size again.

    But you said you wanted it rounded UP:

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

    That adds one 'two-second' to the small number before factoring it up again.

    Pedantically, if input was at exactly the two-second mark, then this would add two seconds to it. To fix that:

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

    That checks whether there's any fractional 'two-second' before deciding to bump it up. I'll leave it up to you as to whether you add this extra check...

    @Matthew Watson points out that the usual programmers trick for the above problem is to pre-add not quite enough to roll input over to the next 'two-second', then go ahead and do the divide-then-multiply. If input was over the minimum, that'll roll it over:

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