2015-03-26 19:59:06 -05:00
/********************************************************************\
* gnc - datetime . cpp - - Date and Time classes for GnuCash *
* *
* Copyright 2015 John Ralls < jralls @ ceridwen . us > *
* *
* This program is free software ; you can redistribute it and / or *
* modify it under the terms of the GNU General Public License as *
* published by the Free Software Foundation ; either version 2 of *
* the License , or ( at your option ) any later version . *
* *
* This program is distributed in the hope that it will be useful , *
* but WITHOUT ANY WARRANTY ; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the *
* GNU General Public License for more details . *
* *
* You should have received a copy of the GNU General Public License *
* along with this program ; if not , contact : *
* *
* Free Software Foundation Voice : + 1 - 617 - 542 - 5942 *
* 51 Franklin Street , Fifth Floor Fax : + 1 - 617 - 542 - 2652 *
* Boston , MA 02110 - 1301 , USA gnu @ gnu . org *
* *
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
extern " C "
{
# include "config.h"
# include "platform.h"
}
# include <boost/date_time/gregorian/gregorian.hpp>
# include <memory>
# include "gnc-timezone.hpp"
# include "gnc-datetime.hpp"
using Date = boost : : gregorian : : date ;
using Month = boost : : gregorian : : greg_month ;
using PTime = boost : : posix_time : : ptime ;
using LDT = boost : : local_time : : local_date_time ;
using Duration = boost : : posix_time : : time_duration ;
using LDTBase = boost : : local_time : : local_date_time_base < PTime , boost : : date_time : : time_zone_base < PTime , char > > ;
using time64 = int64_t ;
static const TimeZoneProvider tzp ;
// For converting to/from POSIX time.
static const PTime unix_epoch ( Date ( 1970 , boost : : gregorian : : Jan , 1 ) ,
boost : : posix_time : : seconds ( 0 ) ) ;
2015-04-07 17:36:40 -05:00
/* To ensure things aren't overly screwed up by setting the nanosecond clock for boost::date_time. Don't do it, though, it doesn't get us anything and slows down the date/time library. */
# ifndef BOOST_DATE_TIME_HAS_NANOSECONDS
static constexpr auto ticks_per_second = INT64_C ( 1000000 ) ;
# else
static constexpr auto ticks_per_second = INT64_C ( 1000000000 ) ;
# endif
2015-03-26 19:59:06 -05:00
/** Private implementation of GncDate. See the documentation for that class.
*/
class GncDateImpl
{
public :
2015-04-07 17:36:40 -05:00
GncDateImpl ( ) : m_greg ( unix_epoch . date ( ) ) { }
2015-03-26 19:59:06 -05:00
GncDateImpl ( const int year , const int month , const int day ) :
m_greg ( year , static_cast < Month > ( month ) , day ) { }
2015-04-07 17:36:40 -05:00
GncDateImpl ( Date d ) : m_greg ( d ) { }
void today ( ) { m_greg = boost : : gregorian : : day_clock : : local_day ( ) ; }
2015-03-26 19:59:06 -05:00
private :
Date m_greg ;
} ;
/** Private implementation of GncDateTime. See the documentation for that class.
*/
static LDT
LDT_from_unix_local ( const time64 time )
{
PTime temp ( unix_epoch . date ( ) ,
boost : : posix_time : : hours ( time / 3600 ) +
boost : : posix_time : : seconds ( time % 3600 ) ) ;
auto tz = tzp . get ( temp . date ( ) . year ( ) ) ;
return LDT ( temp , tz ) ;
}
2015-04-26 18:44:39 -05:00
static LDT
LDT_from_struct_tm ( const struct tm tm )
{
auto tdate = boost : : gregorian : : date_from_tm ( tm ) ;
auto tdur = boost : : posix_time : : time_duration ( tm . tm_hour , tm . tm_min ,
tm . tm_sec , 0 ) ;
auto tz = tzp . get ( tdate . year ( ) ) ;
return LDT ( PTime ( tdate , tdur ) , tz ) ;
}
2015-04-07 17:36:40 -05:00
class GncDateTimeImpl
{
public :
GncDateTimeImpl ( ) : m_time ( unix_epoch , tzp . get ( unix_epoch . date ( ) . year ( ) ) ) { }
GncDateTimeImpl ( const time64 time ) : m_time ( LDT_from_unix_local ( time ) ) { }
2015-04-26 18:44:39 -05:00
GncDateTimeImpl ( const struct tm tm ) : m_time ( LDT_from_struct_tm ( tm ) ) { }
2015-04-07 17:36:40 -05:00
GncDateTimeImpl ( PTime & & pt ) : m_time ( pt , tzp . get ( pt . date ( ) . year ( ) ) ) { }
GncDateTimeImpl ( LDT & & ldt ) : m_time ( ldt ) { }
2015-04-10 11:33:51 -05:00
operator time64 ( ) const ;
2015-04-26 18:44:39 -05:00
operator struct tm ( ) const ;
2015-04-07 17:36:40 -05:00
void now ( ) { m_time = boost : : local_time : : local_sec_clock : : local_time ( tzp . get ( boost : : gregorian : : day_clock : : local_day ( ) . year ( ) ) ) ; }
2015-04-26 18:44:39 -05:00
long offset ( ) const ;
2015-04-26 20:01:23 -05:00
std : : string format ( const char * format ) const ;
2015-04-07 17:36:40 -05:00
private :
LDT m_time ;
} ;
2015-04-10 11:33:51 -05:00
GncDateTimeImpl : : operator time64 ( ) const
{
auto duration = m_time . utc_time ( ) - unix_epoch ;
auto secs = duration . ticks ( ) ;
secs / = ticks_per_second ;
return secs ;
2015-04-07 17:36:40 -05:00
}
2015-04-26 18:44:39 -05:00
GncDateTimeImpl : : operator struct tm ( ) const
{
return to_tm ( m_time ) ;
}
long
GncDateTimeImpl : : offset ( ) const
{
auto offset = m_time . local_time ( ) - m_time . utc_time ( ) ;
return offset . total_seconds ( ) ;
}
2015-04-26 20:01:23 -05:00
std : : string
GncDateTimeImpl : : format ( const char * format ) const
{
using Facet = boost : : local_time : : local_time_facet ;
std : : stringstream ss ;
//The stream destructor frees the facet, so it must be heap-allocated.
auto output_facet ( new Facet ( format ) ) ;
ss . imbue ( std : : locale ( std : : locale ( ) , output_facet ) ) ;
ss < < m_time ;
return ss . str ( ) ;
}
2015-04-07 17:36:40 -05:00
/* =================== Presentation-class Implementations ====================*/
2015-03-26 19:59:06 -05:00
GncDate : : GncDate ( ) : m_impl { new GncDateImpl } { }
GncDate : : GncDate ( int year , int month , int day ) :
m_impl ( new GncDateImpl ( year , month , day ) ) { }
GncDate : : ~ GncDate ( ) = default ;
2015-04-07 17:36:40 -05:00
void
GncDate : : today ( )
{
m_impl - > today ( ) ;
}
2015-03-26 19:59:06 -05:00
GncDateTime : : GncDateTime ( ) : m_impl ( new GncDateTimeImpl ) { }
2015-04-26 18:44:39 -05:00
GncDateTime : : GncDateTime ( const time64 time ) :
m_impl ( new GncDateTimeImpl ( time ) ) { }
GncDateTime : : GncDateTime ( const struct tm tm ) :
m_impl ( new GncDateTimeImpl ( tm ) ) { }
2015-03-26 19:59:06 -05:00
GncDateTime : : ~ GncDateTime ( ) = default ;
2015-04-07 17:36:40 -05:00
void
GncDateTime : : now ( )
{
m_impl - > now ( ) ;
}
2015-04-10 11:33:51 -05:00
GncDateTime : : operator time64 ( ) const
{
return m_impl - > operator time64 ( ) ;
2015-04-07 17:36:40 -05:00
}
2015-04-26 18:44:39 -05:00
GncDateTime : : operator struct tm ( ) const
{
return m_impl - > operator struct tm ( ) ;
}
long
GncDateTime : : offset ( ) const
{
return m_impl - > offset ( ) ;
}
2015-04-26 20:01:23 -05:00
std : : string
GncDateTime : : format ( const char * format ) const
{
return m_impl - > format ( format ) ;
}