SpTime.cpp
上传用户:nameszq
上传日期:2014-08-12
资源大小:336k
文件大小:16k
源码类别:

金融证券系统

开发平台:

Visual C++

  1. // This is a part of the Microsoft Foundation Classes C++ library.
  2. // Copyright (C) 1992-1998 Microsoft Corporation
  3. // All rights reserved.
  4. //
  5. // This source code is only intended as a supplement to the
  6. // Microsoft Foundation Classes Reference and related
  7. // electronic documentation provided with the library.
  8. // See these sources for detailed information regarding the
  9. // Microsoft Foundation Classes product.
  10. #include "stdafx.h"
  11. #include "SpTime.h"
  12. #include "Stockdrv.h"
  13. #include <TCHAR.H>
  14. #ifdef AFX_AUX_SEG
  15. #pragma code_seg(AFX_AUX_SEG)
  16. #endif
  17. #ifdef _DEBUG
  18. #undef THIS_FILE
  19. static char THIS_FILE[] = __FILE__;
  20. #endif
  21. /////////////////////////////////////////////////////////////////////////////
  22. // CSPTimeSpan
  23. CSPTimeSpan::CSPTimeSpan()
  24. { }
  25. CSPTimeSpan::CSPTimeSpan(time_t time)
  26. { m_timeSpan = time; }
  27. CSPTimeSpan::CSPTimeSpan(LONG lDays, int nHours, int nMins, int nSecs)
  28. { m_timeSpan = nSecs + 60* (nMins + 60* (nHours + 24* lDays)); }
  29. CSPTimeSpan::CSPTimeSpan(const CSPTimeSpan& timeSpanSrc)
  30. { m_timeSpan = timeSpanSrc.m_timeSpan; }
  31. /////////////////////////////////////////////////////////////////////////////
  32. // CSPTime - absolute time
  33. CSPTime::CSPTime()
  34. { }
  35. CSPTime::CSPTime(time_t time)
  36. { m_time = time; }
  37. CSPTime::CSPTime(const CSPTime& timeSrc)
  38. { m_time = timeSrc.m_time; }
  39. CSPTime::CSPTime(int nYear, int nMonth, int nDay, int nHour, int nMin, int nSec,
  40. int nDST)
  41. {
  42. struct tm atm;
  43. atm.tm_sec = nSec;
  44. atm.tm_min = nMin;
  45. atm.tm_hour = nHour;
  46. ASSERT(nDay >= 1 && nDay <= 31);
  47. atm.tm_mday = nDay;
  48. ASSERT(nMonth >= 1 && nMonth <= 12);
  49. atm.tm_mon = nMonth - 1;        // tm_mon is 0 based
  50. ASSERT(nYear >= 1900);
  51. atm.tm_year = nYear - 1900;     // tm_year is 1900 based
  52. atm.tm_isdst = nDST;
  53. m_time = mktime(&atm);
  54. ASSERT(m_time != -1);       // indicates an illegal input time
  55. if( m_time == -1 )
  56. m_time = ::time(NULL);
  57. }
  58. CSPTime::CSPTime(WORD wDosDate, WORD wDosTime, int nDST)
  59. {
  60. struct tm atm;
  61. atm.tm_sec = (wDosTime & ~0xFFE0) << 1;
  62. atm.tm_min = (wDosTime & ~0xF800) >> 5;
  63. atm.tm_hour = wDosTime >> 11;
  64. atm.tm_mday = wDosDate & ~0xFFE0;
  65. atm.tm_mon = ((wDosDate & ~0xFE00) >> 5) - 1;
  66. atm.tm_year = (wDosDate >> 9) + 80;
  67. atm.tm_isdst = nDST;
  68. m_time = mktime(&atm);
  69. ASSERT(m_time != -1);       // indicates an illegal input time
  70. if( m_time == -1 )
  71. m_time = ::time(NULL);
  72. }
  73. CSPTime::CSPTime(const SYSTEMTIME& sysTime, int nDST)
  74. {
  75. if (sysTime.wYear < 1900)
  76. {
  77. time_t time0 = 0L;
  78. CSPTime timeT(time0);
  79. *this = timeT;
  80. }
  81. else
  82. {
  83. CSPTime timeT(
  84. (int)sysTime.wYear, (int)sysTime.wMonth, (int)sysTime.wDay,
  85. (int)sysTime.wHour, (int)sysTime.wMinute, (int)sysTime.wSecond,
  86. nDST);
  87. *this = timeT;
  88. }
  89. }
  90. CSPTime::CSPTime(const FILETIME& fileTime, int nDST)
  91. {
  92. // first convert file time (UTC time) to local time
  93. FILETIME localTime;
  94. if (!FileTimeToLocalFileTime(&fileTime, &localTime))
  95. {
  96. m_time = 0;
  97. return;
  98. }
  99. // then convert that time to system time
  100. SYSTEMTIME sysTime;
  101. if (!FileTimeToSystemTime(&localTime, &sysTime))
  102. {
  103. m_time = 0;
  104. return;
  105. }
  106. // then convert the system time to a time_t (C-runtime local time)
  107. CSPTime timeT(sysTime, nDST);
  108. *this = timeT;
  109. }
  110. CSPTime PASCAL CSPTime::GetCurrentTime()
  111. // return the current system time
  112. {
  113. return CSPTime(::time(NULL));
  114. }
  115. DWORD CSPTime::GetTradeSecondsOfOneDay( DWORD dwMarket )
  116. {
  117. return 14400;
  118. }
  119. DWORD CSPTime::GetStockTimeNext( DWORD dwDate, int ktype, DWORD dwYear )
  120. {
  121. CSPTime sptime;
  122. if( ktype == ktypeDay )
  123. {
  124. if( !sptime.FromStockTimeDay( dwDate ) )
  125. return -1;
  126. if( 6 == sptime.GetDayOfWeek() ) // Friday
  127. sptime += CSPTimeSpan(3,0,0,0);
  128. else
  129. sptime += CSPTimeSpan(1,0,0,0);
  130. return sptime.ToStockTimeDay();
  131. }
  132. else if( ktype == ktypeWeek )
  133. {
  134. if( !sptime.FromStockTimeDay( dwDate ) )
  135. return -1;
  136. sptime += CSPTimeSpan(7,0,0,0);
  137. return sptime.ToStockTimeDay();
  138. }
  139. else if( ktype == ktypeMonth )
  140. {
  141. if( !sptime.FromStockTimeDay( dwDate ) )
  142. return -1;
  143. int nYearNew = sptime.GetYear();
  144. int nMonthNew = sptime.GetMonth();
  145. nYearNew = ( nMonthNew >= 12 ? nYearNew+1 : nYearNew );
  146. nMonthNew = ( nMonthNew >= 12 ? 1 : nMonthNew+1 );
  147. CSPTime sptime2( nYearNew, nMonthNew, sptime.GetDay(), sptime.GetHour(),sptime.GetMinute(), sptime.GetSecond() );
  148. if( 6 == sptime2.GetDayOfWeek() ) // Friday
  149. sptime2 += CSPTimeSpan(3,0,0,0);
  150. else
  151. sptime2 += CSPTimeSpan(1,0,0,0);
  152. return sptime2.ToStockTimeDay();
  153. }
  154. else if( ktype == ktypeMin5 )
  155. {
  156. if( !sptime.FromStockTimeMin( dwDate ) )
  157. return -1;
  158. if( sptime.GetHour() == 11 && sptime.GetMinute() >= 25 )
  159. sptime += CSPTimeSpan(0,1,35,0);
  160. else if( sptime.GetHour() == 14 && sptime.GetMinute() >= 55 )
  161. sptime += CSPTimeSpan(0,18,35,0);
  162. else
  163. sptime += CSPTimeSpan(0,0,5,0);
  164. return sptime.ToStockTimeMin();
  165. }
  166. else if( ktype == ktypeMin15 )
  167. {
  168. if( !sptime.FromStockTimeMin( dwDate ) )
  169. return -1;
  170. if( sptime.GetHour() == 11 && sptime.GetMinute() >= 15 )
  171. sptime += CSPTimeSpan(0,1,45,0);
  172. else if( sptime.GetHour() == 14 && sptime.GetMinute() >= 45 )
  173. sptime += CSPTimeSpan(0,18,45,0);
  174. else
  175. sptime += CSPTimeSpan(0,0,15,0);
  176. return sptime.ToStockTimeMin();
  177. }
  178. else if( ktype == ktypeMin30 )
  179. {
  180. if( !sptime.FromStockTimeMin( dwDate ) )
  181. return -1;
  182. if( sptime.GetHour() == 11 && sptime.GetMinute() >= 0 )
  183. sptime += CSPTimeSpan(0,2,0,0);
  184. else if( sptime.GetHour() == 14 && sptime.GetMinute() >= 30 )
  185. sptime += CSPTimeSpan(0,19,0,0);
  186. else
  187. sptime += CSPTimeSpan(0,0,30,0);
  188. return sptime.ToStockTimeMin();
  189. }
  190. else if( ktype == ktypeMin60 )
  191. {
  192. if( !sptime.FromStockTimeMin( dwDate ) )
  193. return -1;
  194. if( (sptime.GetHour() == 10 && sptime.GetMinute() >= 30) || sptime.GetHour() == 11 )
  195. sptime += CSPTimeSpan(0,2,30,0);
  196. else if( sptime.GetHour() == 14 && sptime.GetMinute() >= 0 )
  197. sptime += CSPTimeSpan(0,19,30,0);
  198. else
  199. sptime += CSPTimeSpan(0,1,0,0);
  200. return sptime.ToStockTimeMin();
  201. }
  202. else
  203. {
  204. ASSERT( FALSE );
  205. return -1;
  206. }
  207. }
  208. time_t CSPTime::GetTimeTradeLatest( time_t tmTradeFirstToday )
  209. {
  210. CSPTime tNow = CSPTime::GetCurrentTime();
  211. CSPTime tTradeFirstToday( tmTradeFirstToday );
  212. time_t tmTradeLatest = -1;
  213. int nYear = tTradeFirstToday.GetYear();
  214. int nMonth = tTradeFirstToday.GetMonth();
  215. int nDay = tTradeFirstToday.GetDay();
  216. if( tNow > tTradeFirstToday
  217. && tNow.GetYear() == nYear && tNow.GetMonth() == nMonth && tNow.GetDay() == nDay )
  218. {
  219. tmTradeLatest = tNow.GetTime();
  220. if( (tNow.GetHour() == 11 && tNow.GetMinute() >= 30)
  221. || tNow.GetHour() == 12 )
  222. tmTradeLatest = CSPTime(nYear,nMonth,nDay,11,30,0).GetTime();
  223. else if( tNow.GetHour() >= 15 )
  224. tmTradeLatest = CSPTime(nYear,nMonth,nDay,15,0,0).GetTime();
  225. }
  226. else if( tNow > tTradeFirstToday )
  227. tmTradeLatest = CSPTime(nYear,nMonth,nDay,15,0,0).GetTime();
  228. return tmTradeLatest;
  229. }
  230. double CSPTime::GetTimeTradeRatioOfOneDay( CSPTime tTradeLatestDay, CSPTime tNow )
  231. {
  232. DWORD dwSecOrder = tNow.ToStockTimeSecOrder();
  233. if( 0 == dwSecOrder )
  234. return 1;
  235. if( tTradeLatestDay.GetYear() == tNow.GetYear()
  236. && tTradeLatestDay.GetMonth() == tNow.GetMonth()
  237. && tTradeLatestDay.GetDay() == tNow.GetDay() )
  238. return ((double)dwSecOrder) / GetTradeSecondsOfOneDay();
  239. return 1;
  240. }
  241. time_t CSPTime::GetTradeOffsetToTime( int offset, time_t tmDay)
  242. {
  243. time_t ret = tmDay - ((tmDay+8*3600) % 86400);
  244. if( offset >= 0 && offset <= 120 )
  245. return ret + 9*3600 + (30+offset)*60;
  246. if( offset > 120 && offset <= 240 )
  247. return ret + 13*3600 + (offset-120)*60;
  248. return tmDay;
  249. }
  250. time_t CSPTime::GetLatestTradeTime( time_t tmNow )
  251. {
  252. CSPTime tNow(tmNow);
  253. CSPTime tTradeLatest = tNow;
  254. if( 7 == tNow.GetDayOfWeek() )
  255. {
  256. tNow -= CSPTimeSpan(1,0,0,0);
  257. tTradeLatest = CSPTime(tNow.GetYear(),tNow.GetMonth(),tNow.GetDay(),15,0,0);
  258. }
  259. else if( 1 == tNow.GetDayOfWeek() )
  260. {
  261. tNow -= CSPTimeSpan(2,0,0,0);
  262. tTradeLatest = CSPTime(tNow.GetYear(),tNow.GetMonth(),tNow.GetDay(),15,0,0);
  263. }
  264. else if( (tNow.GetHour() == 9 && tNow.GetMinute() < 25 )
  265. || tNow.GetHour() < 9 )
  266. {
  267. tNow -= CSPTimeSpan(1,0,0,0);
  268. tTradeLatest = CSPTime(tNow.GetYear(),tNow.GetMonth(),tNow.GetDay(),15,0,0);
  269. }
  270. else if( (tNow.GetHour() == 11 && tNow.GetMinute() >= 30)
  271. || tNow.GetHour() == 12 )
  272. {
  273. tTradeLatest = CSPTime(tNow.GetYear(),tNow.GetMonth(),tNow.GetDay(),11,30,0).GetTime();
  274. }
  275. else if( tNow.GetHour() >= 15 )
  276. {
  277. tTradeLatest = CSPTime(tNow.GetYear(),tNow.GetMonth(),tNow.GetDay(),15,0,0).GetTime();
  278. }
  279. return tTradeLatest.GetTime();
  280. }
  281. BOOL CSPTime::InTradeTime( time_t tm, int nInflateSeconds )
  282. {
  283. CSPTime t(tm);
  284. // 周六周日
  285. if( 7 == t.GetDayOfWeek() )
  286. return FALSE;
  287. else if( 1 == t.GetDayOfWeek() )
  288. return FALSE;
  289. time_t day = (t.GetTime()+8*3600) % 86400;
  290. if( day >= 9*3600+25*60-nInflateSeconds && day <= 11*3600+30*60+nInflateSeconds )
  291. return TRUE;
  292. else if( day >= 13*3600-nInflateSeconds && day <= 15*3600+nInflateSeconds )
  293. return TRUE;
  294. return FALSE;
  295. }
  296. BOOL CSPTime::FromStockTimeDay( DWORD date )
  297. {
  298. int nHour = 0;
  299. int nMinute = 0;
  300. int nYear = date / 10000;
  301. int nMonth = (date - nYear*10000)/100;
  302. int nDay = (date - nYear*10000 - nMonth*100);
  303. struct tm atm;
  304. atm.tm_sec = 0;
  305. atm.tm_min = nMinute;
  306. atm.tm_hour = nHour;
  307. if( nDay < 1 || nDay > 31) return FALSE;
  308. atm.tm_mday = nDay;
  309. if( nMonth < 1 && nMonth > 12) return FALSE;
  310. atm.tm_mon = nMonth - 1;        // tm_mon is 0 based
  311. if( nYear < 1900 ) return FALSE;
  312. atm.tm_year = nYear - 1900;     // tm_year is 1900 based
  313. atm.tm_isdst = -1;
  314. time_t tmt = mktime(&atm);
  315. if( tmt == -1 ) // indicates an illegal input time
  316. return FALSE;
  317. *this = CSPTime( nYear, nMonth, nDay, nHour, nMinute, 0 );
  318. return TRUE;
  319. }
  320. BOOL CSPTime::FromStockTimeMin( DWORD date, DWORD year )
  321. {
  322. int nYear = year;
  323. if( -1 == nYear )
  324. nYear = 1990 + date/100000000;
  325. date = date % 100000000;
  326. int nMonth = date/1000000;
  327. int nDay = (date - nMonth*1000000)/10000;
  328. int nHour = (date - nMonth*1000000 - nDay*10000)/100;
  329. int nMinute = (date - nMonth*1000000 - nDay*10000 - nHour*100);
  330. struct tm atm;
  331. atm.tm_sec = 0;
  332. atm.tm_min = nMinute;
  333. atm.tm_hour = nHour;
  334. if( nDay < 1 || nDay > 31) return FALSE;
  335. atm.tm_mday = nDay;
  336. if( nMonth < 1 || nMonth > 12) return FALSE;
  337. atm.tm_mon = nMonth - 1;        // tm_mon is 0 based
  338. if( nYear < 1900 ) return FALSE;
  339. atm.tm_year = nYear - 1900;     // tm_year is 1900 based
  340. atm.tm_isdst = -1;
  341. time_t tmt = mktime(&atm);
  342. if( tmt == -1 ) // indicates an illegal input time
  343. return FALSE;
  344. *this = CSPTime( nYear, nMonth, nDay, nHour, nMinute, 0 );
  345. return TRUE;
  346. }
  347. BOOL CSPTime::FromStockTime( DWORD dwDate, BOOL bDayOrMin, DWORD dwYear )
  348. {
  349. if( bDayOrMin )
  350. return FromStockTimeDay( dwDate );
  351. else
  352. return FromStockTimeMin( dwDate, dwYear );
  353. }
  354. DWORD CSPTime::ToStockTimeDay( )
  355. {
  356. if( -1 == GetTime() || GetTime() < 0 )
  357. return -1;
  358. if( 0 == GetTime() )
  359. return 0;
  360. return ( GetYear() * 10000 + GetMonth() * 100 + GetDay() );
  361. }
  362. DWORD CSPTime::ToStockTimeMin( )
  363. {
  364. if( -1 == GetTime() || GetTime() < 0 )
  365. return -1;
  366. if( 0 == GetTime() )
  367. return 0;
  368. return ( (GetYear() - 1990) * 100000000 + GetMonth() * 1000000 + GetDay() * 10000
  369. + GetHour() * 100 + GetMinute() );
  370. }
  371. DWORD CSPTime::ToStockTimeSecOrder( DWORD dwStockExchange )
  372. {
  373. if( -1 == GetTime() || GetTime() < 0 || 0 == GetTime() )
  374. return 0;
  375. if( GetHour() < 9 || (GetHour() == 9 && GetMinute() < 30) )
  376. return 0;
  377. CSPTime tmStart = CSPTime(GetYear(),GetMonth(),GetDay(),9,30,0);
  378. CSPTime tmEnd = CSPTime(GetYear(),GetMonth(),GetDay(),15,0,0);
  379. if( *this < tmStart )
  380. return 0;
  381. if( *this > tmEnd )
  382. return 14400;
  383. CSPTimeSpan tmSpan = *this - tmStart;
  384. int nSec = tmSpan.GetTotalSeconds();
  385. if( nSec >= 0 && nSec <= 7200 )
  386. return nSec;
  387. if( nSec > 7200 && nSec < 12600 )
  388. return 7200;
  389. if( nSec >= 12600 && nSec <= 19800 )
  390. return nSec-5400;
  391. ASSERT( FALSE );
  392. return 0;
  393. }
  394. DWORD CSPTime::ToStockTime( BOOL bDayOrMin )
  395. {
  396. if( bDayOrMin )
  397. return ToStockTimeDay( );
  398. else
  399. return ToStockTimeMin( );
  400. }
  401. struct tm* CSPTime::GetGmtTm(struct tm* ptm) const
  402. {
  403. if (ptm != NULL)
  404. {
  405. *ptm = *gmtime(&m_time);
  406. return ptm;
  407. }
  408. else
  409. return gmtime(&m_time);
  410. }
  411. struct tm* CSPTime::GetLocalTm(struct tm* ptm) const
  412. {
  413. time_t time_temp = m_time;
  414. if( m_time > 0 )
  415. time_temp = m_time+28800; // 北京时间
  416. if (ptm != NULL)
  417. {
  418. struct tm* ptmTemp = gmtime(&time_temp);
  419. if (ptmTemp == NULL)
  420. return NULL;    // indicates the m_time was not initialized!
  421. *ptm = *ptmTemp;
  422. return ptm;
  423. }
  424. else
  425. return gmtime(&time_temp);
  426. /*
  427. if (ptm != NULL)
  428. {
  429. struct tm* ptmTemp = localtime(&m_time);
  430. if (ptmTemp == NULL)
  431. return NULL;    // indicates the m_time was not initialized!
  432. *ptm = *ptmTemp;
  433. return ptm;
  434. }
  435. else
  436. return localtime(&m_time);
  437. */
  438. }
  439. BOOL CSPTime::GetAsSystemTime(SYSTEMTIME& timeDest) const
  440. {
  441. struct tm* ptm = GetLocalTm(NULL);
  442. if (ptm == NULL)
  443. return FALSE;
  444. timeDest.wYear = (WORD) (1900 + ptm->tm_year);
  445. timeDest.wMonth = (WORD) (1 + ptm->tm_mon);
  446. timeDest.wDayOfWeek = (WORD) ptm->tm_wday;
  447. timeDest.wDay = (WORD) ptm->tm_mday;
  448. timeDest.wHour = (WORD) ptm->tm_hour;
  449. timeDest.wMinute = (WORD) ptm->tm_min;
  450. timeDest.wSecond = (WORD) ptm->tm_sec;
  451. timeDest.wMilliseconds = 0;
  452. return TRUE;
  453. }
  454. /////////////////////////////////////////////////////////////////////////////
  455. // String formatting
  456. #define maxTimeBufferSize       128
  457. // Verifies will fail if the needed buffer size is too large
  458. #ifdef _UNICODE
  459. #endif
  460. CString CSPTimeSpan::Format(LPCTSTR pFormat) const
  461. // formatting timespans is a little trickier than formatting CSPTimes
  462. //  * we are only interested in relative time formats, ie. it is illegal
  463. //      to format anything dealing with absolute time (i.e. years, months,
  464. //         day of week, day of year, timezones, ...)
  465. //  * the only valid formats:
  466. //      %D - # of days -- NEW !!!
  467. //      %H - hour in 24 hour format
  468. //      %M - minute (0-59)
  469. //      %S - seconds (0-59)
  470. //      %% - percent sign
  471. {
  472. TCHAR szBuffer[maxTimeBufferSize];
  473. TCHAR ch;
  474. LPTSTR pch = szBuffer;
  475. while ((ch = *pFormat++) != '')
  476. {
  477. ASSERT(pch < &szBuffer[maxTimeBufferSize]);
  478. if (ch == '%')
  479. {
  480. switch (ch = *pFormat++)
  481. {
  482. default:
  483. ASSERT(FALSE);      // probably a bad format character
  484. case '%':
  485. *pch++ = ch;
  486. break;
  487. case 'D':
  488. pch += wsprintf(pch, _T("%ld"), GetDays());
  489. break;
  490. case 'H':
  491. pch += wsprintf(pch, _T("%02d"), GetHours());
  492. break;
  493. case 'M':
  494. pch += wsprintf(pch, _T("%02d"), GetMinutes());
  495. break;
  496. case 'S':
  497. pch += wsprintf(pch, _T("%02d"), GetSeconds());
  498. break;
  499. }
  500. }
  501. else
  502. {
  503. *pch++ = ch;
  504. if (_istlead(ch))
  505. {
  506. ASSERT(pch < &szBuffer[maxTimeBufferSize]);
  507. *pch++ = *pFormat++;
  508. }
  509. }
  510. }
  511. *pch = '';
  512. return szBuffer;
  513. }
  514. CString CSPTime::Format(LPCTSTR pFormat) const
  515. {
  516. TCHAR szBuffer[maxTimeBufferSize];
  517. struct tm* ptmTemp = localtime(&m_time);
  518. if (ptmTemp == NULL ||
  519. !_tcsftime(szBuffer, sizeof(szBuffer), pFormat, ptmTemp))
  520. szBuffer[0] = '';
  521. return szBuffer;
  522. }
  523. CString CSPTime::FormatGmt(LPCTSTR pFormat) const
  524. {
  525. TCHAR szBuffer[maxTimeBufferSize];
  526. struct tm* ptmTemp = gmtime(&m_time);
  527. if (ptmTemp == NULL ||
  528. !_tcsftime(szBuffer, sizeof(szBuffer), pFormat, ptmTemp))
  529. szBuffer[0] = '';
  530. return szBuffer;
  531. }
  532. #ifdef _UNICODE
  533. // These functions are provided for compatibility with MFC 3.x
  534. CString CSPTime::Format(LPCSTR pFormat) const
  535. {
  536. CString strFormat(pFormat);
  537. return Format((LPCTSTR)strFormat);
  538. }
  539. CString CSPTime::FormatGmt(LPCSTR pFormat) const
  540. {
  541. CString strFormat(pFormat);
  542. return FormatGmt((LPCTSTR)strFormat);
  543. }
  544. CString CSPTimeSpan::Format(LPCSTR pFormat) const
  545. {
  546. CString strFormat = pFormat;
  547. return Format((LPCTSTR)strFormat);
  548. }
  549. #endif // _UNICODE
  550. /////////////////////////////////////////////////////////////////////////////