1/* 2 * Copyright (c) 2013-2019, Huawei Technologies Co., Ltd. All rights reserved. 3 * Copyright (c) 2020, Huawei Device Co., Ltd. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without modification, 6 * are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, this list of 9 * conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, this list 12 * of conditions and the following disclaimer in the documentation and/or other materials 13 * provided with the distribution. 14 * 15 * 3. Neither the name of the copyright holder nor the names of its contributors may be used 16 * to endorse or promote products derived from this software without specific prior written 17 * permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 26 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 28 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 29 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32#include "tzdst.h" 33#include "tzdst_pri.h" 34#include "stdio.h" 35#include "stdlib.h" 36#include "unistd.h" 37#include "los_printf.h" 38#include "los_typedef.h" 39#include "securec.h" 40 41 42/* 2: leap year or normal year */ 43STATIC const INT32 g_monLengths[2][MONSPERYEAR] = { 44 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, 45 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } 46}; 47 48/* Time Zone functions */ 49#define IS_NUM(x) (((x) >= '0') && ((x) <= '9')) 50 51long int timezone; 52 53STATIC VOID BufferInsert(CHAR *buf, size_t bufLen, size_t positions, CHAR data) 54{ 55 if (bufLen <= positions) { 56 return; 57 } 58 if (memmove_s(&buf[positions + 1], bufLen - positions - 1, &buf[positions], bufLen - positions - 1) != EOK) { 59 PRINTK("%s falied \n", __FUNCTION__); 60 return; 61 } 62 63 buf[positions] = data; 64} 65 66#define OPERATE_OFF 3 67#define HOUR_HIGH_OFF 4 68#define HOUR_LOW_OFF 5 69#define MIN_HIGH_OFF 7 70#define MIN_LOW_OFF 8 71#define SEC_HIGH_OFF 10 72#define SEC_LOW_OFF 11 73 74/* 75 * tzn[+/-]hh[:mm[:ss]][dzn] 76 * tzn + 11 :30 : 7 dzn 77 * tzn - 9 : 7 :11 dzn 78 */ 79STATIC BOOL TimezoneFormat(CHAR *standardString, size_t bufLen) 80{ 81 if ((standardString[OPERATE_OFF] == '-') || (standardString[OPERATE_OFF] == '+')) { 82 if (!IS_NUM(standardString[OPERATE_OFF + 1])) { 83 return FALSE; 84 } 85 } else if (IS_NUM(standardString[OPERATE_OFF])) { 86 BufferInsert(standardString, bufLen, OPERATE_OFF, '+'); /* no operate is default to add */ 87 } else { 88 return FALSE; 89 } 90 91 if (!IS_NUM(standardString[HOUR_LOW_OFF])) { 92 BufferInsert(standardString, bufLen, HOUR_HIGH_OFF, '0'); /* hour only one bit, padding 0 to high bit */ 93 } 94 95 if (standardString[HOUR_LOW_OFF + 1] == ':') { 96 if (!IS_NUM(standardString[MIN_HIGH_OFF])) { 97 return FALSE; 98 } else if (!IS_NUM(standardString[MIN_LOW_OFF])) { 99 BufferInsert(standardString, bufLen, MIN_HIGH_OFF, '0'); /* minute only one bit, padding 0 to high bit */ 100 } 101 } else { 102 /* no minute bits, default is 0 */ 103 BufferInsert(standardString, bufLen, HOUR_LOW_OFF + 1, ':'); 104 BufferInsert(standardString, bufLen, MIN_HIGH_OFF, '0'); 105 BufferInsert(standardString, bufLen, MIN_LOW_OFF, '0'); 106 } 107 108 if (standardString[MIN_LOW_OFF + 1] == ':') { 109 if (!IS_NUM(standardString[SEC_HIGH_OFF])) { 110 return FALSE; 111 } else if (!IS_NUM(standardString[SEC_LOW_OFF])) { 112 BufferInsert(standardString, bufLen, SEC_HIGH_OFF, '0'); /* second only one bit, padding 0 to high bit */ 113 } 114 } else { 115 /* no second bits, default is 0 */ 116 BufferInsert(standardString, bufLen, MIN_LOW_OFF + 1, ':'); 117 BufferInsert(standardString, bufLen, SEC_HIGH_OFF, '0'); 118 BufferInsert(standardString, bufLen, SEC_LOW_OFF, '0'); 119 } 120 return TRUE; 121} 122 123STATIC INLINE INT32 StringToDigital(CHAR high, CHAR low) 124{ 125 /* 10: decimal base number */ 126 return ((high - '0') * 10) + (low - '0'); 127} 128 129/* 130 * tzn[+/-]hh[:mm[:ss]][dzn] 131 * tzn + 11 :30 : 7 dzn 132 * tzn - 9 : 7 :11 dzn 133 */ 134void settimezone(const char *buff) 135{ 136#define STANDARD_TZ_LEN 15 137#define MIN_BUF_LEN (OPERATE_OFF + 1) 138 INT32 hour; 139 INT32 minute; 140 INT32 second; 141 size_t buffLen; 142 CHAR standardString[STANDARD_TZ_LEN] = {0}; 143 144 if (buff == NULL) { 145 goto ERROR; 146 } 147 148 buffLen = strlen(buff); 149 if (buffLen < MIN_BUF_LEN) { 150 goto ERROR; 151 } 152 153 (VOID)memset_s(standardString, STANDARD_TZ_LEN, '#', STANDARD_TZ_LEN); 154 if (memcpy_s(standardString, STANDARD_TZ_LEN, buff, buffLen) != EOK) { 155 goto ERROR; 156 } 157 158 if (!TimezoneFormat(standardString, STANDARD_TZ_LEN)) { 159 goto ERROR; 160 } 161 162 hour = StringToDigital(standardString[HOUR_HIGH_OFF], standardString[HOUR_LOW_OFF]); 163 minute = StringToDigital(standardString[MIN_HIGH_OFF], standardString[MIN_LOW_OFF]); 164 second = StringToDigital(standardString[SEC_HIGH_OFF], standardString[SEC_LOW_OFF]); 165 /* [-12:00:00, +14:00:00] limits */ 166 if ((minute > 59 || second > 59) || 167 ((standardString[OPERATE_OFF] == '-') && ((hour > 12) || ((hour == 12) && ((minute != 0) || (second != 0))))) || 168 ((standardString[OPERATE_OFF] == '+') && ((hour > 14) || ((hour == 14) && ((minute != 0) || (second != 0)))))) { 169 goto ERROR; 170 } 171 172 if (lock()) { 173 goto ERROR; 174 } 175 176 /* 1h: 3600s, 1min: 60s */ 177 timezone = hour * 3600 + minute * 60 + second; 178 if (standardString[OPERATE_OFF] == '-') { 179 timezone = -timezone; 180 } 181 182 unlock(); 183 184 return; 185 186ERROR: 187 PRINT_ERR("TZ file data error\n"); 188} 189 190/* DST functions */ 191#define DST_STR_LEN_FORMAT_MDAY 15 /* for example "Feb-03 03:00:00" */ 192#define DST_STR_LEN_FORMAT_WDAY 20 /* for example "Oct-1st-Fri 02:59:59" */ 193#define DST_SET_LENGTH_MAX (DST_STR_LEN_FORMAT_WDAY + 1) 194 195#define MONTH_NAME_LEN 3 196STATIC const CHAR *g_strMonth[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", 197 "Aug", "Sep", "Oct", "Nov", "Dec" }; 198STATIC const CHAR *g_strMonthWeek[] = { "1st", "2nd", "3rd", "4th", "5th" }; 199STATIC const CHAR *g_strWeekDay[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; 200 201STATIC BOOL g_isDstWork = FALSE; 202INT32 g_dstForwardSeconds = 0; 203STATIC CHAR g_strDstStart[DST_SET_LENGTH_MAX] = {0}; 204STATIC CHAR g_strDstEnd[DST_SET_LENGTH_MAX] = {0}; 205 206INT32 DstForwardSecondGet(VOID) 207{ 208 return g_dstForwardSeconds; 209} 210 211STATIC INT32 GetMonthFromString(const CHAR *strMonth) 212{ 213 UINT32 index; 214 for (index = 0; index < sizeof(g_strMonth) / sizeof(CHAR *); index++) { 215 if (strncmp(strMonth, g_strMonth[index], MONTH_NAME_LEN) == 0) { 216 /* A legal month is from 0 to 11. */ 217 return (int)index; 218 } 219 } 220 221 return -1; 222} 223 224STATIC INT32 GetWeekDayFromString(const CHAR *strWeekDay) 225{ 226 UINT32 index; 227 for (index = 0; index < sizeof(g_strWeekDay) / sizeof(CHAR *); index++) { 228 if (strncmp(strWeekDay, g_strWeekDay[index], MONTH_NAME_LEN) == 0) { 229 /* Day of the week (0-6, Sunday = 0) */ 230 return (INT32)index; 231 } 232 } 233 234 return -1; 235} 236 237STATIC INT32 GetMonthWeekFromString(const CHAR *strMonthWeek) 238{ 239 UINT32 index; 240 for (index = 0; index < sizeof(g_strMonthWeek) / sizeof(CHAR *); index++) { 241 if (strncmp(strMonthWeek, g_strMonthWeek[index], MONTH_NAME_LEN) == 0) { 242 /* Month of the week (1-5) */ 243 return (INT32)(index + 1); 244 } 245 } 246 247 return -1; 248} 249 250/* Day of the month 1 ~ 31 */ 251STATIC INT32 GetMonthDayFromString(INT32 month, const CHAR *strMonDay) 252{ 253 INT32 monDay; 254 255 if (((strMonDay[0] < '0') || (strMonDay[0] > '9')) || 256 ((strMonDay[1] < '0') || (strMonDay[1] > '9'))) { 257 return -1; 258 } 259 260 monDay = StringToDigital(strMonDay[0], strMonDay[1]); 261 if (monDay > 31) { 262 return -1; 263 } 264 265 /* Not every year have 29 days in Feb when set DST. */ 266 if ((monDay == 29) && ((month + 1) == 2)) { 267 return -1; 268 } 269 270 if (monDay > g_monLengths[0][month]) { 271 return -1; 272 } 273 274 /* Day of the month (1-31) */ 275 return monDay; 276} 277 278/* 279 * time format HH:MM:SS 280 * index 01234567 281 * 0~23 for hours per day 282 * 0~59 for minutes per hour 283 * 0~59 for seconds per minute 284 */ 285STATIC INT32 GetDaySecondsFromString(const CHAR *strTimeString) 286{ 287 INT32 hour, min, sec; 288 289 if (((strTimeString[0] < '0') || (strTimeString[0] > '9')) || 290 ((strTimeString[1] < '0') || (strTimeString[1] > '9'))) { 291 return -1; 292 } 293 294 if (((strTimeString[3] < '0') || (strTimeString[3] > '9')) || 295 ((strTimeString[4] < '0') || (strTimeString[4] > '9'))) { 296 return -1; 297 } 298 299 if (((strTimeString[6] < '0') || (strTimeString[6] > '9')) || 300 ((strTimeString[7] < '0') || (strTimeString[7] > '9'))) { 301 return -1; 302 } 303 304 if ((strTimeString[2] != ':') || (strTimeString[5] != ':')) { 305 return -1; 306 } 307 308 hour = StringToDigital(strTimeString[0], strTimeString[1]); 309 min = StringToDigital(strTimeString[3], strTimeString[4]); 310 sec = StringToDigital(strTimeString[6], strTimeString[7]); 311 /* Hours (0-23) */ 312 if ((hour < 0) || (hour > 23)) { 313 return -1; 314 } 315 316 /* Minutes (0-59) */ 317 if ((min < 0) || (min > 59)) { 318 return -1; 319 } 320 321 /* Seconds (0-59), not consider of the leap seconds in DST. */ 322 if ((sec < 0) || (sec > 59)) { 323 return -1; 324 } 325 /* 1h: 3600s, 1min: 60s */ 326 return hour * 3600 + min * 60 + sec; 327} 328 329STATIC INT32 DstGetDayOfMonth(INT32 year, INT32 month, INT32 mweek, INT32 wday) 330{ 331#define FIRST_DAY 4 /* the first day of 1970.1.1 is Thursday. */ 332 INT32 firstWeekDay; /* First week day in this month of the specified year. */ 333 INT32 firstMdayOfTargetWday; /* First target month day in this month of the specified year. */ 334 INT32 targetMdayOfTargetWday; /* The target month day specified by user. */ 335 struct tm time = {0}; 336 INT64 seconds, days; 337 338 time.tm_year = year; 339 time.tm_mon = month; 340 time.tm_mday = 1; 341 /* 14: Hour-value range is [0,23] */ 342 time.tm_hour = 14; 343 time.tm_isdst = 0; 344 345 seconds = mktime(&time); 346 347 if (seconds == -1) { 348 return -1; 349 } 350 days = seconds / SECSPERDAY; 351 if (days < 0) { 352 days = -days; 353 firstWeekDay = DAYSPERWEEK - (days - (DAYSPERWEEK - FIRST_DAY)) % DAYSPERWEEK; 354 } else { 355 if (days > FIRST_DAY) { 356 firstWeekDay = 1 + (days - FIRST_DAY) % DAYSPERWEEK; 357 } else { 358 firstWeekDay = FIRST_DAY; 359 } 360 } 361 362 firstMdayOfTargetWday = 1 + (DAYSPERWEEK + wday - firstWeekDay) % DAYSPERWEEK; 363 /* 364 * Users may use 5th weekday to represent the last week of this month 365 * for example "Oct-5th-Fri", but there does not exist the 5th Friday in October, so the last monweek is 4th. 366 */ 367 targetMdayOfTargetWday = firstMdayOfTargetWday + (mweek - 1) * DAYSPERWEEK; 368 if (targetMdayOfTargetWday > g_monLengths[(INT32)isleap(year + TM_YEAR_BASE)][month]) { 369 targetMdayOfTargetWday -= 7; 370 } 371 372 return targetMdayOfTargetWday; 373} 374 375/* 376 * time format decode 377 * 1. Feb-03 03:00:00 378 * idx 012345678901234 379 * 2. Oct-1st-Fri 02:59:59 380 * idx 12345678901234567890 381 */ 382STATIC INT32 DateDecode(INT32 year, const CHAR *dstString, INT32 *month, INT32 *monDay, INT32 *sec) 383{ 384 INT32 monWeek, weekDay; 385 /* For example "Feb-03 03:00:00" */ 386 if (strlen(dstString) == DST_STR_LEN_FORMAT_MDAY) { 387 if ((dstString[3] != '-') || (dstString[6] != ' ')) { 388 return -1; 389 } 390 391 *month = GetMonthFromString(&dstString[0]); 392 if (*month == -1) { 393 return -1; 394 } 395 396 *monDay = GetMonthDayFromString(*month, &dstString[4]); 397 if (*monDay == -1) { 398 return -1; 399 } 400 401 *sec = GetDaySecondsFromString(&dstString[7]); 402 if (*sec == -1) { 403 return -1; 404 } 405 } else if (strlen(dstString) == DST_STR_LEN_FORMAT_WDAY) { 406 /* For example "Oct-1st-Fri 02:59:59" */ 407 if ((dstString[3] != '-') || (dstString[7] != '-') || (dstString[11] != ' ')) { 408 return -1; 409 } 410 411 *month = GetMonthFromString(&dstString[0]); 412 if (*month == -1) { 413 return -1; 414 } 415 416 monWeek = GetMonthWeekFromString(&dstString[4]); 417 if (monWeek == -1) { 418 return -1; 419 } 420 421 weekDay = GetWeekDayFromString(&dstString[8]); 422 if (weekDay == -1) { 423 return -1; 424 } 425 426 *sec = GetDaySecondsFromString(&dstString[12]); 427 if (*sec == -1) { 428 return -1; 429 } 430 431 *monDay = DstGetDayOfMonth(year, *month, monWeek, weekDay); 432 if (*monDay == -1) { 433 return -1; 434 } 435 } else { 436 return -1; 437 } 438 439 return 0; 440} 441 442STATIC INT64 DstConfigDecode(INT32 year, const CHAR *dstString) 443{ 444 INT32 month, monDay, sec; 445 INT32 ret; 446 struct tm time = {0}; 447 INT64 dstSeconds; 448 449 ret = DateDecode(year, dstString, &month, &monDay, &sec); 450 if (ret == -1) { 451 return -1; 452 } 453 /* get the DST period */ 454 time.tm_year = year; 455 time.tm_mon = month; 456 time.tm_mday = monDay; 457 time.tm_isdst = 0; 458 459 dstSeconds = mktime(&time); 460 461 if (dstSeconds == -1) { 462 return -1; 463 } 464 465 return dstSeconds + sec; 466} 467 468STATIC BOOL DstConfigCheck(const CHAR *strDstStart, const CHAR *strDstEnd) 469{ 470 INT64 dstStart, dstEnd; 471 const INT32 year = 70; /* 70 stands for epoch time */ 472 473 if ((strDstStart == NULL) || (strDstEnd == NULL)) { 474 return FALSE; 475 } 476 477 dstStart = DstConfigDecode(year, strDstStart); 478 dstEnd = DstConfigDecode(year, strDstEnd); 479 if ((dstStart < 0) || (dstEnd < 0)) { 480 return FALSE; 481 } 482 483 if (dstStart >= dstEnd) { 484 return FALSE; 485 } 486 487 return TRUE; 488} 489 490STATIC BOOL CheckDstPeriodInner(const struct tm * const tm, INT64 seconds, INT64 dstStart, INT64 dstEnd) 491{ 492 if (tm != NULL) { 493 if (tm->tm_isdst < 0) { 494 /* it must to be. */ 495 if ((seconds >= dstStart) && (seconds < dstStart + g_dstForwardSeconds)) { 496 return FALSE; 497 } 498 499 /* determine the time period of the second pass, out of the DST period. */ 500 if ((seconds > dstEnd) && (seconds <= dstEnd + g_dstForwardSeconds)) { 501 return TRUE; 502 } 503 } else if (tm->tm_isdst > 0) { 504 /* the logical judgment here is the opposite of common sense */ 505 return TRUE; 506 } else { 507 /* tm->tm_isdst is zero */ 508 return FALSE; 509 } 510 } 511 512 if ((seconds < dstStart) || (seconds >= dstEnd)) { 513 return FALSE; /* daylight saving time is not effect. */ 514 } 515 516 return TRUE; 517} 518 519BOOL CheckWithinDstPeriod(const struct tm * const tm, INT64 seconds) 520{ 521 INT64 dstStart, dstEnd; 522 struct tm time = {0}; 523 524 if (g_isDstWork == FALSE) { 525 return FALSE; 526 } 527 528 /* represent a local time. */ 529 if (tm != NULL) { 530 (void)memcpy_s(&time, sizeof(struct tm), tm, sizeof(struct tm)); 531 time.tm_isdst = 0; 532 /* the input-param of seconds is unused in this case. */ 533 seconds = mktime(&time); 534 if (seconds == -1) { 535 return FALSE; 536 } 537 } else { 538 /* represent a standard time, not care TZ. */ 539 if (gmtime_r(&seconds, &time) == NULL) { 540 return FALSE; 541 } 542 } 543 544 dstStart = DstConfigDecode(time.tm_year, g_strDstStart); 545 dstEnd = DstConfigDecode(time.tm_year, g_strDstEnd); 546 if ((dstStart == -1) || (dstEnd == -1)) { 547 return FALSE; 548 } 549 550 return CheckDstPeriodInner(tm, seconds, dstStart, dstEnd); 551} 552 553int dst_disable(VOID) 554{ 555 if (lock()) { 556 return -1; 557 } 558 559 g_isDstWork = FALSE; 560 561 unlock(); 562 563 return 0; 564} 565 566int dst_enable(const char *strDstStartTime, const char *strDstEndTime, int swForwardSeconds) 567{ 568 if (lock()) { 569 return -1; 570 } 571 572 /* Check if the format of dst config is correct. */ 573 if (DstConfigCheck(strDstStartTime, strDstEndTime) != TRUE) { 574 unlock(); 575 return -1; 576 } 577 578 if ((swForwardSeconds < 0) || (swForwardSeconds >= 24 * 3600)) { /* seconds per day 24 * 3600 */ 579 unlock(); 580 return -1; 581 } 582 583 g_isDstWork = FALSE; 584 if (strncpy_s(g_strDstStart, DST_SET_LENGTH_MAX, strDstStartTime, strlen(strDstStartTime)) != EOK) { 585 unlock(); 586 return -1; 587 } 588 if (strncpy_s(g_strDstEnd, DST_SET_LENGTH_MAX, strDstEndTime, strlen(strDstEndTime)) != EOK) { 589 unlock(); 590 return -1; 591 } 592 593 g_dstForwardSeconds = swForwardSeconds; 594 g_isDstWork = TRUE; 595 596 unlock(); 597 598 return 0; 599} 600 601int dst_inquire(int year, struct tm *pstDstStart, struct tm *pstDstEnd) 602{ 603 INT64 dstStart, dstEnd; 604 605 if (lock()) { 606 return -1; 607 } 608 609 if (!g_isDstWork) { 610 unlock(); 611 return -1; 612 } 613 614 if ((pstDstStart == NULL) || (pstDstEnd == NULL)) { 615 unlock(); 616 return -1; 617 } 618 619 dstStart = DstConfigDecode(year, g_strDstStart); 620 dstEnd = DstConfigDecode(year, g_strDstEnd); 621 if ((dstStart == -1) || (dstEnd == -1)) { 622 unlock(); 623 return -1; 624 } 625 626 dstStart += timezone; 627 dstEnd += timezone; 628 if ((gmtime_r(&dstStart, pstDstStart) == NULL) || (gmtime_r(&dstEnd, pstDstEnd) == NULL)) { 629 unlock(); 630 return -1; 631 } 632 633 unlock(); 634 return 0; 635} 636 637