2

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?

Cœur
  • 37,241
  • 25
  • 195
  • 267
GoldieLocks
  • 845
  • 7
  • 22

3 Answers3

3

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;
John Burger
  • 3,662
  • 1
  • 13
  • 23
0

Work with raw ticks, then round these up to two-second intervals. This is simpler than trying to add or remove things after the comma.

const long twoSecondsInTicks = 20000000;    // 20 million
long twoSecondIntervals = originalDt.Ticks / twoSecondsInTicks;
if (originalDt.Ticks % twoSecondsInTicks != 0) ++twoSecondIntervals;
var newDt = new DateTime(twoSecondIntervals * twoSecondsInTicks);
Jeroen Mostert
  • 27,176
  • 2
  • 52
  • 85
0

Your problem is in the rounding up to the nearest second line:

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

you leave intact fractions of milliseconds (since originalDt.Millisecond is a integer value), micro- and nano- seconds; it should be

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

when working with ticks, the smallest possible datetime unit, you'll get expected 130790247820000000 without nanoseconds (...8763)

Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215