18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: LGPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 1993, 1994, 1995, 1996, 1997 Free Software Foundation, Inc. 48c2ecf20Sopenharmony_ci * This file is part of the GNU C Library. 58c2ecf20Sopenharmony_ci * Contributed by Paul Eggert (eggert@twinsun.com). 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * The GNU C Library is free software; you can redistribute it and/or 88c2ecf20Sopenharmony_ci * modify it under the terms of the GNU Library General Public License as 98c2ecf20Sopenharmony_ci * published by the Free Software Foundation; either version 2 of the 108c2ecf20Sopenharmony_ci * License, or (at your option) any later version. 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * The GNU C Library is distributed in the hope that it will be useful, 138c2ecf20Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 148c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 158c2ecf20Sopenharmony_ci * Library General Public License for more details. 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * You should have received a copy of the GNU Library General Public 188c2ecf20Sopenharmony_ci * License along with the GNU C Library; see the file COPYING.LIB. If not, 198c2ecf20Sopenharmony_ci * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 208c2ecf20Sopenharmony_ci * Boston, MA 02111-1307, USA. 218c2ecf20Sopenharmony_ci */ 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/* 248c2ecf20Sopenharmony_ci * Converts the calendar time to broken-down time representation 258c2ecf20Sopenharmony_ci * Based on code from glibc-2.6 268c2ecf20Sopenharmony_ci * 278c2ecf20Sopenharmony_ci * 2009-7-14: 288c2ecf20Sopenharmony_ci * Moved from glibc-2.6 to kernel by Zhaolei<zhaolei@cn.fujitsu.com> 298c2ecf20Sopenharmony_ci */ 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#include <linux/time.h> 328c2ecf20Sopenharmony_ci#include <linux/module.h> 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* 358c2ecf20Sopenharmony_ci * Nonzero if YEAR is a leap year (every 4 years, 368c2ecf20Sopenharmony_ci * except every 100th isn't, and every 400th is). 378c2ecf20Sopenharmony_ci */ 388c2ecf20Sopenharmony_cistatic int __isleap(long year) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci return (year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0); 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* do a mathdiv for long type */ 448c2ecf20Sopenharmony_cistatic long math_div(long a, long b) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci return a / b - (a % b < 0); 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci/* How many leap years between y1 and y2, y1 must less or equal to y2 */ 508c2ecf20Sopenharmony_cistatic long leaps_between(long y1, long y2) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci long leaps1 = math_div(y1 - 1, 4) - math_div(y1 - 1, 100) 538c2ecf20Sopenharmony_ci + math_div(y1 - 1, 400); 548c2ecf20Sopenharmony_ci long leaps2 = math_div(y2 - 1, 4) - math_div(y2 - 1, 100) 558c2ecf20Sopenharmony_ci + math_div(y2 - 1, 400); 568c2ecf20Sopenharmony_ci return leaps2 - leaps1; 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci/* How many days come before each month (0-12). */ 608c2ecf20Sopenharmony_cistatic const unsigned short __mon_yday[2][13] = { 618c2ecf20Sopenharmony_ci /* Normal years. */ 628c2ecf20Sopenharmony_ci {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}, 638c2ecf20Sopenharmony_ci /* Leap years. */ 648c2ecf20Sopenharmony_ci {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366} 658c2ecf20Sopenharmony_ci}; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci#define SECS_PER_HOUR (60 * 60) 688c2ecf20Sopenharmony_ci#define SECS_PER_DAY (SECS_PER_HOUR * 24) 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci/** 718c2ecf20Sopenharmony_ci * time64_to_tm - converts the calendar time to local broken-down time 728c2ecf20Sopenharmony_ci * 738c2ecf20Sopenharmony_ci * @totalsecs the number of seconds elapsed since 00:00:00 on January 1, 1970, 748c2ecf20Sopenharmony_ci * Coordinated Universal Time (UTC). 758c2ecf20Sopenharmony_ci * @offset offset seconds adding to totalsecs. 768c2ecf20Sopenharmony_ci * @result pointer to struct tm variable to receive broken-down time 778c2ecf20Sopenharmony_ci */ 788c2ecf20Sopenharmony_civoid time64_to_tm(time64_t totalsecs, int offset, struct tm *result) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci long days, rem, y; 818c2ecf20Sopenharmony_ci int remainder; 828c2ecf20Sopenharmony_ci const unsigned short *ip; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci days = div_s64_rem(totalsecs, SECS_PER_DAY, &remainder); 858c2ecf20Sopenharmony_ci rem = remainder; 868c2ecf20Sopenharmony_ci rem += offset; 878c2ecf20Sopenharmony_ci while (rem < 0) { 888c2ecf20Sopenharmony_ci rem += SECS_PER_DAY; 898c2ecf20Sopenharmony_ci --days; 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci while (rem >= SECS_PER_DAY) { 928c2ecf20Sopenharmony_ci rem -= SECS_PER_DAY; 938c2ecf20Sopenharmony_ci ++days; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci result->tm_hour = rem / SECS_PER_HOUR; 978c2ecf20Sopenharmony_ci rem %= SECS_PER_HOUR; 988c2ecf20Sopenharmony_ci result->tm_min = rem / 60; 998c2ecf20Sopenharmony_ci result->tm_sec = rem % 60; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci /* January 1, 1970 was a Thursday. */ 1028c2ecf20Sopenharmony_ci result->tm_wday = (4 + days) % 7; 1038c2ecf20Sopenharmony_ci if (result->tm_wday < 0) 1048c2ecf20Sopenharmony_ci result->tm_wday += 7; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci y = 1970; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci while (days < 0 || days >= (__isleap(y) ? 366 : 365)) { 1098c2ecf20Sopenharmony_ci /* Guess a corrected year, assuming 365 days per year. */ 1108c2ecf20Sopenharmony_ci long yg = y + math_div(days, 365); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci /* Adjust DAYS and Y to match the guessed year. */ 1138c2ecf20Sopenharmony_ci days -= (yg - y) * 365 + leaps_between(y, yg); 1148c2ecf20Sopenharmony_ci y = yg; 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci result->tm_year = y - 1900; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci result->tm_yday = days; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci ip = __mon_yday[__isleap(y)]; 1228c2ecf20Sopenharmony_ci for (y = 11; days < ip[y]; y--) 1238c2ecf20Sopenharmony_ci continue; 1248c2ecf20Sopenharmony_ci days -= ip[y]; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci result->tm_mon = y; 1278c2ecf20Sopenharmony_ci result->tm_mday = days + 1; 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ciEXPORT_SYMBOL(time64_to_tm); 130