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