4

I'm current using this method (C#) to get the Unix Time in milliseconds:

long UnixTime()
{
    return (long) (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0)).TotalMilliseconds;
}   

Question - Is there a way to get the unix time in nanoseconds?

Thanks in advance.

Acidic
  • 6,154
  • 12
  • 46
  • 80

4 Answers4

5

The calculation by itself isn't hard:

long UnixTime()
{
    DateTime epochStart=new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
    return (DateTime.UtcNow - epochStart).Ticks*100;
}

DateTime and TimeSpan internally store an integral amounts of ticks, with one tick being 100ns. I also specified the epoch start as UTC time, because I consider it ugly to subtract DateTimes with different Kind, even if it works.

But DateTime.UtcNow has very low accuracy. It is only updated every few milliseconds(Typical values vary between 1ms and 16ms).


To get a constant framerate you could use StopWatch since you don't need the absolute time. But if you go that way you must use a busy wait. Since Thread.Sleep, timers,... suffer from the same limitation.

Alternatively you can use the timeBeginPeriod(1) API, to force windows to update the clock and run timers every 1ms. But this is a global setting and increases power consumption. Still it's better than busy-wait.

To measure time differences you can use StopWatch with is based on QueryPerformanceCounter, but this comes with its own set of problems, such as desyncs between different cores. I've seen machines were QueryPerformanceCounter jumped by several hundred Milliseconds when your thread gets scheduled on another core.

CodesInChaos
  • 106,488
  • 23
  • 218
  • 262
  • So it would seem, as I read via the link posted by @kol. It seems awfully complicated to mimic the effect of java's System.nanoTime() in C#... – Acidic Nov 23 '11 at 11:05
  • @Acidic In my experience it's rarely necessary to get an absolute time with such high accuracy. Typically high accuracy is only needed for time differences, which `StopWatch` handles easily. And did you check how accurate `nanoTime` is in Java on windows? The accuracy problem is inherent in the windows API, it's not a C# or .net problem. – CodesInChaos Nov 23 '11 at 11:07
  • I require this to create a thread that performs a certain task 60 times per second, and using milliseconds I am forced to have it tick every 16 or 17 ms, which is obviously far from being accurate. – Acidic Nov 23 '11 at 11:09
  • You can use `timeBeginPeriod`, but have a look at the caveats first. It's fine for games, but I'd avoid it in background applications. – CodesInChaos Nov 23 '11 at 11:10
  • from what I know `nanoTime()` is not dead-on accurate, but it is in most cases (for my uses) accurate enough - something that I can't say about using the millisecond equivalent. – Acidic Nov 23 '11 at 11:12
  • @Acidic Stopwatch would be accurate enough. It's not like you need the absolute time if you just want 60FPS. – CodesInChaos Nov 23 '11 at 11:15
  • Welp, I ended up using a `Stopwatch` @ `Elapsed.Ticks` to measure the time between the thead ticks. – Acidic Nov 23 '11 at 11:37
2

The TotalMilliseconds property returns a double containing whole and fractional milliseconds.

So, you only have to multiply its value by 1000000 to obtain nanoseconds:

return (long) ((DateTime.UtcNow
    - new DateTime(1970, 1, 1, 0, 0, 0)).TotalMilliseconds * 1000000.0);
Frédéric Hamidi
  • 258,201
  • 41
  • 486
  • 479
1

I think this is not so easy (on trivial desktop x86 computer) doe to problems with precision. So first of all DateTime class is useless in this situation. You may use StopWatch

Here is very good article about this : http://www.codeproject.com/KB/testing/stopwatch-measure-precise.aspx

+1 : https://stackoverflow.com/q/1416188/241506enter link description here

Community
  • 1
  • 1
VMykyt
  • 1,589
  • 12
  • 17
  • Well, the computer I work on is x64. It is quite easy to achieve this in Java as well. – Acidic Nov 23 '11 at 10:54
  • I'm talking about precision of OS (actually Windows family) but not language. – VMykyt Nov 23 '11 at 10:56
  • @VMykyd I understand that, I simply meant that I do this in Java on this computer and it works as expected - so the hardware shouldn't limit me to that extent. – Acidic Nov 23 '11 at 10:59
  • Very interesting. When I looked for some information about this I had realized than the bottleneck in cpu and|or OS. So could you post some links? :) – VMykyt Nov 23 '11 at 11:11
  • I'm not sure I understand your last comment... I think you had a typo somewhere in there. – Acidic Nov 23 '11 at 11:38
