Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: LGPL-2.0+
0002 /*
0003  * Copyright (C) 1993, 1994, 1995, 1996, 1997 Free Software Foundation, Inc.
0004  * This file is part of the GNU C Library.
0005  * Contributed by Paul Eggert (eggert@twinsun.com).
0006  *
0007  * The GNU C Library is free software; you can redistribute it and/or
0008  * modify it under the terms of the GNU Library General Public License as
0009  * published by the Free Software Foundation; either version 2 of the
0010  * License, or (at your option) any later version.
0011  *
0012  * The GNU C Library is distributed in the hope that it will be useful,
0013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0015  * Library General Public License for more details.
0016  *
0017  * You should have received a copy of the GNU Library General Public
0018  * License along with the GNU C Library; see the file COPYING.LIB.  If not,
0019  * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
0020  * Boston, MA 02111-1307, USA.
0021  */
0022 
0023 /*
0024  * Converts the calendar time to broken-down time representation
0025  *
0026  * 2009-7-14:
0027  *   Moved from glibc-2.6 to kernel by Zhaolei<zhaolei@cn.fujitsu.com>
0028  * 2021-06-02:
0029  *   Reimplemented by Cassio Neri <cassio.neri@gmail.com>
0030  */
0031 
0032 #include <linux/time.h>
0033 #include <linux/module.h>
0034 #include <linux/kernel.h>
0035 
0036 #define SECS_PER_HOUR   (60 * 60)
0037 #define SECS_PER_DAY    (SECS_PER_HOUR * 24)
0038 
0039 /**
0040  * time64_to_tm - converts the calendar time to local broken-down time
0041  *
0042  * @totalsecs:  the number of seconds elapsed since 00:00:00 on January 1, 1970,
0043  *      Coordinated Universal Time (UTC).
0044  * @offset: offset seconds adding to totalsecs.
0045  * @result: pointer to struct tm variable to receive broken-down time
0046  */
0047 void time64_to_tm(time64_t totalsecs, int offset, struct tm *result)
0048 {
0049     u32 u32tmp, day_of_century, year_of_century, day_of_year, month, day;
0050     u64 u64tmp, udays, century, year;
0051     bool is_Jan_or_Feb, is_leap_year;
0052     long days, rem;
0053     int remainder;
0054 
0055     days = div_s64_rem(totalsecs, SECS_PER_DAY, &remainder);
0056     rem = remainder;
0057     rem += offset;
0058     while (rem < 0) {
0059         rem += SECS_PER_DAY;
0060         --days;
0061     }
0062     while (rem >= SECS_PER_DAY) {
0063         rem -= SECS_PER_DAY;
0064         ++days;
0065     }
0066 
0067     result->tm_hour = rem / SECS_PER_HOUR;
0068     rem %= SECS_PER_HOUR;
0069     result->tm_min = rem / 60;
0070     result->tm_sec = rem % 60;
0071 
0072     /* January 1, 1970 was a Thursday. */
0073     result->tm_wday = (4 + days) % 7;
0074     if (result->tm_wday < 0)
0075         result->tm_wday += 7;
0076 
0077     /*
0078      * The following algorithm is, basically, Proposition 6.3 of Neri
0079      * and Schneider [1]. In a few words: it works on the computational
0080      * (fictitious) calendar where the year starts in March, month = 2
0081      * (*), and finishes in February, month = 13. This calendar is
0082      * mathematically convenient because the day of the year does not
0083      * depend on whether the year is leap or not. For instance:
0084      *
0085      * March 1st        0-th day of the year;
0086      * ...
0087      * April 1st        31-st day of the year;
0088      * ...
0089      * January 1st      306-th day of the year; (Important!)
0090      * ...
0091      * February 28th    364-th day of the year;
0092      * February 29th    365-th day of the year (if it exists).
0093      *
0094      * After having worked out the date in the computational calendar
0095      * (using just arithmetics) it's easy to convert it to the
0096      * corresponding date in the Gregorian calendar.
0097      *
0098      * [1] "Euclidean Affine Functions and Applications to Calendar
0099      * Algorithms". https://arxiv.org/abs/2102.06959
0100      *
0101      * (*) The numbering of months follows tm more closely and thus,
0102      * is slightly different from [1].
0103      */
0104 
0105     udays   = ((u64) days) + 2305843009213814918ULL;
0106 
0107     u64tmp      = 4 * udays + 3;
0108     century     = div64_u64_rem(u64tmp, 146097, &u64tmp);
0109     day_of_century  = (u32) (u64tmp / 4);
0110 
0111     u32tmp      = 4 * day_of_century + 3;
0112     u64tmp      = 2939745ULL * u32tmp;
0113     year_of_century = upper_32_bits(u64tmp);
0114     day_of_year = lower_32_bits(u64tmp) / 2939745 / 4;
0115 
0116     year        = 100 * century + year_of_century;
0117     is_leap_year    = year_of_century ? !(year_of_century % 4) : !(century % 4);
0118 
0119     u32tmp      = 2141 * day_of_year + 132377;
0120     month       = u32tmp >> 16;
0121     day     = ((u16) u32tmp) / 2141;
0122 
0123     /*
0124      * Recall that January 1st is the 306-th day of the year in the
0125      * computational (not Gregorian) calendar.
0126      */
0127     is_Jan_or_Feb   = day_of_year >= 306;
0128 
0129     /* Convert to the Gregorian calendar and adjust to Unix time. */
0130     year        = year + is_Jan_or_Feb - 6313183731940000ULL;
0131     month       = is_Jan_or_Feb ? month - 12 : month;
0132     day     = day + 1;
0133     day_of_year += is_Jan_or_Feb ? -306 : 31 + 28 + is_leap_year;
0134 
0135     /* Convert to tm's format. */
0136     result->tm_year = (long) (year - 1900);
0137     result->tm_mon  = (int) month;
0138     result->tm_mday = (int) day;
0139     result->tm_yday = (int) day_of_year;
0140 }
0141 EXPORT_SYMBOL(time64_to_tm);