Вопрос

I have a timestamp (64-bit integer) that represents the number of nanoseconds from 0 (imagine it representing how long a stopwatch has been running). To test this, I am using 31,740,770,222,000,008, which is 1 year, 2 days, 3 hours, 4 minutes, 5 seconds, 6 milliseconds, and 7 nanoseconds in nanoseconds, and I want to convert this integer back into years, days, hours, minutes, seconds, milliseconds, and nanoseconds in the format Y:D:H:M:S:m:n, so I expect to see 1:2:3:4:5:6:7. I also want to be able to pick and choose which resolutions I want, so if the stopwatch is only going to measure times under an hour, I could display it as M:S:m, and expect 4:5:6 from this.

Below is the algorithm I was using, but it isn't working at all (it returns 1:31970222000008:3170222000008:50222000008:222000008:8:8). Does anyone know a quick and reliable way to do this? I want it to perform all calculations in under half a millisecond, so the stopwatch can display the current number of milliseconds.

public static final byte
    YEAR        = 0b0100_0000,
    DAY         = 0b0010_0000,
    HOUR        = 0b0001_0000,
    MINUTE      = 0b0000_1000,
    SECOND      = 0b0000_0100,
    MILLISECOND = 0b0000_0010,
    NANOSECOND  = 0b0000_0001,

    RES_DEFAULT = (byte)(MINUTE | SECOND | MILLISECOND);

private static final double
    YEAR_IN_DAYS = 365.24219,
    DAY_IN_HOURS = 24,
    HOUR_IN_MINUTES = 60,
    MINUTE_IN_SECONDS = 60,
    SECOND_IN_MILLISECONDS = 1000,

    MILLISECOND_IN_NANOSECONDS = 1_000_000,
    SECOND_IN_NANOSECONDS = SECOND_IN_MILLISECONDS * MILLISECOND_IN_NANOSECONDS,
    MINUTE_IN_NANOSECONDS = MINUTE_IN_SECONDS * SECOND_IN_NANOSECONDS,
    HOUR_IN_NANOSECONDS = HOUR_IN_MINUTES * MINUTE_IN_NANOSECONDS,
    DAY_IN_NANOSECONDS = DAY_IN_HOURS * HOUR_IN_NANOSECONDS,
    YEAR_IN_NANOSECONDS = YEAR_IN_DAYS * DAY_IN_NANOSECONDS;

/**
 * Converts the given number of nanoseconds from timer 0 to a String in the format
 * {@code YYY:DDD:HH:MM:SS:mmmm}
 * where all values have leading zeroes. You can choose which values are used with the resFlags, which can be combined bitwise.
 * @param timeStamp the number of <B>nano</B>seconds away from timer 0
 * @param resFlags the flags representing which levels of resolution to return
 * @return the requested timestamp as a String
 */
public static String toString(long timeStamp, byte resFlags)
{
    return ""+
        ((resFlags & YEAR) != 0
            ? (long)(timeStamp / YEAR_IN_NANOSECONDS)
                + ((resFlags & ~YEAR) != 0
                    ? ":"
                    : "")
            : "")
        + ((resFlags & DAY) != 0
            ? (long)(timeStamp % DAY_IN_NANOSECONDS)
                + ((resFlags & ~(YEAR | DAY)) != 0
                    ? ":"
                    : "")
            : "")
        + ((resFlags & HOUR) != 0
            ? (long)(timeStamp % HOUR_IN_NANOSECONDS)
                + ((resFlags & ~(YEAR | DAY | HOUR)) != 0
                    ? ":"
                    : "")
            : "")
        + ((resFlags & MINUTE) != 0
            ? (long)(timeStamp % MINUTE_IN_NANOSECONDS)
                + ((resFlags & ~(YEAR | DAY | HOUR | MINUTE)) != 0
                    ? ":"
                    : "")
            : "")
        + ((resFlags & SECOND) != 0
            ? (long)(timeStamp % SECOND_IN_NANOSECONDS)
                + ((resFlags & (MILLISECOND | NANOSECOND)) != 0
                    ? ":"
                    : "")
            : "")
        + ((resFlags & MILLISECOND) != 0
            ? (long)(timeStamp % MILLISECOND_IN_NANOSECONDS)
                + ((resFlags & NANOSECOND) != 0
                    ? ":"
                    : "")
            : "")
        + ((resFlags & NANOSECOND) != 0
            ? (long)(timeStamp % 1_000_000)
            : "")
        ;
}
Это было полезно?

Решение

For each unit past the year, you need to subtract off the portion "consumed" by the previous unit. Then divide (not mod) by the next unit. Something like this, for example:

years = timeStamp / YEAR_IN_NANOSECONDS;
timeStamp -= years * YEAR_IN_NANOSECONDS;
days = timeStamp / DAY_IN_NANOSECONDS;
timeStamp -= days * DAY_IN_NANOSECONDS;
// hours next, etc...

For days you could also do:

days = (timeStamp % YEAR_IN_NANOSECONDS) / DAY_IN_NANOSECONDS;

But, for each new increment it will get more complicated. The first way generally seems cleaner to me.

You might want to break up that long return statement to facilitate. Get the numbers you want, then build the string after you have them all.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top