Question

I can't seem to find any information on how a "variant time" (DATE, double, 8-byte variable) is handled.... I have a variant time "A" which value is "41716.892329". If I convert "A" using "VariantTimeToSystemTime" (or "COleDateTime") - I get "2014-03-18 21:24:57".

  • How is this variant time calculated?
  • Is it capable of storing milliseconds?
  • Is there any way to determine if variant time is an AM or PM time?

I'm a bit confused regarding the AM/PM thing because the device that I'm working with at that moment was set to "09:24:57" (AM) and not "21:24:57" (PM).

Can this be a problem of the device SDK that provides me with an incorrect variant time?

EDIT: This was a problem of incorrect timezone set in the device (was set to "GMT-12:00" instead of "GMT-00:00")

Thanks.

Was it helpful?

Solution

The "variant time" (DATE) is almost but not quite the same as an Excel Date. Excel (because it was originally competing with Lotus-123) copied the way Lotus stored Dates (for compatibility with existing users). Unfortunately, Lotus had a bug (possibly deliberate, see Joel Spolsky's blog) which meant that February 1900 had 29 days. As such, Excel also thinks that February 1900 has 29 days (still). Try formatting the value 60 as a date. However, a 'variant time' is used with OLE Automation (not just inside Excel) and was fixed so that it could correctly represent any point in time, originally between January 1, 1753 and December 31, 2078 (ca. 2006) but now between January 1, 100 and December 31, 9999 (12/05/2018) such that a time difference could be obtained by a simple (possibly two part, see below) subtraction.

Eric Lippert has a comprehensive analysis but a summary is:

The "variant time" is a double where the integer part is the number of days after 30dec1899. As such, 0 is 30dec1899 while 01jan1900 is actually 2 (unlike Excel which copies Lotus and defines it as day 1) and there is no 29feb1900. The fractional part of the decimal string representation is treated as an unsigned partial day offset from 00:00:00 on that day. The significance of the wording here is that while numerically -1.75 is the same as -1 + -0.75, a variant time treats -1.75 as -1 + +0.75 (i.e. 3/4 of a day after midnight 29dec1899). Thus, when converting negative values to YYYY/MM/DD HH:MM:SS or calculating date differences involving negative values, the date and time parts must be treated separately.

OTHER TIPS

Ok, I think I've found all of my answers! As Simon Mourier commented, "41716.892329" is really "2014-03-18 21:24:57" and this is why - it looks like variant time is split like this: "(date) 41716 . (time) 892329". If you take "0.892329" and multiply it by 24.0, we will get 21,415896 where "21" is my hours value.

I've found some information on how to calculate this variant time on your own here: http://doxygen.reactos.org/df/d85/variant_8c_source.html - (in the "VarUdateFromDate" function)

It looks like variant time is capable of handling milliseconds, it's just functions like "VariantTimeToSystemTime" ignores them (maybe for the lack of precision reasons?). Function just rounds the milliseconds, adjusting the date-time forward if needed. (So in case we have input time "21:24:57.567", output time will be "21:24:58".

Found some interesting material here: http://www.codeproject.com/Articles/17576/SystemTime-to-VariantTime-with-Milliseconds - "SystemTime to VariantTime with Milliseconds".

The date/time format in variant is identical to the date/time format in Excel.

Here is a short view how it is calculated: The part before the decimal separator specifies the day and the part after the decimal separator specifies the time.

The base of the format is 1900-01-01 00:00:00 with the value 1. So 1900-01-02 00:00:00 has the value 2, snd so forth, with one litte exception: The value 60 represents the date 1900-02-29, which didn't exist (1900 was no leapyear).

Values >= 0 and < 1 specify times without date relation. Values smaller than 0 are undefined.

The time is simply divided to days. Hour / 24, minute / 24 / 60, second / 24 / 60 / 60.

Information about the time zone is not saved.

Milliseconds are usually not used, but could be stored.

In C++20, one can convert this to a time_point<system_clock, milliseconds> with:

using namespace std::chrono;
using ddays = duration<double, days::period>;

constexpr auto tp = round<milliseconds>(
   sys_time{ddays{41716.892329}} - (sys_seconds{} - sys_days{December/30/1899}));

static_assert(tp == sys_days{2014y/3/18} + 21h + 24min + 57s + 226ms);

At this range (from 1899-12-30) a double has a precision of about half a microsecond.

This is the formula I've used to calculate the time portion, including milliseconds. Note that it is important to round the milliseconds if this is as far as you plan to calculate.

    double date = var.date;
    double hours = (date - floor(date)) * 24;
    double minutes = (hours - floor(hours)) * 60;
    double seconds = (minutes - floor(minutes)) * 60;
    double milliseconds = round((seconds - floor(seconds)) * 1000);

Each of these are doubles, so to get the final figure cast each to int. I know this is an old question but might be useful for somebody

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top