I'm working with an data set in which there is a date field with dates that look like the following:
42437.4261290278
42437.5460402431
42437.5478825116
with the larger of the numbers being the most recent. One of us believes it is relating to unix epoch and alternate time representations. The issue we are facing now is reading those date's above into standard MM-DD-YYYY format. Any one have any ideas on how to convert these alternate date forms into standard dates?
I'm trying to do this in C#. And for reference, I expect that the last two dates listed to be sometime on March 8th, 2016 and the first to be some time before then.
These are OLE Automation VT_DATE values. This is the date system used by Microsoft products such as Excel and pre-.NET versions of Visual Basic. It is a somewhat bizarre date format.
The format is: consider the double to have two parts: a signed integer and an unsigned fraction. The signed integer is the number of days since 30 Dec 1899. Note NOT 31 Dec 1899 and NOT 1 Jan 1900. The fraction is the fraction of the 24 hour (always!) day gone by. No adjustment is made for the 23 or 25 hour days we have twice a year.
This format has an interesting (to me) history; you can read about it in my blog article from 2003:
https://blogs.msdn.microsoft.com/ericlippert/2003/09/16/erics-complete-guide-to-vt_date/
And Stack Overflow founder Joel Spolsky's article from 2006:
http://www.joelonsoftware.com/items/2006/06/16.html
Note that if you have negative VT_DATE values then you must be very careful to get the conversion correct. The code is not hard; it's just a couple lines, but you have to reason about it carefully. I used to ask "take the difference between two VT_DATEs" as an interview question, and a surprising number of candidates cannot actually do subtraction.
Following your assertion that the dates represented are 2016-03-08, I assume the start of the epoch is 1899-12-30:
static string UnixTimeStampToDateAsString(double ts)
{
DateTime epoch = new DateTime(1899, 12, 30);
DateTime d = epoch.AddDays(ts);
return (d.ToString("yyyy-MM-dd HH:mm:ss"));
}
static void Main(string[] args)
{
foreach (double dateNumber in new double[] { 42437.4261290278, 42437.5460402431, 42437.5478825116 })
{
Console.WriteLine(UnixTimeStampToDateAsString(dateNumber));
}
Console.ReadLine();
}
Outputs:
2016-03-08 10:13:37
2016-03-08 13:06:17
2016-03-08 13:08:57
I have to state that the 30th of December 1899 is a rather unlikely value, but I suppose someone might have had a "reason" to use that.
Edit Thanks to #EricLippert I can suggest this instead:
Console.WriteLine(DateTime.FromOADate(dateNumber).ToString("yyyy-MM-dd HH:mm:ss"));
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 following 3 fields
startingdate, expirydate, number of months
startingdate = DateTimeOffset.Now;
and number of months, say 24 months
How to calculate expirydate = ?
Can anybody give me an idea?
You don't need multiplication in this case - just addition, specifying the units:
DateTimeOffset startDate = DateTimeOffset.Now;
DateTimeOffset expiryDate = startDate.AddMonths(months);
Two things to note:
Date and time arithmetic can be odd. In your example case it's less likely to be odd than normal, as you've got 2 years, so the only corner case is adding 2 years to February 29th and getting February 28th; normally you'd need to consider (say) adding 1 month to August 31st and getting September 30th. In other words, just because two expiry dates are the same doesn't mean they came from the same start date.
You might want to consider using DateTimeOffset.UtcNow and doing everything in UTC, rather than using the local time zone. Using DateTimeOffset instead of DateTime protects you from time zone problems to some extent, but keeping everything in UTC is clearer.
If you really mean you have dates rather than dates and times, you might want to explicitly use midnight... it's unfortunate that .NET doesn't have any "date-only" type. You might want to consider using my Noda Time which is designed to make things rather clearer than the BCL API.
I am trying to convert to Julian Time Stamp to Date Time. I have the following microseconds time stamp 212302469304212709. As i understand i need to add these milliseconds to the beginning of Julian Calendar (January 1, 4713 B.C., 12:00 (noon)). So i have the following method:
private DateTime GetDateTime(string julianTimeStamp)
{
var julianMilliseconds = Convert.ToDouble(julianTimeStamp)/1000;
var beginningOfTimes = new DateTime(1, 1, 1, 0, 0, 0, 0);
var dateTime = beginningOfTimes.AddMilliseconds(julianMilliseconds).AddYears(-4713).AddMonths(-1).AddDays(-1).AddHours(-12);
return dateTime;
}
Assume i pass 212302469304212709 string as the parameter. The expected result should be 2015/07(July)/01 00:08:24.212. Based on my method, i have almost the same result, but day is not 1, it is 6. Same problem for different time stamps i tested.
Could any one tell me what i am doing wrong? Thanks in advance.
Edited:
This is the exact date time i expect to receive: 2015(year) 7(month) 1(day) 0(hour) 8(minute) 24(second) 212(millisecond) 709(microsecond)
The given timestamp 212,302,469,304,212,709 μs when converted to days (just divide by 86,400,000,000) gives 2457204.505836 days (to six decimal places, which is the best I can do without a lot of extra trouble). Using the Multi Year Computer Interactive Almanac (MICA) written by the United States Naval Observatory, and putting in the free form date 2015(year) 7(month) 1(day) 0(hour) 8(minute) 24(second) 212(millisecond) 709(microsecond), the program calculates exactly the same day count (to six decimal places), proving the time stamp is an accurate Julian date.
One problem with the OP's calculation is trying to use the DateTime class before the earliest supported date, as pointed out by another poster. Also, the OP didn't say if 1 July 2015 was in the Julian or Gregorian calendar, but the MICA calculation proves it is in the Gregorian calendar. Since the OP is working in the Gregorian calendar, the epoch of Julian dates should be stated in the Gregorian proleptic calendar: Noon Universal Time, November 24, 4714 BC. The oft-quoted date January 1, 4713 BC is a proleptic Julian calendar date.
"Proleptic" means a date has been found by beginning at a modern date, who's calendar date is known with absolute certainty, and applying the rules of the chosen calendar backward until the desired date is reached, even though the desired date is before the chosen calendar was invented.
DateTime uses Gregorian calendar, so when you substract years, months and so on you are doing it with that calendar, not the Julian.
Unfortunately DateTime does not support dates before year 1. You can check the library in this post, maybe it helps you.
I have written a function in VBA which creates a code from the properties of a file.
I need a developer to do the same in C# on their end.
The developer says it is not possible to do the same in c#.
So in VBA, part of my process is to convert a date to a double. The way VBA does this is to basically count the number of days between the given date and 1 Jan 1900. So 19 Mar 2014 would be a double value of 41,717.
How would I say write a function in C# (not a language I am familiar with) that would convert a date data type to the number of days that have passed since 1 January 1900?
Any help would be appreciated.
Subtracting two DateTimes gives you a TimeSpan. You can just use TimeSpan.TotalDays to get the number of days between two dates:
double days = (DateTime.Today - new DateTime(1900,1,1)).TotalDays;
If the DateTime has a time component it will be represented as fractions of a day, so for example:
// current date and time (16:24:15)
(new DateTime(2014, 3, 18, 16, 24, 15) - new DateTime(1900,1,1)).TotalDays
would give you 41714.6835069444
Note that the result is 2 days different that using CDbl() in VBA since a date in VBA is represented by the number of days since 12/30/1899 rather than 1/1/1900.
Use .net DateTime method ToOADate() wich returns a double representing the OLE Automation date
VBA uses this same format to representa a date as a double.
I got exactly 3 days difference. Which might be because I'm in NZ at GMT + 12.
Or it might be because I was multiplying a double by "TicksPerDay" and .Net doesn't allow for some strange numbers.
DateTime.FromOADate(vbaTime) was the perfect solution for me moving dates between MS Access and C#.
Incidentally, I suspect that this is a result of the "date calculation issue" that Joel Spolsky refered to:
http://www.joelonsoftware.com/items/2006/06/16.html
when discussing Lotus notes compatibility in Excel as the program manager at Microsoft.
How about this, without using OLE Automation:
'get time elapsed since some earlier point in time, such as midnight today
Dim tim As TimeSpan = Now.Subtract(Today)
'calc the time elapsed in fractional seconds
'note that tim.Seconds only provides whole seconds, so add tim.Milliseconds
Dim tsec As Double = tim.Hours * 3600 + tim.Minutes * 60 + tim.Seconds + tim.Milliseconds / 1000
I have a code in vb, and I'm trying convert it to c#.
_nextContactDate.ToShortDateString > New Date(1900, 1, 1)
This is _nexContractDate declaration:
Private _nextContactDate As DateTime
It's weird for me. Comapre datetime to string?
What this code is doing is extracting the date part (i.e. removing the time part) and using VB's loose nature to allow a date represented as a string to be implicitly converted back to a date for the purposes of comparison with an actual date.
The correct way to remove the time part would be to check as follows:
_nextContactDate.Date > new DateTime(1900, 1, 1)
It seems odd, as this means that the 1st Jan 1900 will fail this check, and only dates from the 2nd Jan 1900 will pass. As such, I'd be inclined to check whether this code has a logic error.
I'm not sure I understand your question entirely, but why compare a DateTime to a string anyway, why not just compare dates?
if (_nextContactDate > new DateTime(1900, 1, 1))
{
}
As noted by Greg, currently the ToShortDateString removes some parts of the date (specifically, the time units), but upon comparison with a minimum date as such, this is rather redundant - if you are concerned at such a level, then you can compare only the Date members.
no, you don't need to compare DataTime variable in string format.
you can compare like below:
DateTime myDate = new DateTime(2011,8,24);
if(myDate > DateTime.MinValue)
DoSomething();
In many databases, time is stored as the minimum date + the time value.
So assuming the minimum date is 31 Dec 1899 2359H (if I reckon right, that's the minimum for Access) then 1300H will be stored as 01 Jan 1900 1300H.
Dates are stored as 'usual'. And dates with time components have the date value with the time component attached to them.
What's this got to do with the code? The original programmer is trying to determine whether the field is holding a date or a time value. The analogy is simple. If the value is time only, then by stripping off the time component, you'll be left with 01 Jan 1900. If it contains a date component, it's going to be more than 01 Jan 1900.