1///////////////////////////////////////////////////////////////////////////// 2// Name: src/msw/wince/time.cpp 3// Purpose: Implements missing time functionality for WinCE 4// Author: Marco Cavallini (MCK) - wx@koansoftware.com 5// Modified by: Vadim Zeitlin for VC8 support 6// Created: 31-08-2003 7// RCS-ID: $Id: time.cpp 43076 2006-11-04 23:27:15Z VZ $ 8// Copyright: (c) Marco Cavallini 9// Licence: wxWindows licence 10///////////////////////////////////////////////////////////////////////////// 11 12// =========================================================================== 13// declarations 14// =========================================================================== 15 16// --------------------------------------------------------------------------- 17// headers 18// --------------------------------------------------------------------------- 19 20// For compilers that support precompilation, includes "wx.h". 21#include "wx/wxprec.h" 22 23#ifdef __BORLANDC__ 24 #pragma hdrstop 25#endif 26 27#ifndef WX_PRECOMP 28 #include "wx/msw/wrapwin.h" 29#endif 30 31#include "wx/msw/wince/time.h" 32 33#if defined(__VISUALC__) && (__VISUALC__ >= 1400) 34 35// VC8 does provide the time functions but not the standard ones 36#include <altcecrt.h> 37 38time_t __cdecl time(time_t *t) 39{ 40 __time64_t t64; 41 if ( !_time64(&t64) ) 42 return (time_t)-1; 43 44 if ( t ) 45 *t = (time_t)t64; 46 47 return (time_t)t64; 48} 49 50time_t __cdecl mktime(struct tm *t) 51{ 52 return (time_t)_mktime64(t); 53} 54 55#else // !VC8 56 57///////////////////////////////////////////////////////////////////////////////////////////// 58// // 59// strftime() - taken from OpenBSD // 60// // 61///////////////////////////////////////////////////////////////////////////////////////////// 62 63#define IN_NONE 0 64#define IN_SOME 1 65#define IN_THIS 2 66#define IN_ALL 3 67#define CHAR_BIT 8 68 69#define TYPE_BIT(type) (sizeof (type) * CHAR_BIT) 70#define TYPE_SIGNED(type) (((type) -1) < 0) 71 72#define INT_STRLEN_MAXIMUM(type) \ 73 ((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + 1 + TYPE_SIGNED(type)) 74 75#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) 76 77#define MONSPERYEAR 12 78#define DAYSPERWEEK 7 79#define TM_YEAR_BASE 1900 80#define HOURSPERDAY 24 81#define DAYSPERNYEAR 365 82#define DAYSPERLYEAR 366 83 84static char wildabbr[] = "WILDABBR"; 85 86char * tzname[2] = { 87 wildabbr, 88 wildabbr 89}; 90 91 92#define Locale (&C_time_locale) 93 94struct lc_time_T { 95 const char * mon[MONSPERYEAR]; 96 const char * month[MONSPERYEAR]; 97 const char * wday[DAYSPERWEEK]; 98 const char * weekday[DAYSPERWEEK]; 99 const char * X_fmt; 100 const char * x_fmt; 101 const char * c_fmt; 102 const char * am; 103 const char * pm; 104 const char * date_fmt; 105}; 106 107static const struct lc_time_T C_time_locale = { 108 { 109 "Jan", "Feb", "Mar", "Apr", "May", "Jun", 110 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 111 }, { 112 "January", "February", "March", "April", "May", "June", 113 "July", "August", "September", "October", "November", "December" 114 }, { 115 "Sun", "Mon", "Tue", "Wed", 116 "Thu", "Fri", "Sat" 117 }, { 118 "Sunday", "Monday", "Tuesday", "Wednesday", 119 "Thursday", "Friday", "Saturday" 120 }, 121 122 /* X_fmt */ 123 "%H:%M:%S", 124 125 /* 126 ** x_fmt 127 ** C99 requires this format. 128 ** Using just numbers (as here) makes Quakers happier; 129 ** it's also compatible with SVR4. 130 */ 131 "%m/%d/%y", 132 133 /* 134 ** c_fmt 135 ** C99 requires this format. 136 ** Previously this code used "%D %X", but we now conform to C99. 137 ** Note that 138 ** "%a %b %d %H:%M:%S %Y" 139 ** is used by Solaris 2.3. 140 */ 141 "%a %b %e %T %Y", 142 143 /* am */ 144 "AM", 145 146 /* pm */ 147 "PM", 148 149 /* date_fmt */ 150 "%a %b %e %H:%M:%S %Z %Y" 151}; 152 153 154static char * 155_add(const char * str, char * pt, const char * const ptlim) 156{ 157 while (pt < ptlim && (*pt = *str++) != '\0') 158 ++pt; 159 return pt; 160} 161 162 163static char * 164_conv(const int n, const char * const format, char * const pt, const char * const ptlim) 165{ 166 char buf[INT_STRLEN_MAXIMUM(int) + 1]; 167 168 (void) _snprintf(buf, sizeof buf, format, n); 169 return _add(buf, pt, ptlim); 170} 171 172 173static char * 174_fmt(const char * format, const struct tm * const t, char * pt, const char * const ptlim, int * warnp) 175{ 176 for ( ; *format; ++format) { 177 if (*format == '%') { 178label: 179 switch (*++format) { 180 case '\0': 181 --format; 182 break; 183 case 'A': 184 pt = _add((t->tm_wday < 0 || 185 t->tm_wday >= DAYSPERWEEK) ? 186 "?" : Locale->weekday[t->tm_wday], 187 pt, ptlim); 188 continue; 189 case 'a': 190 pt = _add((t->tm_wday < 0 || 191 t->tm_wday >= DAYSPERWEEK) ? 192 "?" : Locale->wday[t->tm_wday], 193 pt, ptlim); 194 continue; 195 case 'B': 196 pt = _add((t->tm_mon < 0 || 197 t->tm_mon >= MONSPERYEAR) ? 198 "?" : Locale->month[t->tm_mon], 199 pt, ptlim); 200 continue; 201 case 'b': 202 case 'h': 203 pt = _add((t->tm_mon < 0 || 204 t->tm_mon >= MONSPERYEAR) ? 205 "?" : Locale->mon[t->tm_mon], 206 pt, ptlim); 207 continue; 208 case 'C': 209 /* 210 ** %C used to do a... 211 ** _fmt("%a %b %e %X %Y", t); 212 ** ...whereas now POSIX 1003.2 calls for 213 ** something completely different. 214 ** (ado, 1993-05-24) 215 */ 216 pt = _conv((t->tm_year + TM_YEAR_BASE) / 100, 217 "%02d", pt, ptlim); 218 continue; 219 case 'c': 220 { 221 int warn2 = IN_SOME; 222 223 pt = _fmt(Locale->c_fmt, t, pt, ptlim, warnp); 224 if (warn2 == IN_ALL) 225 warn2 = IN_THIS; 226 if (warn2 > *warnp) 227 *warnp = warn2; 228 } 229 continue; 230 case 'D': 231 pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp); 232 continue; 233 case 'd': 234 pt = _conv(t->tm_mday, "%02d", pt, ptlim); 235 continue; 236 case 'E': 237 case 'O': 238 /* 239 ** C99 locale modifiers. 240 ** The sequences 241 ** %Ec %EC %Ex %EX %Ey %EY 242 ** %Od %oe %OH %OI %Om %OM 243 ** %OS %Ou %OU %OV %Ow %OW %Oy 244 ** are supposed to provide alternate 245 ** representations. 246 */ 247 goto label; 248 case 'e': 249 pt = _conv(t->tm_mday, "%2d", pt, ptlim); 250 continue; 251 case 'F': 252 pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp); 253 continue; 254 case 'H': 255 pt = _conv(t->tm_hour, "%02d", pt, ptlim); 256 continue; 257 case 'I': 258 pt = _conv((t->tm_hour % 12) ? 259 (t->tm_hour % 12) : 12, 260 "%02d", pt, ptlim); 261 continue; 262 case 'j': 263 pt = _conv(t->tm_yday + 1, "%03d", pt, ptlim); 264 continue; 265 case 'k': 266 /* 267 ** This used to be... 268 ** _conv(t->tm_hour % 12 ? 269 ** t->tm_hour % 12 : 12, 2, ' '); 270 ** ...and has been changed to the below to 271 ** match SunOS 4.1.1 and Arnold Robbins' 272 ** strftime version 3.0. That is, "%k" and 273 ** "%l" have been swapped. 274 ** (ado, 1993-05-24) 275 */ 276 pt = _conv(t->tm_hour, "%2d", pt, ptlim); 277 continue; 278#ifdef KITCHEN_SINK 279 case 'K': 280 /* 281 ** After all this time, still unclaimed! 282 */ 283 pt = _add("kitchen sink", pt, ptlim); 284 continue; 285#endif /* defined KITCHEN_SINK */ 286 case 'l': 287 /* 288 ** This used to be... 289 ** _conv(t->tm_hour, 2, ' '); 290 ** ...and has been changed to the below to 291 ** match SunOS 4.1.1 and Arnold Robbin's 292 ** strftime version 3.0. That is, "%k" and 293 ** "%l" have been swapped. 294 ** (ado, 1993-05-24) 295 */ 296 pt = _conv((t->tm_hour % 12) ? 297 (t->tm_hour % 12) : 12, 298 "%2d", pt, ptlim); 299 continue; 300 case 'M': 301 pt = _conv(t->tm_min, "%02d", pt, ptlim); 302 continue; 303 case 'm': 304 pt = _conv(t->tm_mon + 1, "%02d", pt, ptlim); 305 continue; 306 case 'n': 307 pt = _add("\n", pt, ptlim); 308 continue; 309 case 'p': 310 pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ? 311 Locale->pm : 312 Locale->am, 313 pt, ptlim); 314 continue; 315 case 'R': 316 pt = _fmt("%H:%M", t, pt, ptlim, warnp); 317 continue; 318 case 'r': 319 pt = _fmt("%I:%M:%S %p", t, pt, ptlim, warnp); 320 continue; 321 case 'S': 322 pt = _conv(t->tm_sec, "%02d", pt, ptlim); 323 continue; 324 case 's': 325 { 326 struct tm tm; 327 char buf[INT_STRLEN_MAXIMUM( 328 time_t) + 1]; 329 time_t mkt; 330 331 tm = *t; 332 mkt = mktime(&tm); 333 if (TYPE_SIGNED(time_t)) 334 (void) _snprintf(buf, sizeof buf, 335 "%ld", (long) mkt); 336 else (void) _snprintf(buf, sizeof buf, 337 "%lu", (unsigned long) mkt); 338 pt = _add(buf, pt, ptlim); 339 } 340 continue; 341 case 'T': 342 pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp); 343 continue; 344 case 't': 345 pt = _add("\t", pt, ptlim); 346 continue; 347 case 'U': 348 pt = _conv((t->tm_yday + DAYSPERWEEK - 349 t->tm_wday) / DAYSPERWEEK, 350 "%02d", pt, ptlim); 351 continue; 352 case 'u': 353 /* 354 ** From Arnold Robbins' strftime version 3.0: 355 ** "ISO 8601: Weekday as a decimal number 356 ** [1 (Monday) - 7]" 357 ** (ado, 1993-05-24) 358 */ 359 pt = _conv((t->tm_wday == 0) ? 360 DAYSPERWEEK : t->tm_wday, 361 "%d", pt, ptlim); 362 continue; 363 case 'V': /* ISO 8601 week number */ 364 case 'G': /* ISO 8601 year (four digits) */ 365 case 'g': /* ISO 8601 year (two digits) */ 366 { 367 int year; 368 int yday; 369 int wday; 370 int w; 371 372 year = t->tm_year + TM_YEAR_BASE; 373 yday = t->tm_yday; 374 wday = t->tm_wday; 375 for ( ; ; ) { 376 int len; 377 int bot; 378 int top; 379 380 len = isleap(year) ? 381 DAYSPERLYEAR : 382 DAYSPERNYEAR; 383 /* 384 ** What yday (-3 ... 3) does 385 ** the ISO year begin on? 386 */ 387 bot = ((yday + 11 - wday) % 388 DAYSPERWEEK) - 3; 389 /* 390 ** What yday does the NEXT 391 ** ISO year begin on? 392 */ 393 top = bot - 394 (len % DAYSPERWEEK); 395 if (top < -3) 396 top += DAYSPERWEEK; 397 top += len; 398 if (yday >= top) { 399 ++year; 400 w = 1; 401 break; 402 } 403 if (yday >= bot) { 404 w = 1 + ((yday - bot) / 405 DAYSPERWEEK); 406 break; 407 } 408 --year; 409 yday += isleap(year) ? 410 DAYSPERLYEAR : 411 DAYSPERNYEAR; 412 } 413 if (*format == 'V') 414 pt = _conv(w, "%02d", 415 pt, ptlim); 416 else if (*format == 'g') { 417 *warnp = IN_ALL; 418 pt = _conv(year % 100, "%02d", 419 pt, ptlim); 420 } else pt = _conv(year, "%04d", 421 pt, ptlim); 422 } 423 continue; 424 case 'v': 425 pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp); 426 continue; 427 case 'W': 428 pt = _conv((t->tm_yday + DAYSPERWEEK - 429 (t->tm_wday ? 430 (t->tm_wday - 1) : 431 (DAYSPERWEEK - 1))) / DAYSPERWEEK, 432 "%02d", pt, ptlim); 433 continue; 434 case 'w': 435 pt = _conv(t->tm_wday, "%d", pt, ptlim); 436 continue; 437 case 'X': 438 pt = _fmt(Locale->X_fmt, t, pt, ptlim, warnp); 439 continue; 440 case 'x': 441 { 442 int warn2 = IN_SOME; 443 444 pt = _fmt(Locale->x_fmt, t, pt, ptlim, &warn2); 445 if (warn2 == IN_ALL) 446 warn2 = IN_THIS; 447 if (warn2 > *warnp) 448 *warnp = warn2; 449 } 450 continue; 451 case 'y': 452 *warnp = IN_ALL; 453 pt = _conv((t->tm_year + TM_YEAR_BASE) % 100, 454 "%02d", pt, ptlim); 455 continue; 456 case 'Y': 457 pt = _conv(t->tm_year + TM_YEAR_BASE, "%04d", 458 pt, ptlim); 459 continue; 460 case 'Z': 461 if (t->tm_isdst >= 0) 462 pt = _add(tzname[t->tm_isdst != 0], 463 pt, ptlim); 464 /* 465 ** C99 says that %Z must be replaced by the 466 ** empty string if the time zone is not 467 ** determinable. 468 */ 469 continue; 470 case 'z': 471 { 472 int diff = -timezone; 473 char const * sign; 474 475 if (diff < 0) { 476 sign = "-"; 477 diff = -diff; 478 } else sign = "+"; 479 pt = _add(sign, pt, ptlim); 480 diff /= 60; 481 pt = _conv((diff/60)*100 + diff%60, 482 "%04d", pt, ptlim); 483 } 484 continue; 485 case '+': 486 pt = _fmt(Locale->date_fmt, t, pt, ptlim, 487 warnp); 488 continue; 489 case '%': 490 default: 491 break; 492 } 493 } 494 if (pt == ptlim) 495 break; 496 *pt++ = *format; 497 } 498 return pt; 499} 500 501size_t 502strftime(char * const s, const size_t maxsize, const char *format, const struct tm * const t) 503{ 504 char * p; 505 int warn; 506 507 //tzset(); 508 509 warn = IN_NONE; 510 p = _fmt(((format == NULL) ? "%c" : format), t, s, s + maxsize, &warn); 511 512 if (p == s + maxsize) { 513 if (maxsize > 0) 514 s[maxsize - 1] = '\0'; 515 return 0; 516 } 517 *p = '\0'; 518 return p - s; 519} 520 521extern "C" 522{ 523 524/* Not needed in VS Studio 2005 */ 525 526size_t wcsftime(wchar_t *s, 527 const size_t maxsize, 528 const wchar_t *format, 529 const struct tm *t) 530{ 531 wxCharBuffer sBuf(maxsize/sizeof(wchar_t)); 532 533 wxString formatStr(format); 534 wxCharBuffer bufFormatStr(formatStr.mb_str()); 535 536 size_t sz = strftime(sBuf.data(), maxsize/sizeof(wchar_t), bufFormatStr, t); 537 538 wxMB2WC(s, sBuf, maxsize); 539 540 return sz; 541} 542 543} /* extern "C" */ 544 545#define is_leap(y) (y % 4 == 0 && (y % 100 != 0 || y % 400 == 0)) 546#define SECONDS_IN_ONE_MINUTE 60 547#define DAYS_IN_ONE_YEAR 365 548#define SECONDS_IN_ONE_MIN 60 549#define SECONDS_IN_ONE_HOUR 3600 550#define SECONDS_IN_ONE_DAY 86400 551#define DEFAULT_TIMEZONE 28800 552#define DO_GMTIME 0 553#define DO_LOCALTIME 1 554 555 556long timezone ; // global variable 557 558 559//////////////////////////////////////////////////////////////////////// 560// Common code for localtime and gmtime (static) 561//////////////////////////////////////////////////////////////////////// 562 563static struct tm * __cdecl common_localtime(const time_t *t, BOOL bLocal) 564{ 565 wxLongLong i64; 566 FILETIME ft; 567 wxString str ; 568 SYSTEMTIME SystemTime; 569 TIME_ZONE_INFORMATION pTz; 570 static struct tm st_res ; // data holder 571 static struct tm * res = &st_res ; // data pointer 572 int iLeap; 573 const unsigned short int __mon_yday[2][13] = 574 { 575 // Normal years 576 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, 577 // Leap years 578 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } 579 }; 580 581 if (!*t) 582 ::GetLocalTime(&SystemTime); 583 else 584 { 585 i64 = *t; 586 i64 = i64 * 10000000 + 116444736000000000; 587 588 ft.dwLowDateTime = i64.GetLo(); 589 ft.dwHighDateTime = i64.GetHi(); 590 591 ::FileTimeToSystemTime(&ft, &SystemTime); 592 } 593 594 ::GetTimeZoneInformation(&pTz); 595 596 /////////////////////////////////////////////// 597 // Set timezone 598 timezone = pTz.Bias * SECONDS_IN_ONE_MINUTE ; 599 /////////////////////////////////////////////// 600 601 iLeap = is_leap(SystemTime.wYear) ; 602 603 res->tm_hour = SystemTime.wHour; 604 res->tm_min = SystemTime.wMinute; 605 res->tm_sec = SystemTime.wSecond; 606 607 res->tm_mday = SystemTime.wDay; 608 res->tm_mon = SystemTime.wMonth - 1; // this the correct month but localtime returns month aligned to zero 609 res->tm_year = SystemTime.wYear; // this the correct year 610 res->tm_year = res->tm_year - 1900; // but localtime returns the value starting at the 1900 611 612 res->tm_wday = SystemTime.wDayOfWeek; 613 res->tm_yday = __mon_yday[iLeap][res->tm_mon] + SystemTime.wDay - 1; // localtime returns year-day aligned to zero 614 615 // if localtime behavior and daylight saving 616 if (bLocal && pTz.DaylightBias != 0) 617 res->tm_isdst = 1; 618 else 619 res->tm_isdst = 0; // without daylight saving or gmtime 620 621 return res; 622} 623 624extern "C" 625{ 626 627//////////////////////////////////////////////////////////////////////// 628// Receive the number of seconds elapsed since midnight(00:00:00) 629// and convert a time value and corrects for the local time zone 630//////////////////////////////////////////////////////////////////////// 631struct tm * __cdecl localtime(const time_t * t) 632{ 633 return common_localtime(t, DO_LOCALTIME) ; 634} 635 636//////////////////////////////////////////////////////////////////////// 637// Receives the number of seconds elapsed since midnight(00:00:00) 638// and converts a time value WITHOUT correcting for the local time zone 639//////////////////////////////////////////////////////////////////////// 640struct tm * __cdecl gmtime(const time_t *t) 641{ 642 return common_localtime(t, DO_GMTIME) ; 643} 644 645} 646 647//////////////////////////////////////////////////////////////////////// 648// Common code for conversion of struct tm into time_t (static) 649//////////////////////////////////////////////////////////////////////// 650static time_t __cdecl common_tm_to_time(int day, int month, int year, int hour, int minute, int second) 651{ 652#if 1 653 // Use mktime since it seems less broken 654 tm t; 655 t.tm_isdst = -1; 656 t.tm_hour = hour; 657 t.tm_mday = day; 658 t.tm_min = minute; 659 t.tm_mon = month-1; 660 t.tm_sec = second; 661 t.tm_wday = -1; 662 t.tm_yday = -1; 663 t.tm_year = year - 1900; 664 return mktime(& t); 665#else 666 time_t prog = 0 ; 667 static int mdays[12] = { 31,28,31,30,31,30,31,31,30,31,30,31 } ; 668 669 while (--month) 670 { 671 prog += mdays[month - 1] ; 672 if (month == 2 && is_leap(year)) 673 prog++ ; 674 } 675 676 // Calculate seconds in elapsed days 677 prog = day - 1 ; // align first day of the year to zero 678 prog += (DAYS_IN_ONE_YEAR * (year - 1970) + (year - 1901) / 4 - 19) ; 679 prog *= SECONDS_IN_ONE_DAY ; 680 681 // Add Calculated elapsed seconds in the current day 682 prog += (hour * SECONDS_IN_ONE_HOUR + minute * 683 SECONDS_IN_ONE_MIN + second) ; 684 685 return prog ; 686#endif 687} 688 689extern "C" 690{ 691 692//////////////////////////////////////////////////////////////////////// 693// Returns the number of seconds elapsed since 694// midnight(00:00:00) of 1 January 1970 695//////////////////////////////////////////////////////////////////////// 696time_t __cdecl time(time_t *t) 697{ 698 time_t prog = 0 ; 699 700 if (t != NULL) 701 { 702 SYSTEMTIME SystemTime; 703 704 ::GetLocalTime(&SystemTime) ; 705 prog = common_tm_to_time(SystemTime.wDay, SystemTime.wMonth, SystemTime.wYear, 706 SystemTime.wHour, SystemTime.wMinute, SystemTime.wSecond) ; 707 *t = prog ; 708 } 709 710 return prog ; 711} 712 713//////////////////////////////////////////////////////////////////////// 714// Converts the local time provided by struct tm 715// into a time_t calendar value 716// Implementation from OpenBSD 717//////////////////////////////////////////////////////////////////////// 718 719#if 1 720int month_to_day[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; 721 722time_t mktime(struct tm *t) 723{ 724 short month, year; 725 time_t result; 726 727 month = t->tm_mon; 728 year = t->tm_year + month / 12 + 1900; 729 month %= 12; 730 if (month < 0) 731 { 732 year -= 1; 733 month += 12; 734 } 735 result = (year - 1970) * 365 + (year - 1969) / 4 + month_to_day[month]; 736 result = (year - 1970) * 365 + month_to_day[month]; 737 if (month <= 1) 738 year -= 1; 739 result += (year - 1968) / 4; 740 result -= (year - 1900) / 100; 741 result += (year - 1600) / 400; 742 result += t->tm_mday; 743 result -= 1; 744 result *= 24; 745 result += t->tm_hour; 746 result *= 60; 747 result += t->tm_min; 748 result *= 60; 749 result += t->tm_sec; 750 return(result); 751} 752 753#else 754time_t __cdecl mktime(struct tm *t) 755{ 756 return (common_tm_to_time(t->tm_mday, t->tm_mon+1, t->tm_year+1900, t->tm_hour, t->tm_min, t->tm_sec)) ; 757} 758#endif 759 760} // extern "C" 761 762#endif // VC8/!VC8 763