/* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "stdafx.h" #include "simulationtime.h" #include "Globals.h" #include "utilities.h" namespace simulation { scenario_time Time; } // simulation void scenario_time::init(std::time_t timestamp) { char monthdaycounts[ 2 ][ 13 ] = { { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } }; ::memcpy( m_monthdaycounts, monthdaycounts, sizeof( monthdaycounts ) ); // potentially adjust scenario clock auto const requestedtime { clamp_circular( m_time.wHour * 60 + m_time.wMinute + Global.ScenarioTimeOffset * 60, 24 * 60 ) }; auto const requestedhour { ( requestedtime / 60 ) % 24 }; auto const requestedminute { requestedtime % 60 }; // cache requested elements, if any if( timestamp != 0 ) { std::tm *tms = std::gmtime( ×tamp ); m_time.wYear = tms->tm_year + 1900; m_time.wMonth = tms->tm_mon + 1; m_time.wDayOfWeek = tms->tm_wday; m_time.wDay = tms->tm_mday; m_time.wHour = tms->tm_hour; m_time.wMinute = tms->tm_min; m_time.wSecond = tms->tm_sec; m_time.wMilliseconds = 0; } else { ::GetLocalTime( &m_time ); } if( Global.fMoveLight > 0.f ) { // day and month of the year can be overriden by scenario setup daymonth( m_time.wDay, m_time.wMonth, m_time.wYear, static_cast( Global.fMoveLight ) ); } if( requestedhour != -1 ) { m_time.wHour = static_cast( clamp( requestedhour, 0, 23 ) ); } if( requestedminute != -1 ) { m_time.wMinute = static_cast( clamp( requestedminute, 0, 59 ) ); } // if the time is taken from the local clock leave the seconds intact, otherwise set them to zero if( ( requestedhour != -1 ) || ( requestedminute != 1 ) ) { m_time.wSecond = 0; } m_yearday = year_day( m_time.wDay, m_time.wMonth, m_time.wYear ); // calculate time zone bias // retrieve relevant time zone info from system registry (or fall back on supplied default) // TODO: select timezone matching defined geographic location and/or country struct registry_time_zone_info { long Bias; long StandardBias; long DaylightBias; SYSTEMTIME StandardDate; SYSTEMTIME DaylightDate; } registrytimezoneinfo = { -60, 0, -60, { 0, 10, 0, 5, 3, 0, 0, 0 }, { 0, 3, 0, 5, 2, 0, 0, 0 } }; #ifdef _WIN32 TCHAR timezonekeyname[] { TEXT( "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones\\Central European Standard Time" ) }; HKEY timezonekey { NULL }; DWORD size { sizeof( registrytimezoneinfo ) }; if( ::RegOpenKeyEx( HKEY_LOCAL_MACHINE, timezonekeyname, 0, KEY_QUERY_VALUE, &timezonekey ) == ERROR_SUCCESS ) { ::RegQueryValueEx( timezonekey, "TZI", NULL, NULL, (BYTE *)®istrytimezoneinfo, &size ); } #endif convert_transition_time( registrytimezoneinfo.StandardDate ); convert_transition_time( registrytimezoneinfo.DaylightDate ); TIME_ZONE_INFORMATION timezoneinfo { 0 }; timezoneinfo.Bias = registrytimezoneinfo.Bias; timezoneinfo.DaylightBias = registrytimezoneinfo.DaylightBias; timezoneinfo.DaylightDate = registrytimezoneinfo.DaylightDate; timezoneinfo.StandardBias = registrytimezoneinfo.StandardBias; timezoneinfo.StandardDate = registrytimezoneinfo.StandardDate; auto zonebias { timezoneinfo.Bias }; if( m_yearday < year_day( timezoneinfo.DaylightDate.wDay, timezoneinfo.DaylightDate.wMonth, m_time.wYear ) ) { zonebias += timezoneinfo.StandardBias; } else if( m_yearday < year_day( timezoneinfo.StandardDate.wDay, timezoneinfo.StandardDate.wMonth, m_time.wYear ) ) { zonebias += timezoneinfo.DaylightBias; } else { zonebias += timezoneinfo.StandardBias; } m_timezonebias = ( zonebias / 60.0 ); } void scenario_time::update( double const Deltatime ) { m_milliseconds += ( 1000.0 * Deltatime ); while( m_milliseconds >= 1000.0 ) { ++m_time.wSecond; m_milliseconds -= 1000.0; } m_time.wMilliseconds = std::floor( m_milliseconds ); while( m_time.wSecond >= 60 ) { ++m_time.wMinute; m_time.wSecond -= 60; } while( m_time.wMinute >= 60 ) { ++m_time.wHour; m_time.wMinute -= 60; } while( m_time.wHour >= 24 ) { ++m_time.wDay; ++m_time.wDayOfWeek; if( m_time.wDayOfWeek >= 7 ) { m_time.wDayOfWeek -= 7; } m_time.wHour -= 24; } int leap { is_leap( m_time.wYear ) }; while( m_time.wDay > m_monthdaycounts[ leap ][ m_time.wMonth ] ) { m_time.wDay -= m_monthdaycounts[ leap ][ m_time.wMonth ]; ++m_time.wMonth; // unlikely but we might've entered a new year if( m_time.wMonth > 12 ) { ++m_time.wYear; leap = is_leap( m_time.wYear ); m_time.wMonth -= 12; } } } int scenario_time::year_day( int Day, const int Month, const int Year ) const { char const daytab[ 2 ][ 13 ] = { { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } }; int const leap { is_leap( Year ) }; for( int i = 1; i < Month; ++i ) Day += daytab[ leap ][ i ]; return Day; } void scenario_time::daymonth( WORD &Day, WORD &Month, WORD const Year, WORD const Yearday ) { WORD daytab[ 2 ][ 13 ] = { { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } }; int const leap { is_leap( Year ) }; WORD idx = 1; while( ( idx < 13 ) && ( Yearday >= daytab[ leap ][ idx ] ) ) { ++idx; } Month = idx; Day = Yearday - daytab[ leap ][ idx - 1 ]; } int scenario_time::julian_day() const { int yy = ( m_time.wYear < 0 ? m_time.wYear + 1 : m_time.wYear ) - std::floor( ( 12 - m_time.wMonth ) / 10.f ); int mm = m_time.wMonth + 9; if( mm >= 12 ) { mm -= 12; } int K1 = std::floor( 365.25 * ( yy + 4712 ) ); int K2 = std::floor( 30.6 * mm + 0.5 ); // for dates in Julian calendar int JD = K1 + K2 + m_time.wDay + 59; // for dates in Gregorian calendar; 2299160 is October 15th, 1582 const int gregorianswitchday = 2299160; if( JD > gregorianswitchday ) { int K3 = std::floor( std::floor( ( yy * 0.01 ) + 49 ) * 0.75 ) - 38; JD -= K3; } return JD; } void scenario_time::set_time(int yearday, int minute) { m_yearday = yearday; daymonth(m_time.wDay, m_time.wMonth, m_time.wYear, m_yearday); m_time.wHour = minute / 60; m_time.wMinute = minute % 60; } // calculates day of week for provided date int scenario_time::day_of_week( int const Day, int const Month, int const Year ) const { // using Zeller's congruence, http://en.wikipedia.org/wiki/Zeller%27s_congruence int const q = Day; int const m = Month > 2 ? Month : Month + 12; int const y = Month > 2 ? Year : Year - 1; int const h = ( q + ( 26 * ( m + 1 ) / 10 ) + y + ( y / 4 ) + 6 * ( y / 100 ) + ( y / 400 ) ) % 7; /* return ( (h + 5) % 7 ) + 1; // iso week standard, with monday = 1 */ return ( (h + 6) % 7 ) + 1; // sunday = 1 numbering method, used in north america, japan } // calculates day of month for specified weekday of specified month of the year int scenario_time::day_of_month( int const Week, int const Weekday, int const Month, int const Year ) const { int day = 0; int dayoffset = weekdays( day_of_week( 1, Month, Year ), Weekday ); day = ( Week - 1 ) * 7 + 1 + dayoffset; if( Week == 5 ) { // 5th week potentially indicates last week in the month, not necessarily actual 5th char const daytab[ 2 ][ 13 ] = { { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } }; int const leap { is_leap( Year ) }; while( day > daytab[ leap ][ Month ] ) { day -= 7; } } return day; } // returns number of days between specified days of week int scenario_time::weekdays( int const First, int const Second ) const { if( Second >= First ) { return Second - First; } else { return 7 - First + Second; } } // helper, converts provided time transition date to regular date void scenario_time::convert_transition_time( SYSTEMTIME &Time ) const { // NOTE: windows uses 0-6 range for days of week numbering, our methods use 1-7 Time.wDay = day_of_month( Time.wDay, Time.wDayOfWeek + 1, Time.wMonth, m_time.wYear ); } bool scenario_time::is_leap( int const Year ) const { return ( ( Year % 4 == 0 ) && ( ( Year % 100 != 0 ) || ( Year % 400 == 0 ) ) ); } //---------------------------------------------------------------------------