1

This class will help. It allows you to convert back and forth from Unix to Windows time. Comments may not need to be updated but it all works well.

public sealed class LinuxToWindowsFileTimeConverter : IValueConverter
    {
        static long ticksFrom1601To1970 = (long)(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc) - DateTime.FromFileTimeUtc(0)).Ticks;        

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {                   
            return new DateTime();       
        }        

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return new DateTime();
        }


        public static DateTime Convert(Int64 nanoSecsSince1970)
        {            
            return Convert(nanoSecsSince1970, ScaleFactor.Billion);
        }

        /// <summary>
        /// Converts from Linux seconds to Windows DateTime
        /// </summary>
        /// <param name="secs"></param><remarks> secs</remarks>
        /// <param name="sf"></param><remarks>specifies scale factor.  
        /// Specify ScaleFactor.One for secs since 1970.  
        /// ScaleFactor.Thousand for milli (10^3) seconds since 1970. 
        /// ScaleFactor.Million for micro (10^6)seconds since 1970.
        /// ScaleFactor.Billion for nano (10^9)seconds since 1970.
        /// etc.</remarks>
        /// <returns></returns>
        public static DateTime Convert(Int64 secs, ScaleFactor sf)
        {                                                                   
            long hndrdnsfrom1601 = 0;

            switch(sf)
            {                
                case ScaleFactor.Billion:
                    hndrdnsfrom1601 = ticksFrom1601To1970 + secs / 100;                    
                    break;                
                default:
                    // TODO:  Correct for other cases.
                    hndrdnsfrom1601 = (long)ticksFrom1601To1970 + (secs * (long)ScaleFactor.TenMillion / (long)sf); 
                    break;
            }

            return DateTime.FromFileTimeUtc(hndrdnsfrom1601);                        
        }

        public static long ConvertBack(DateTime dateTimeInUTC)
        {
            if (dateTimeInUTC == new DateTime()) 
                dateTimeInUTC = new DateTime(1980, 1,1).ToUniversalTime();           

            long secsSince1970 = (dateTimeInUTC.ToFileTimeUtc() - ticksFrom1601To1970) * ((long)ScaleFactor.Billion / (long)ScaleFactor.TenMillion);           
            return secsSince1970;
        }        

        public Int64 ConvertBack(DateTime dateTimeInUTC, CultureInfo culture)
        {
            return ConvertBack(dateTimeInUTC, culture, ScaleFactor.Billion);
        }


        /// <summary>
        /// Converts from Windows file time to Linux seconds.
        /// </summary>
        /// <param name="dateTimeInUTC"></param>
        /// <param name="culture"></param>
        /// <param name="sf"></param><remarks>
        /// Specify ScaleFactor.One for secs since 1970.  
        /// ScaleFactor.Thousand for milli (10^3) seconds since 1970. 
        /// ScaleFactor.Million for micro (10^6)seconds since 1970.
        /// ScaleFactor.Billion for nano (10^9)seconds since 1970.
        /// </remarks>
        /// <returns></returns>
        public Int64 ConvertBack(DateTime dateTimeInUTC, CultureInfo culture, ScaleFactor sf)
        {
            long secsSince1970 = (dateTimeInUTC.ToFileTimeUtc() - ticksFrom1601To1970) * ((long)sf / (long)ScaleFactor.TenMillion);
            return secsSince1970;
        }
    }

    public enum ScaleFactor : long
    { 
        One = 1,
        Ten = 10,
        Hundred = 100,
        Thousand = 1000,
        TenThou = 10000,
        HundredThou = 100000,
        Million = 1000000,
        TenMillion = 10000000,
        HundredMillion = 100000000,
        Billion = 1000000000,
        TenBillion = 10000000000,
        HundredBillion = 100000000000
    }
Klaus Nji
  • 18,107
  • 29
  • 105
  • 185