Branch data Line data Source code
1 : : // SPDX-License-Identifier: LGPL-2.0+ 2 : : /* 3 : : * Copyright (C) 1993, 1994, 1995, 1996, 1997 Free Software Foundation, Inc. 4 : : * This file is part of the GNU C Library. 5 : : * Contributed by Paul Eggert (eggert@twinsun.com). 6 : : * 7 : : * The GNU C Library is free software; you can redistribute it and/or 8 : : * modify it under the terms of the GNU Library General Public License as 9 : : * published by the Free Software Foundation; either version 2 of the 10 : : * License, or (at your option) any later version. 11 : : * 12 : : * The GNU C Library is distributed in the hope that it will be useful, 13 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 : : * Library General Public License for more details. 16 : : * 17 : : * You should have received a copy of the GNU Library General Public 18 : : * License along with the GNU C Library; see the file COPYING.LIB. If not, 19 : : * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 20 : : * Boston, MA 02111-1307, USA. 21 : : */ 22 : : 23 : : /* 24 : : * Converts the calendar time to broken-down time representation 25 : : * Based on code from glibc-2.6 26 : : * 27 : : * 2009-7-14: 28 : : * Moved from glibc-2.6 to kernel by Zhaolei<zhaolei@cn.fujitsu.com> 29 : : */ 30 : : 31 : : #include <linux/time.h> 32 : : #include <linux/module.h> 33 : : 34 : : /* 35 : : * Nonzero if YEAR is a leap year (every 4 years, 36 : : * except every 100th isn't, and every 400th is). 37 : : */ 38 : : static int __isleap(long year) 39 : : { 40 : 3 : return (year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0); 41 : : } 42 : : 43 : : /* do a mathdiv for long type */ 44 : : static long math_div(long a, long b) 45 : : { 46 : 2 : return a / b - (a % b < 0); 47 : : } 48 : : 49 : : /* How many leap years between y1 and y2, y1 must less or equal to y2 */ 50 : 2 : static long leaps_between(long y1, long y2) 51 : : { 52 : 2 : long leaps1 = math_div(y1 - 1, 4) - math_div(y1 - 1, 100) 53 : : + math_div(y1 - 1, 400); 54 : 2 : long leaps2 = math_div(y2 - 1, 4) - math_div(y2 - 1, 100) 55 : : + math_div(y2 - 1, 400); 56 : 2 : return leaps2 - leaps1; 57 : : } 58 : : 59 : : /* How many days come before each month (0-12). */ 60 : : static const unsigned short __mon_yday[2][13] = { 61 : : /* Normal years. */ 62 : : {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}, 63 : : /* Leap years. */ 64 : : {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366} 65 : : }; 66 : : 67 : : #define SECS_PER_HOUR (60 * 60) 68 : : #define SECS_PER_DAY (SECS_PER_HOUR * 24) 69 : : 70 : : /** 71 : : * time64_to_tm - converts the calendar time to local broken-down time 72 : : * 73 : : * @totalsecs the number of seconds elapsed since 00:00:00 on January 1, 1970, 74 : : * Coordinated Universal Time (UTC). 75 : : * @offset offset seconds adding to totalsecs. 76 : : * @result pointer to struct tm variable to receive broken-down time 77 : : */ 78 : 3 : void time64_to_tm(time64_t totalsecs, int offset, struct tm *result) 79 : : { 80 : : long days, rem, y; 81 : : int remainder; 82 : : const unsigned short *ip; 83 : : 84 : 3 : days = div_s64_rem(totalsecs, SECS_PER_DAY, &remainder); 85 : 3 : rem = remainder; 86 : 3 : rem += offset; 87 : 3 : while (rem < 0) { 88 : 0 : rem += SECS_PER_DAY; 89 : 0 : --days; 90 : : } 91 : 3 : while (rem >= SECS_PER_DAY) { 92 : 0 : rem -= SECS_PER_DAY; 93 : 0 : ++days; 94 : : } 95 : : 96 : 3 : result->tm_hour = rem / SECS_PER_HOUR; 97 : 3 : rem %= SECS_PER_HOUR; 98 : 3 : result->tm_min = rem / 60; 99 : 3 : result->tm_sec = rem % 60; 100 : : 101 : : /* January 1, 1970 was a Thursday. */ 102 : 3 : result->tm_wday = (4 + days) % 7; 103 : 3 : if (result->tm_wday < 0) 104 : 0 : result->tm_wday += 7; 105 : : 106 : : y = 1970; 107 : : 108 : 3 : while (days < 0 || days >= (__isleap(y) ? 366 : 365)) { 109 : : /* Guess a corrected year, assuming 365 days per year. */ 110 : 2 : long yg = y + math_div(days, 365); 111 : : 112 : : /* Adjust DAYS and Y to match the guessed year. */ 113 : 2 : days -= (yg - y) * 365 + leaps_between(y, yg); 114 : : y = yg; 115 : : } 116 : : 117 : 3 : result->tm_year = y - 1900; 118 : : 119 : 3 : result->tm_yday = days; 120 : : 121 : 3 : ip = __mon_yday[__isleap(y)]; 122 : 3 : for (y = 11; days < ip[y]; y--) 123 : 3 : continue; 124 : 3 : days -= ip[y]; 125 : : 126 : 3 : result->tm_mon = y; 127 : 3 : result->tm_mday = days + 1; 128 : 3 : } 129 : : EXPORT_SYMBOL(time64_to_tm);