0

How can I write a C++ function which takes a long long value representing a VMS timestamp and returns the corresponding time_t value, assuming the conversion yields a valid time_t? (I'll be parsing binary data sent over network on a commodity CentOS server, if that makes any differences.)

I've had a look into a document titled "Why Is Wednesday November 17, 1858 The Base Time For VAX/VMS" but I don't think I can write a correct implementation without testing with actual data which I don't have at hand right now, unfortunately.

If I'm not mistaken, it should be a simple arithmetic in this form:

time_t vmsTimeToTimeT(long long v) {
   return v/10'000'000 - OFFSET;
}

Could somebody tell me what value to put into OFFSET ?

Things I'm concerned about:

  • I don't want to be bitten by my local timezone
  • I don't want to be bitten by the 0.5 thing (afternoon vs midnight) in the definition of modified Julian date (though it should be helping me here; modified Julian epoch and Unix Epoch should differ by a multiple of 24 hours thanks to the definition)

I tried to compute it by myself with the help from Boost.DateTime, only to get a mysterious negative value...

int main() {
    boost::posix_time::ptime x(
        boost::gregorian::date(1858, boost::gregorian::Nov, 17),
        boost::posix_time::time_duration(0, 0, 0) );
    boost::posix_time::ptime y(
        boost::gregorian::date(1970, boost::gregorian::Jan,  1),
        boost::posix_time::time_duration(0, 0, 0) );
    std::cout << (y - x).total_seconds() << std::endl;
    std::cout << (y > x ? "y is after x" : "y is before x") << std::endl;
}

-788250496
y is after x

I used Boost 1.60 for it:

The current implementation supports dates in the range 1400-Jan-01 to 9999-Dec-31.

Update

Crap, sizeof(total_seconds()) was 4, dispite what the document says

So I got 3506716800 from

auto diff = y - x;
std::cout << diff.ticks() / diff.ticks_per_second() << std::endl;

which doesn't look too wrong but... who can assure this is really correct?

Community
  • 1
  • 1
nodakai
  • 7,773
  • 3
  • 30
  • 60

3 Answers3

2

Wow, you guys make it all appear to be so difficult with libraries and all. So you read up on November-17 1858 and found out that VMS stores the time as 100nS 'clunks' since that date. Right?

Unix times are Seconds (or microseconds) since 1-jan-1970. Right?

So all you need to do is to subtract the OpenVMS time value 'offset' for 1-jan-1970 from the reported OpenVMS times ad divide by 10,000,000 (seconds) or 10 (microseconds). You only need to find that value once using a trivial OpenVMS program. Below I did not even use a dedicated program, just used the OpenVMS interactive debugger running a random executable program:

 $ run tmp/debug
DBG> set rad hex
DBG> dep/date 10000 = "01-JAN-1970 00:00:00"  ! Local time
DBG> examin/quad 10000
TMP\main:       007C95674C3DA5C0
DBG> examin/quad/dec  10000
TMP\main:       35067168005400000

So there is you offset, both in HEX and DECIMAL to use as you see fit.

In the simplest form you pre-divide the incoming OpenVMS time by 10,000,000 and subtract 3506716800 (decimal) to get Epoch seconds. Be sure to keep the math, including the subtract to long-long int's

hth, Hein.

Hein
  • 1,453
  • 8
  • 8
  • Very interesting, so the VMS debugger has the `deposit` instruction to write an arbitrary data into an arbitrary memory location http://h41379.www4.hpe.com/doc/84final/4538/4538pro_040.html#index_x_1802 But what is "540000" in the `examine` output? An offset of 0.54 sec?? – nodakai Mar 18 '17 at 17:32
  • 1
    Try `DBG> deposit/date 10000="01-JAN-1970 00:00:00.00"` – user2116290 Mar 18 '17 at 21:22
  • 1
    Ooops, my bad. OpenVMS has this long-standing 'feature' where it defaults the parts from a date/time which are not provided to the current values. So it picked up the 54 centi seconds corresponding to the moment I entered the command. It's a bug IMHO. Anyway, I know this and should have provided the .00 fraction explicitly. The debugger can only write to arbitrary _data_ areas: static, malloc-ed, stack. I just know the static data program section just about always starts at 0x10000. The debugger knows dates, floats, packed-decimal, ascic, asciz... short,long,quad,octa ints. Handy! – Hein Mar 19 '17 at 03:55
1

According to this: https://www.timeanddate.com/date/durationresult.html?d1=17&m1=11&y1=1858&d2=1&m2=jan&y2=1970

you'd want 40587 days, times 86400 seconds, makes 3506716800 as the offset in your calculation.

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
  • Actually the website says it amounts to the said number of seconds :-) But suppose you are working for NASA, would you use that number from timeanddate.com in the flight control system? – nodakai Mar 17 '17 at 23:47
  • No, if I worked for NASA, I'd find the department within NASA that deals with date and time calculations, and ask them to give me the code they are using... ;) And if you want to double check, obviously you'd count back the number of days in each year (plus the 31 + 13 days from the 1st of Jan 1859 to 17th of Nov 1858). You'd obviously have to deal with leap years (including the fact that year 2000 is not a leap-year). Do you have a particular reason NOT to trust the timeanddate website? I mean, it's not, despite the reference to NASA, rocket science, but I admit I have not double checked. – Mats Petersson Mar 18 '17 at 08:01
  • And a rough calculation, which WILL be wrong, but is a starting point for checking this, would be to take 1970-1859 to get 111 years. Multiply by 365.25 which is the average year, gives 40542.75 (because it's not an even multiple of 4 years, it's got a fraction to it), and then add the 44 days of end of December back to November 17th, which gives us 40586.75. Obviously, to do this precisely, you'd still have to count 365 or 366 days for each year of those 111 years (and that would give you an integer result, of course). – Mats Petersson Mar 18 '17 at 08:11
1

Using this free open-source library which extends <chrono> to calendrical computations, I can confirm your figure of the offset in seconds:

#include "chrono_io.h"
#include "date.h"
#include <iostream>

int
main()
{
    using namespace date;
    using namespace std::chrono;
    using namespace std;
    seconds offset = sys_days{jan/1/1970} - sys_days{nov/17/1858};
    cout << offset << '\n';
}

Output:

3506716800s
Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
  • Interesting, but I guess scientific English orthography mandates a quantity with a unit shall be spelt as two words? Like, we never write "3apples and 5oranges" – nodakai Mar 20 '17 at 23:38
  • @nodakai: Fair complaint. I am in the process of proposing this library to the standards committee, and am happy to change it in this regard to whatever they prefer. It would/will be very easy to change the implementation in `"chrono_io.h"` in this regard (add a `' '` in one place). – Howard Hinnant Mar 21 '17 at 00:27