If I write a simple method to return the milliseconds between epoch time and DateTime.UtcNow, I get a proper answer. However, if I write a method to return the milliseconds between some arbitrary date and epoch time, the last three digits are always zero. 'Some arbitrary date' means that I pass in to the method the output of DateTime.Parse("arbitrary date string"). As near as I can make out, the DateTime object returned by .Parse is not returning all the significant digits.
Test method:
static void GetMillis()
{
DateTime dUtc = DateTime.UtcNow;
DateTime epoch = new DateTime(1970,1,1,0,0,0,DateTimeKind.Utc);
double utcmillis = (dUtc - epoch).TotalMilliseconds;
String timestamp = dUtc.ToString();
DateTime arbitrary = (DateTime.Parse(timestamp));
Console.WriteLine("Milliseconds between DateTime.UtcNow {0} \nand epoch time {1} are {2}", dUtc, epoch, utcmillis);
Console.WriteLine("Milliseconds between arbitrary date {0} \nand epoch time {1} are {2}", arbitrary, epoch, (arbitrary - epoch).TotalMilliseconds);
}
Output:
C:\src\vs\epochConverter\epochConverter\bin\Debug
{powem} [54] --> .\epochConverter.exe -debug
Milliseconds between DateTime.UtcNow 8/26/2012 11:12:31 PM
and epoch time 1/1/1970 12:00:00 AM are 1346022751385.8
Milliseconds between arbitrary date 8/26/2012 11:12:31 PM
and epoch time 1/1/1970 12:00:00 AM are 1346022751000
I don't know if I'm doing something grotesquely wrong or not understanding the math here. I've researched in MSDN and can't find anything relating to this difference. I really would like to be able to compute the millis as described -- is it possible?
Thanks.
mp
You want to examine the intermediate values of:
String timestamp = dUtc.ToString();
Just what it returns will depend on your local settings, but it'll be something like 8/26/2012 11:12:31, which is only accurate to the nearest second.
Parsing that of course gives a date-time with 0 milliseconds.
It is therefore correct that your milliseconds-since-epoch method has zeros at that point.
If however you did something like:
arbitrary = new DateTime(2012, 8, 26, 11, 12, 31, 123);
You'd get those 123 milliseconds influencing the result. You can also use a ToString and a ParseExact that includes fractions of a second, or a whole slew of other ways of obtaining a DateTime.
In all, your milliseconds-since-epoch worked perfectly, but your way of getting a date to test it was flawed.
The default DateTime.ToString() format does not include the milliseconds and this is where the data is being lost; it happens before the Parse. To obtain the milliseconds in the string representation, use a custom format:
DateTime.UtcNow.ToString()
// -> 8/26/2012 11:37:24 PM
DateTime.Parse("8/26/2012 11:37:24 PM").Millisecond
// -> 0
DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.fffffffK")
// -> 2012-08-26T23:41:17.3085938Z
DateTime.Parse("2012-08-26T23:41:17.3085938Z").Millisecond
// -> 308
See The Round-trip ("O", "o") Format Specifier
to type less. Or, in this case, consider avoiding the conversion entirely :-)
The math is sound.
The math looks reasonable here. Don't forget there are 1000 milliseconds in a 1 second, so any date computation from an arbitrary time that does not include milliseconds vs an almost identical time that includes milliseconds will have an error of +/- 1000 milliseconds.
Related
This question already has answers here:
How do you convert epoch time in C#?
(14 answers)
Closed 2 years ago.
I have a simple DateTime object, equal to the date: 11/1/2020 8:11:14 AM.
I want to convert it to milliseconds so I do:
myTimestamp?.Ticks / TimeSpan.TicksPerMillisecond.
I get 63739786274788, which seems correct from the pure calculation perspective.
However, when I input it into one of the online converters to validate, I get the date Wed Nov 01 3989 01:11:14, which is of course way off.
Questions:
What is this number 63739786274788 if not time in ms?
How do I get "normal" timestamp in ms?
In .NET, DateTime ticks are based on an epoch of 0001-01-01T00:00:00.0000000. The .Kind property is used to decide whether that is UTC, local time, or "unspecified".
Most online converters, such as the one you linked to, are expecting a Unix Timestamp, where the value is based on an epoch of 1970-01-01T00:00:00.000Z. It is always UTC based. (The precision varies, both seconds and milliseconds are commonly used.)
If you want to get a milliseconds-based Unix Timestamp From .NET, instead of dividing you should use the built-in functions DateTimeOffset.FromUnixTimeMilliseconds and DateTimeOffset.ToUnixTimeMilliseconds. (There are also seconds-based versions of these functions.)
Assuming your input values are UTC-based:
DateTime dt = new DateTime(2020, 11, 1, 8, 11, 14, DateTimeKind.Utc);
DateTimeOffset dto = new DateTimeOffset(dt);
long timestamp = dto.ToUnixTimeMilliseconds();
// output: 1604218274000
DateTimeKind.Local will also work with this, assuming your values are indeed based on the computer's local time zone. DateTimeKind.Unspecified is a bit trickier, as you'll need to convert to a DateTimeOffset with a specific time zone using TimeZoneInfo first.
You could also construct the DateTimeOffset value directly, rather than go through DateTime at all.
Okay, so you start off dividing Ticks by TicksPerMillisecond (10,000)
As you can see, the number you generated is much larger than the current milliseconds:
63739786274788
1607363529803
The short answer is that Ticks are based off of 12:00:00 midnight January 1, 0001 and a your online calculator is based off of unix time, January 1, 1970. So that would explain why you're about 2,000 years off. If you subtracted the Ticks from a new DateTime(1970,1,1), then that would give you about the right number to satisfy the online calculator.
For more info, I would suggest reading through MS's docs on DateTime
I have a DateTime represented as long (8 bytes), that came from DateTime.ToBinary(), let's call it dateTimeBin. Is there an optimal way of dropping the Time information (I only care for the date) so I can compare it to a start of day? Lets say we have this sample value as a start of day.
DateTime startOfDay = new DateTime(2020,3,4,0,0,0);
long startOfDayBin = startOfDay.ToBinary();
I obviously know I can always convert to a DateTime object then get the date component. However, this operation is going to happen billions of times and every little performance tweak helps.
Is there an efficient way of extracting the Date info of dateTimeBin without converting it to DateTime? Or any arithmetic operation on the long that will return the date only?
Is there a way to match startOfDay (or startOfDayBin) and dateTimeBin if they have the same date components?
Is there a way to see if (dateTimeBin >= startOfDayBin), I don't think the long comparison is valid.
N.B. all the dates are UTC
Since you are working only with UTC dates - makes sense to use DateTime.Ticks instead of DateTime.ToBinary, because former has relatively clear meaning - number of ticks since epoch, just like the unix time, the only difference is unix time interval is second and not tick (where tick is 1/10.000.000 of a second), and epoch is midnight January 1st of 0001 year and not year 1970. While ToBinary only promises that you can restore original DateTime value back and that's it.
With ticks it's easy to extract time and date. To extract time, you need to remainder of division of ticks by number of ticks in a full day, so
long binTicks = myDateTime.Ticks;
long ticksInDay = 24L * 60 * 60 * 10_000_000;
long time = binTicks % ticksInDay;
You can then use convert that to TimeSpan:
var ts = TimeSpan.FromTicks(time);
for convenience, or use as is. The same with extracting only date: just substract time
long date = binTicks - (binTicks % ticksInDay);
Regular comparision (dateTimeBin >= startOfDayBin) in also valid for tick values.
I'm trying to convert ULONG to DateTime and as DateTime accepts Ticks as param which are LONG, here's how I do it.
ulong time = 12354;
new DateTime((long)time).ToString("HH:mm:ss");
The result of this is 00:00:00.
I don't understand the result, am I doing something wrong?
P.S. i.Time is not 0, I checked multiple times.
Citing the documentation:
Initializes a new instance of the DateTime structure to a specified number of ticks.
ticks
Type: System.Int64
A date and time expressed in the number of 100-nanosecond intervals that have elapsed since January 1, 0001 at 00:00:00.000 in the Gregorian calendar.
This is 100 nanoseconds which is a super small time unit. So unless your number is larger than 10000000, you don’t even get a single second:
Console.WriteLine(new DateTime((long)10000000).ToString());
// 01.01.0001 00:00:01
So you should really think about what your “time left” (i.Time) value is supposed to mean? Is this really time in the unit of 100 nanoseconds? Very likely not. It’s probably more about seconds or something completely different.
Btw. if the number you have does not actually represent a moment in time, you should not use DateTime. You should use TimeSpan instead. Its long constructor has the same behavior though, but you can use one of the handy static functions to create a time span with the correct unit:
var ts = TimeSpan.FromSeconds(1000);
Console.WriteLine(ts.ToString());
// 00:16:40
Because a tick is 100 nanoseconds, and so 12354 ticks is only 1235400 nanoseconds which is only .0012354 seconds. So your datetime is .0012354 seconds after midnight on 1 Jan in the year one.
I am working on a C# project where I have a date/time in the format of 2012-11-24 15:35:18 and I need to convert this into an epoch time stamp.
Everything I've found on Google is to convert an epoch time stamp into a human readable but I need it to be done the other way round.
Thanks for any help you can provide.
I found this here:
epoch = (DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000000;
Instead of DateTime.Now, you should be able to input your desired time.
You didn't say your exact use case, but the standard .NET DateTime has a Ticks attribute which is defined as:
The value of this property represents the number of 100-nanosecond
intervals that have elapsed since 12:00:00 midnight, January 1, 0001,
which represents DateTime.MinValue. It does not include the number of
ticks that are attributable to leap seconds.
This is essentially an epoch based time, if it will suit your needs. Otherwise, with this value, you should be easily able to compute a conversion to another epoch time keeping method.
You need to use TryParse:
string input = "2012-11-24 15:35:18";
DateTime dateTime;
if (DateTime.TryParse(input, out dateTime))
{
ulong epoch = (dateTime.ToUniversalTime().Ticks - 621355968000000000) / 10000000;
}
I got some strange result for:
Console.WriteLine(new DateTime(1296346155).ToString());
Result is:
01.01.0001 0:02:09
But it is not right!
I parsed value 1296346155 from some file. It said that it is in UTC;
Please explain;)
Thank you for help!)))
DateTime expects "A date and time expressed in the number of 100-nanosecond intervals that have elapsed since January 1, 0001 at 00:00:00.000 in the Gregorian calendar." (from msdn)
This question shows how you can convert a unix timestamp to a DateTime.
The constructor for DateTime that accept long type is expecting ticks value, not seconds or even milliseconds, and not from 1/1/1970 like in other languages.
So 1296346155 ticks is 129 seconds.
If it's Unix time, then the following should yield the expected result;
DateTime baseTime = new DateTime(1970, 1, 1, 0, 0, 0);
Console.WriteLine(baseTime.AddSeconds(1296346155));
See Unix Time for more information.
That constructor is not what you want as the time is not measured in ticks.
DateTime start = new DateTime(1970,1,1,0,0,0,0);
start = start.AddSeconds(1296346155).ToLocalTime();
Console.WriteLine(start);
// You don't need to use ToString() in a Console.WriteLine call
Ive found the following subject where there is a conversion between unix timestamp (the one you have) and .Net DateTime
How to convert a Unix timestamp to DateTime and vice versa?
That is correct - what were you expecting it to be and why?
The constructor System.DateTime(Int64) takes the number of 100-nanosecond intervals (known as Ticks) since January 1st 0001 (in the Gregorian calendar).
Therefore, 1296346155 / 10000000 gives you the number of seconds, which is 129.6.
Therefore, this should display 2 minutes and 9 seconds since midnight on 1st January 0001.