来自TickCount的日期时间?

时间:2012-07-04 18:24:40

标签: c# datetime

我正在C#程序中捕获一些事件,这些事件以时间戳作为系统tickcount(自开始时间以来的毫秒数)返回。

根据我看过的其他问题,知道我可以从System.Environment.TickCount属性(或其他东西)获得相同的数字,如何推断出与我收到的TickCount相对应的DateTime对象? / p>

4 个答案:

答案 0 :(得分:6)

如果没有更多信息,你就不可能(即便如此,它也可能含糊不清)。 Environment.TickCount返回:

  

一个32位有符号整数,包含自上次启动计算机以来经过的时间量(以毫秒为单位)。

...除非你能从某个地方找到计算机启动的时间,否则你运气不好。您可以使用注册表项或系统调用来查找上次启动时间,但我不了解它们。当然,您可以尽可能快地在Environment.TickCountDateTime.UtcNow之后(或之前)获得近似值,并找出两者之间的差异:< / p>

public static DateTime UnreliableDateTimeFromTickCount(int tickCount)
{
    DateTime now = DateTime.UtcNow;
    DateTime boot = now - TimeSpan.FromMilliseconds(Environment.TickCount);
    return boot + TimeSpan.FromMilliseconds(tickCount);
}

然而,即使 with ,该值也会每24.9天循环一次,因此如果计算机已经打开的时间超过了这个值,那么计数就不明确了。

我建议尽可能避免使用Environment.TickCount,基本上 - 这完全在你的控制之下吗?

答案 1 :(得分:2)

我知道这是一个非常老的问题,但是由于这是我搜索时Google的第一个热门产品,因此我认为其他人可能会来到这里。 @JonSkeet答案中的所有要点都是有效的,请务必阅读它们并充分理解它适用于您的位置。对于我的具体情况,我知道我需要转换的滴答计数值将在最近几天之内,但是存在捕获的值是在TickCount溢出之前进行转换的风险。下面是我编写的方法,该方法应该处理TickCount溢出的情况,并将给定的滴答计数转换为DateTime,只要它在过去49天内即可。

详细介绍Environment.TickCount的工作方式:打开计算机后,它从0开始,每毫秒递增。在启动24.9天后,达到了Int32的容量,并且TickCount从Int32.MaxValue转换为Int32.MinValue。初始换行后,它将每49.7天继续溢出。

/// <summary>
/// Converts the given tick count into a DateTime. Since TickCount rolls over after 24.9 days, 
/// then every 49.7 days, it is assumed that the given tickCount occurrs in the past and is 
/// within the last 49.7 days.
/// </summary>
/// <param name="tickCount">A tick count that has occurred in the past 49.7 days</param>
/// <returns>The DateTime the given tick count occurred</returns>
private DateTime ConvertTickToDateTime(int tickCount)
{
    // Get a reference point for the current time
    int nowTick = Environment.TickCount;
    DateTime currTime = DateTime.Now;
    Int64 mSecElapsed = 0;

    // Check for overflow condition
    if( tickCount < nowTick) // Then no overflow has occurred since the recorded tick
    {
        // MIN|--------------TC---------------0------------Now-------------|MAX
        mSecElapsed = nowTick - tickCount;
    }
    else // tickCount >= currTick; Some overflow has occurred since the recorded tick
    {
        // MIN|--------------Now---------------0------------TC-------------|MAX
        mSecElapsed = Convert.ToInt64((int.MaxValue - tickCount) + (nowTick + Math.Abs(Convert.ToDouble(int.MinValue))));   // Time BEFORE overflow + time since the overflow
    }

    DateTime tickCountAsDateTime = currTime - TimeSpan.FromMilliseconds(mSecElapsed);
    return tickCountAsDateTime;        
}

为了测试该方法,我使用了以下代码:

