Skip to content
Snippets Groups Projects
DateAndTimeTest.h 15.3 KiB
Newer Older
/*
 * DateAndTimeTest.h
 *
 *  Created on: Aug 30, 2010
 *      Author: janik
 */

#ifndef DATEANDTIMETEST_H_
#define DATEANDTIMETEST_H_

#include <cxxtest/TestSuite.h>
#include "MantidKernel/DateAndTime.h"
#include "MantidKernel/System.h"
#include <sys/stat.h>
#include <time.h>

using namespace Mantid;
using namespace Mantid::Kernel;

using std::runtime_error;
using std::size_t;
using std::vector;
using std::cout;
using std::endl;

//==========================================================================================
class DateAndTimeTest: public CxxTest::TestSuite
{
public:

  void test_constructors_and_set()
  {
    //Expected will be Jan 2, 1990, at 00:01:02
    boost::posix_time::ptime expected = boost::posix_time::from_iso_string(std::string("19900102T000102.345"));
    DateAndTime d;
    //1day, 1 minute, 2 seconds, 0.345 seconds = 86462345000000 nanosec
    //Nanoseconds constructor
    d = DateAndTime( int64_t(86462345000000LL) );
    TS_ASSERT_EQUALS(d, expected);

    //Second, nanosec constructor
    d = DateAndTime(86462, 345000000);
    TS_ASSERT_EQUALS(d, expected);
    d = DateAndTime(86462.0, 345000000.0);
    TS_ASSERT_EQUALS(d, expected);
    //ptime
    d = DateAndTime(expected);
    TS_ASSERT_EQUALS(d, expected);

    //string
    d = DateAndTime("1990-01-02T00:01:02.345");
    TS_ASSERT_EQUALS(d, expected);
    d.setFromISO8601("1990-01-02T00:01:02.345");
    TS_ASSERT_EQUALS(d, expected);

    //string with a space
    d = DateAndTime("1990-01-02 00:01:02.345");
    TS_ASSERT_EQUALS(d, expected);
    d.setFromISO8601("1990-01-02 00:01:02.345");
    TS_ASSERT_EQUALS(d, expected);
  }


  void test_limits_on_construction()
  {
    //direct nanoseconds constructor
    a = DateAndTime(int64_t(6917529027641081856LL));
    TS_ASSERT_EQUALS( a, DateAndTime::maximum());
    a = DateAndTime(int64_t(-6917529027641081856LL));

    //Double constructor
    TS_ASSERT_EQUALS( a, DateAndTime::minimum());
    a = DateAndTime(1e20, 0.2);
    TS_ASSERT_EQUALS( a, DateAndTime::maximum());
    a = DateAndTime(-1e20, 0.2);
    TS_ASSERT_EQUALS( a, DateAndTime::minimum());

    //long int constructor
    int64_t li = 1000000000000000000LL;
    int64_t li2 = 2000000LL;
    a = DateAndTime(li, li2);
    TS_ASSERT_EQUALS( a, DateAndTime::maximum());
    a = DateAndTime(-li, li2);
    TS_ASSERT_EQUALS( a, DateAndTime::minimum());

    //String constructors
    a = DateAndTime("2490-01-02 00:01:02.345");
    TS_ASSERT_EQUALS( a, DateAndTime::maximum());
    a = DateAndTime("1600-01-02 00:01:02.345");
    TS_ASSERT_EQUALS( a, DateAndTime::minimum());

    //ptime constructor
    boost::posix_time::ptime p;
    p = boost::posix_time::from_iso_string("24000102T000102");
    a = DateAndTime(p);
    TS_ASSERT_EQUALS( a, DateAndTime::maximum());
    p = boost::posix_time::from_iso_string("16000102T000102");
    a = DateAndTime(p);
    TS_ASSERT_EQUALS( a, DateAndTime::minimum());

    //time_t and int constructors can't overflow on 32-bits at least
  }

