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.
Related
I would like to convert the current time to a decimal representing a fraction of the day. For example, if the day starts at 0, then 12:00 PM should be 0.5.
I need to send that value to an API, and it needs to be in that format. i.e.
"LAST_PRINT_TIME":0.22020833"
Depending on the precision requirements of your result, this may help you:
DateTime now = DateTime.Now;
double dayFraction = (now.Hour + now.Minute / 60d) / 24d;
now.Minute / 60d calculates the fraction of the current hour (so if the time is XX:15 PM this will give 0.25). This is then added to the current hour. This value is then divided by 24 to obtain the final result.
For example, 3:45 PM would go as follows:
(15 + 45 / 60) / 24) => (15 + 0.75) / 24 => 15.75 / 24 => 0.65625
So 3:45 PM, which is 15.75 hours into the day, would be 0.65625 (or 65.625%) of the day.
Or, as #madreflection mentioned in a comment, you could use .ToOADate() as well. In this case, you could do something like:
DateTime now = DateTime.Now;
double dayFraction = now.ToOADate() - now.Date.ToOADate();
This is one of those problems that seems deceptively simple, but the solution is actually much more complex than you would think.
The complexities arise from the nature of local time, whose rules are defined by time zones. Many time zones have transitions that occur either regularly (such as for daylight saving time), or irregularly (such as for changes in standard time).
As such, one needs to consider:
Could the day be shorter or longer than 24 hours?
For example, in most of the US the start of DST is at 2:00 AM, and on that day there are 23 hours in the day because the hour from 2:00 to 2:59 is skipped. At the end of DST, also at 2:00 AM in the US, the hour from 1:00 through 1:59 is repeated, creating 25 hours in that day.
Could the day start or stop at a time other than midnight?
For example, in most of Chile in 2019, the start of DST made the date 2019-09-08 start at 01:00 instead of 00:00.
Learn more in Falsehoods programmers believe about time.
Consider using the following approach to overcome these real-world considerations.
First, define some helper functions to do most of the work. They are not specific to a particular point in time or a particular time zone.
static double GetFractionOfDay(DateTimeOffset dto, TimeZoneInfo tz)
{
// Get the start of the day, and the start of the next day
DateTimeOffset startOfDay = GetStartOfDay(dto, tz);
DateTimeOffset startOfNextDay = GetStartOfDay(startOfDay.AddDays(1), tz);
// Calculate the length of the day. It might not be 24 hours!
TimeSpan lengthOfDay = startOfNextDay - startOfDay;
// Now calculate the position within the day, and the fraction to return
TimeSpan durationSinceStartOfDay = dto - startOfDay;
return durationSinceStartOfDay / lengthOfDay;
}
static DateTimeOffset GetStartOfDay(DateTimeOffset dto, TimeZoneInfo tz)
{
// Make sure we're in the correct time zone
dto = TimeZoneInfo.ConvertTime(dto, tz);
// Start by assuming a local midnight exists
DateTime dt = dto.Date;
// Handle days without a local midnight (these do exist in several time zones)
if (tz.IsInvalidTime(dt))
{
// Advance by the transition gap. This is usually 1 hour, but best not to hard-code that.
TimeSpan[] offsets = { tz.GetUtcOffset(dt.AddDays(-1)), tz.GetUtcOffset(dt.AddDays(1)) };
TimeSpan gap = offsets[1] - offsets[0];
return new DateTimeOffset(dt.Add(gap), offsets[1]);
}
// Handle days with more than one midnight (it's possible, even if unlikely)
if (tz.IsAmbiguousTime(dt))
{
// There's more than one. Prefer the first one, since we want the beginning of the day.
TimeSpan[] offsets = tz.GetAmbiguousTimeOffsets(dt);
TimeSpan offset = offsets[0] > offsets[1] ? offsets[0] : offsets[1];
return new DateTimeOffset(dt, offset);
}
// Clean case, just one local midnight and it does exist
return new DateTimeOffset(dt, tz.GetUtcOffset(dt));
}
With those defined, you can now get an answer to your question with regard to "now" in the local time zone.
double dayFraction = GetFractionOfDay(DateTimeOffset.Now, TimeZoneInfo.Local);
However - Though this is the correct answer of "what fraction of the day is it", keep in mind it may be more important to align with what the receiving API expects, even if not exactly correct. In other words, if 12:00 should always be 0.5, even when it's not exactly at the midpoint of the day, then use elmer007's approach.
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;
}
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.
I am working with Visual Studio 2010, MVC 3 and C#. I am creating some highcharts and need to have the x-axis be a date. I am pulling the dates from a database and adding them to and array that will then be passed to highcharts. I think highcharts requires the dates to be in millisecond format. Ho do I go about converting a DateTime of '12/20/2011 5:10:13 PM" for example to milliseconds?
Once you figure out what you want to calculate milliseconds from, you can just take one DateTime object from another to get a TimeSpan object. From TimeSpan you can get TotalMilliseconds.
In other words, if start and end are DateTime objects, you can do this:
double milliseconds = (end - start).TotalMilliseconds;
You can use the DateTime.Ticks property and convert the value to milliseconds.
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.
A single tick represents one hundred nanoseconds or one ten-millionth of a second. There are 10,000 ticks in a millisecond.
The .Ticks in C# DateTime gives you the value of any time in ticks. You can thereafter convert to milliseconds as shown below:
long dateticks = DateTime.Now.Ticks;
long datemilliseconds = dateticks / TimeSpan.TicksPerMillisecond;
DateTime[] dates = ;
var minDate = dates.Min();
var msDates = dates.Select(date => (date - minDate).TotalMilliseconds).ToArray();