static void Main(string[] args)
{
    Console.WriteLine("Test Start Time: {0}", DateTime.Now);

    // 10 seconds ago
    int tc0 = CalculateTC(TimeSpan.FromSeconds(10));
    Console.WriteLine("Expect 10 seconds ago: {0}", ConvertTickToDateTime(tc0));

    // 10 minutes ago
    int tc1 = CalculateTC(TimeSpan.FromMinutes(10));
    Console.WriteLine("Expect 10 minutes ago: {0}", ConvertTickToDateTime(tc1));

    // 10 hours ago
    int tc2 = CalculateTC(TimeSpan.FromHours(10));
    Console.WriteLine("Expect 10 hours ago: {0}", ConvertTickToDateTime(tc2));

    // 1 Day ago
    int tc3 = CalculateTC(TimeSpan.FromDays(1));
    Console.WriteLine("Expect 1 Day ago: {0}", ConvertTickToDateTime(tc3));

    // 10 Day ago
    int tc4 = CalculateTC(TimeSpan.FromDays(10));
    Console.WriteLine("Expect 10 Days ago: {0}", ConvertTickToDateTime(tc4));

    // 30 Day ago
    int tc5 = CalculateTC(TimeSpan.FromDays(30));
    Console.WriteLine("Expect 30 Days ago: {0}", ConvertTickToDateTime(tc5));

    // 48 Day ago
    int tc6 = CalculateTC(TimeSpan.FromDays(48));
    Console.WriteLine("Expect 48 Days ago: {0}", ConvertTickToDateTime(tc6));

    // 50 Day ago (Should read as a more recent time because of the Environment.TickCount wrapping limit - within a day or two)
    int tc7 = CalculateTC(TimeSpan.FromDays(50));
    Console.WriteLine("Expect to not see 50 Days ago: {0}", ConvertTickToDateTime(tc7));

    // 10 Seconds ahead (Should read as a very old date - around 50 days ago)
    int tc8 = Convert.ToInt32(Environment.TickCount + TimeSpan.FromSeconds(10).TotalMilliseconds);
    Console.WriteLine("Expect to not see 10 seconds from now: {0}", ConvertTickToDateTime(tc8));    
}


private static int CalculateTC(TimeSpan timespan)
{
    int nowTick = Environment.TickCount;
    double mSecToGoBack = timespan.TotalMilliseconds;

    int tc;

    if (Math.Abs(nowTick - int.MinValue) >= mSecToGoBack) // Then we don't have to deal with an overflow
    {
        tc = Convert.ToInt32(nowTick - mSecToGoBack);
    }
    else // Deal with the overflow wrapping
    {
        double remainingTime = nowTick + Math.Abs(Convert.ToDouble(int.MinValue));
        remainingTime = mSecToGoBack - remainingTime;

        tc = Convert.ToInt32(int.MaxValue - remainingTime);
    }
    return tc;
}

以下是测试应用程序的输出:

Test Start Time: 5/3/2019 4:30:05 PM
Expect 10 seconds ago: 5/3/2019 4:29:55 PM
Expect 10 minutes ago: 5/3/2019 4:20:05 PM
Expect 10 hours ago: 5/3/2019 6:30:05 AM
Expect 1 Day ago: 5/2/2019 4:30:05 PM
Expect 10 Days ago: 4/23/2019 4:30:05 PM
Expect 30 Days ago: 4/3/2019 4:30:05 PM
Expect 48 Days ago: 3/16/2019 4:30:05 PM
Expect to not see 50 Days ago: 5/3/2019 9:32:53 AM
Expect to not see 10 seconds from now: 3/14/2019 11:27:28 PM

我希望这对可能与我处于类似状况的人有所帮助。

答案 2 :(得分:1)

不是滴答计数为// Set up the network to use HttpURLConnection as the HTTP client. Network network = new BasicNetwork(new HurlStack()); // Instantiate the RequestQueue with the cache and network. requestQueue = new RequestQueue(cache, network); // Start the queue requestQueue.start();

似乎您希望使用不同的数据类型:

  • 系统正常运行时间为int
  • 系统启动时间为TimeSpan

(但是,根据您所提问题的措辞,我不确定100%)

下面是一个使用Windows Management Instrumentation获取这些属性的代码示例。

DateTime

答案 3 :(得分:0)

我似乎从杰伦的解决方案中得到了错误的结果;这可能是个错误/不正确-涉及到溢出的复杂性,但这使我接近测试的正确结果,试图近似结果:

    // TimeSpan result

    var approxUpTime = TryApproximateUpTime();

    private static TimeSpan? TryApproximateUpTime()
    {
        TimeSpan? retVal;

        var envTickCountInMs =
            Environment.TickCount;

        try
        {
            retVal = 
                envTickCountInMs > 0
                    ?
                        new DateTime()
                            .AddMilliseconds(Environment.TickCount) -
                                DateTime.MinValue
                    :
                        new TimeSpan(
                            new DateTime(
                                ((long)int.MaxValue + (envTickCountInMs & int.MaxValue)) * 10 * 1000).Ticks);
        }
        catch (Exception)
        {
            // IGNORE
            retVal = null;
        }

        return retVal;
    }