  void test_year_month_etc()
  {
    DateAndTime a;
    a = DateAndTime("1990-01-02 03:04:05.678");
    TS_ASSERT_EQUALS( a.year(), 1990);
    TS_ASSERT_EQUALS( a.month(), 1);
    TS_ASSERT_EQUALS( a.day(), 2);
    TS_ASSERT_EQUALS( a.hour(), 3);
    TS_ASSERT_EQUALS( a.minute(), 4);
    TS_ASSERT_EQUALS( a.second(), 5);
    TS_ASSERT_EQUALS( a.nanoseconds(), 678000000);
  }

  void test_comparison_operators()
  {
    DateAndTime a,b,c;
    a = DateAndTime("1990-01-02 00:00:02.000");
    b = DateAndTime("1990-01-02 00:01:02.345");
    c = DateAndTime("1990-01-02 00:01:02.345");
    TS_ASSERT( a < b );
    TS_ASSERT( b > a );
    TS_ASSERT( a <= b );
    TS_ASSERT( b >= a );
    TS_ASSERT( a == a );
    TS_ASSERT( b == b );
    TS_ASSERT( b == c );
    TS_ASSERT( a != b );

    boost::posix_time::ptime p;
    p = boost::posix_time::from_iso_string("19900102T000002.000");
    TS_ASSERT( a == p );
    TS_ASSERT( b != p );

  }

  void test_toFormattedString()
  {
    DateAndTime a;
    a = DateAndTime("1990-01-02 03:04:05.678");
    std::string s = a.toSimpleString();
    TS_ASSERT_EQUALS( s.substr(0,20), "1990-Jan-02 03:04:05");
    TS_ASSERT_EQUALS( a.toFormattedString(), "1990-Jan-02 03:04:05");
    TS_ASSERT_EQUALS( a.toFormattedString("%Y-%m-%d"), "1990-01-02");
    TS_ASSERT_EQUALS( a.toISO8601String(), "1990-01-02T03:04:05.678000000");
  void test_to_int64()
  {
    DateAndTime a;
    a = DateAndTime("1990-01-02 00:01:02.345");
    int64_t nanosec = a.totalNanoseconds();
    //1day, 1 minute, 2 seconds, 0.345 seconds = 86462345000000 nanosec
    TS_ASSERT_EQUALS( nanosec, int64_t(86462345000000LL) );
   }

  void test_stream_operator()
  {
    DateAndTime a;
    std::ostringstream message;
    a = DateAndTime("1990-01-02 03:04:05.678");
    message << a;
    TS_ASSERT_EQUALS( message.str(), a.toSimpleString() );
    std::ostringstream message2;
    message2 << a << "\n";
    TS_ASSERT_EQUALS( message2.str(), a.toSimpleString()+"\n" );
  }


  void test_subtraction_of_times()
  {
    DateAndTime a,b,c;
    boost::posix_time::ptime p;
    time_duration td;

    a = DateAndTime("1990-01-02 00:01:02.345");
    b = DateAndTime("1990-01-02 00:00:02.000");
    td = a-b;
    TS_ASSERT_EQUALS( td, DateAndTime::durationFromNanoseconds(int64_t(60345000000LL)) );

    a = DateAndTime("1990-01-02 00:01:02.345");
    p = boost::posix_time::from_iso_string("19900102T000002.000");
    //boost ptime gets converted to ptime implicitely
    td = a-p;
    TS_ASSERT_EQUALS( td, DateAndTime::durationFromNanoseconds(int64_t(60345000000LL)) );
  void test_subtraction_of_times_limits()
  {
    DateAndTime a,b,c;
    boost::posix_time::ptime p;
    time_duration td;

    a = DateAndTime("2200-01-02 00:01:02.345");
    b = DateAndTime("1800-01-02 00:01:02.345");
    td = a-b;
    //The difference won't be correct, but it is positive and ~2**62 nanoseconds
    TS_ASSERT_LESS_THAN( 4.6e9, DateAndTime::secondsFromDuration(td) );

    td = b-a;
    //The difference won't be correct, but it is negative
    TS_ASSERT_LESS_THAN( DateAndTime::secondsFromDuration(td), -4.6e9 );
  }


  void test_addition_and_subtraction_operators_nanoseconds_as_int()
  {
    DateAndTime a,b,c;
    a = DateAndTime("1990-01-02 00:00:02.000");
    b = DateAndTime("1990-01-02 00:01:02.345");
    TS_ASSERT_EQUALS( c, b);
    TS_ASSERT_EQUALS( a, b);

    a = DateAndTime("1990-01-02 00:00:02.000");
    b = DateAndTime("1990-01-02 00:01:02.345");
    TS_ASSERT_EQUALS( c, a);
    TS_ASSERT_EQUALS( b, a);
  }

  void test_addition_and_subtraction_operators_time_duration()
  {
    DateAndTime a,b,c;
    a = DateAndTime("1990-01-02 00:00:02.000");
    b = DateAndTime("1990-01-02 00:01:02.345");
    c = a + DateAndTime::durationFromNanoseconds(int64_t(60345000000LL));
    a += DateAndTime::durationFromNanoseconds(int64_t(60345000000LL));
    TS_ASSERT_EQUALS( a, b);

    a = DateAndTime("1990-01-02 00:00:02.000");
    b = DateAndTime("1990-01-02 00:01:02.345");
    c = b - DateAndTime::durationFromNanoseconds(int64_t(60345000000LL));
    b -= DateAndTime::durationFromNanoseconds(int64_t(60345000000LL));
    TS_ASSERT_EQUALS( b, a);
  }


  void test_addition_and_subtraction_operators_double()
  {
    DateAndTime a,b,c;
    a = DateAndTime("1990-01-02 00:00:02.000");
    b = DateAndTime("1990-01-02 00:01:02.345");
    c = a + 60.345;
    TS_ASSERT_EQUALS( c, b);
    a += 60.345;
    TS_ASSERT_EQUALS( a, b);

    a = DateAndTime("1990-01-02 00:00:02.000");
    b = DateAndTime("1990-01-02 00:01:02.345");
    c = b - 60.345;
    TS_ASSERT_EQUALS( c, a);
    b -= 60.345;
    TS_ASSERT_EQUALS( b, a);
  }

  void test_limits_on_addition_and_subtraction()
  {
    DateAndTime a,b,c;
    a = DateAndTime("1990-01-02 00:00:02.000");
    b = a + 1e20;
    TS_ASSERT_EQUALS( b, DateAndTime::maximum());
    b = a - 1e20;
    TS_ASSERT_LESS_THAN( b.year(), 1900);

    a = DateAndTime("1989-01-02 00:00:02.000");
    b = a - 1e20;
    TS_ASSERT_EQUALS( b, DateAndTime::minimum());
    b = a + 1e20;
    TS_ASSERT_LESS_THAN( 2000, b.year());
  }


    TS_ASSERT_EQUALS( sizeof( DateAndTime ), 8 );
  }

  void test_time_t_support()
  {
    DateAndTime t;
    std::time_t current = time (NULL);
    t.set_from_time_t( current );
//    if (cur.day() < 28) // Annoying bug at the end of a month
    {
      TS_ASSERT_EQUALS( current, t.to_time_t() );
    }
  {
    //Use the c-method to get current (local) time
    std::time_t current_t = DateAndTime::getCurrentTime().to_time_t() ;
    std::tm * current = gmtime( &current_t );
    //std::cout << "UTC time is " << current->tm_hour << "h" << current->tm_min << "\n";
    TS_ASSERT( current->tm_year >= 110 ); //Wrote this in 2010, so the year must be > 110
  {
    int hour = 12;

    std::time_t rawtime;
    std::time(&rawtime); //current time will be overwritten

    std::tm * timeinfo = new std::tm;
    timeinfo->tm_isdst = -1;
    timeinfo->tm_year = 108;
    timeinfo->tm_mon = 1;
    timeinfo->tm_mday = 29;
    timeinfo->tm_hour = hour;
    timeinfo->tm_min = 0;
    timeinfo->tm_sec = 0;
    //Convert to time_t but assuming the tm is specified in UTC time.
    std::time_t utc_time_t =  Mantid::Kernel::DateAndTimeHelpers::utc_mktime ( timeinfo );
    //This will be the local time
    std::time_t local_time_t =  std::mktime( timeinfo );
    //our format, as utc
    DateAndTime utc_time;
    utc_time.set_from_time_t(utc_time_t);

    //Timezone offset in hours (sorry, newfoundland and labrador - half time zones are crazy! )
    int tz_offset = static_cast<int>( difftime(utc_time_t, local_time_t) / 3600 );
    TS_ASSERT_EQUALS( utc_tm.tm_hour, hour);

    //Get tm in localtime
    std::tm local_tm = utc_time.to_localtime_tm();
    TS_ASSERT_EQUALS( local_tm.tm_hour, hour + tz_offset);

    //Now the time_t conversion, UTC time
    TS_ASSERT_EQUALS( utc_time.to_time_t(), utc_time_t);

    //Now the time_t conversion, local time
    TS_ASSERT_EQUALS( utc_time.to_localtime_t(), local_time_t);
    TS_ASSERT_EQUALS( utc_time.toSimpleString(), "2008-Feb-29 12:00:00");
  void test_ISO8601_string_with_timezones()
  {
    //Time without timezone : UTC assumed
    DateAndTime time_no_tz = DateAndTime("2010-03-24T14:12:51.562");
    DateAndTime time_no_fraction = DateAndTime("2010-03-24T14:12:51");
    TS_ASSERT_DELTA(  Mantid::Kernel::DateAndTime::secondsFromDuration( time_no_tz-time_no_fraction ), 0.562, 0.0005);
    DateAndTime time_z = DateAndTime("2010-03-24T14:12:51.562Z");
    //Positive time offset (also a fraction like Newfoundland (crazy newfies ;) )
    DateAndTime time_positive_tz = DateAndTime("2010-03-24T19:42:51.562+05:30");
    DateAndTime time_positive_tz2 = DateAndTime("2010-03-24T16:12:51.562+02");
    DateAndTime time_negative_tz = DateAndTime("2010-03-24T10:12:51.562-04:00");
    DateAndTime time_negative_tz2 = DateAndTime("2010-03-24T06:12:51.562-08");
    TS_ASSERT_DELTA(  Mantid::Kernel::DateAndTime::secondsFromDuration( time_no_tz-time_z ),  0.0, 1e-4);
    TS_ASSERT_DELTA(  Mantid::Kernel::DateAndTime::secondsFromDuration( time_no_tz-time_positive_tz ),  0.0, 1e-4);
    TS_ASSERT_DELTA(  Mantid::Kernel::DateAndTime::secondsFromDuration( time_no_tz-time_negative_tz ),  0.0, 1e-4);
    TS_ASSERT_DELTA(  Mantid::Kernel::DateAndTime::secondsFromDuration( time_no_tz-time_positive_tz2 ),  0.0, 1e-4);
    TS_ASSERT_DELTA(  Mantid::Kernel::DateAndTime::secondsFromDuration( time_no_tz-time_negative_tz2 ),  0.0, 1e-4);
  void testDurations()
  {
    time_duration onesec = time_duration(0,0,1,0);
    TS_ASSERT_EQUALS( DateAndTime::secondsFromDuration(onesec), 1.0 );
    onesec = DateAndTime::durationFromSeconds(1.0);
    TS_ASSERT_EQUALS( DateAndTime::secondsFromDuration(onesec), 1.0 );
    time_duration td = DateAndTime::durationFromSeconds(1e-6);
    TS_ASSERT_DELTA( DateAndTime::secondsFromDuration(td), 1e-6, 1e-9 );
    DateAndTime dt = DateAndTime(0);
    DateAndTime dt2 = dt + td;
    TS_ASSERT_DELTA( DateAndTime::secondsFromDuration(dt2-dt), 1e-6, 1e-9 );
    td = DateAndTime::durationFromSeconds(12.345);
    TS_ASSERT_DELTA( DateAndTime::secondsFromDuration(td), 12.345, 1e-9 );
    dt2 = dt + DateAndTime::durationFromSeconds(123.5e-3);
    TS_ASSERT_DELTA( DateAndTime::secondsFromDuration(dt2-dt), 123.5e-3, 1e-9 );
    dt2 = dt + DateAndTime::durationFromSeconds(15.2345);
    TS_ASSERT_DELTA( DateAndTime::secondsFromDuration(dt2-dt), 15.2345, 1e-9 );
    dt2 = dt + DateAndTime::durationFromSeconds(152.345);
    TS_ASSERT_DELTA( DateAndTime::secondsFromDuration(dt2-dt), 152.345, 1e-9 );
  /* Ensure that exceptions thrown by boost date_time conversions are caught where they
     may cause problems. */
  void testNotADateTime()
  {
    boost::posix_time::ptime time(boost::posix_time::not_a_date_time);
    TS_ASSERT_THROWS(boost::posix_time::to_tm(time), std::out_of_range);
    TS_ASSERT_THROWS_NOTHING(dt.to_tm());
    a = DateAndTime("2010-03-24T14:12:51.562");
    // Only about 290 years time difference are supported (2^63 nanoseconds)!
    b = DateAndTime("2300-03-24T14:12:51.562");
    td = b-a;
    c = a + td;
    TS_ASSERT_EQUALS( c, b);
  }


  void test_duration_from_seconds_Extremes()
  {
    time_duration onesec = time_duration(0,0,1,0);
    time_duration extreme;
    extreme = DateAndTime::durationFromSeconds(1e20);
    //Output value is positive
    extreme = DateAndTime::durationFromSeconds(-1e20);
    //Output value is negative
  void test_Vector()
  {
    DateAndTime a = DateAndTime("1990-01-02 03:04:05.000");
    std::vector<double> secs;
    secs.push_back(1.0);
    secs.push_back(2.0);
    secs.push_back(0.5);
    secs.push_back(-3.0);

    std::vector<DateAndTime> times;
    DateAndTime::createVector(a, secs, times);
    TS_ASSERT_EQUALS( times.size(), secs.size());
    TS_ASSERT_EQUALS( times[0], DateAndTime("1990-01-02 03:04:06.000"));
    TS_ASSERT_EQUALS( times[1], DateAndTime("1990-01-02 03:04:07.000"));
    TS_ASSERT_EQUALS( times[2], DateAndTime("1990-01-02 03:04:05.500"));
    TS_ASSERT_EQUALS( times[3], DateAndTime("1990-01-02 03:04:02.000"));
  void test_stringIsISO8601()
    TS_ASSERT( DateAndTime::stringIsISO8601("1990-01-02 03:04:02.000") );
    TS_ASSERT( DateAndTime::stringIsISO8601("1990-01-02T03:04:02.000") );
    TS_ASSERT( DateAndTime::stringIsISO8601("1990-01-02T03:04:02.000+05:30") );
    TS_ASSERT( DateAndTime::stringIsISO8601("1990-01-02 03:04") );
    TS_ASSERT( DateAndTime::stringIsISO8601("1990-01-02") );
    TS_ASSERT( DateAndTime::stringIsISO8601("1822-01-02") );

    TS_ASSERT(!DateAndTime::stringIsISO8601("January 1, 2345") );
    TS_ASSERT(!DateAndTime::stringIsISO8601("2010-31-56") );
    TS_ASSERT(!DateAndTime::stringIsISO8601("1990-01-02 45:92:22") );