Commit 57f1f0874f426a9bdfc5cd3f886113dd5cd17834

Authored by Zhaolei
Committed by Linus Torvalds
1 parent ef1ff6b8c0

time: add function to convert between calendar time and broken-down time for universal use

There are many similar code in kernel for one object: convert time between
calendar time and broken-down time.

Here is some source I found:
  fs/ncpfs/dir.c
  fs/smbfs/proc.c
  fs/fat/misc.c
  fs/udf/udftime.c
  fs/cifs/netmisc.c
  net/netfilter/xt_time.c
  drivers/scsi/ips.c
  drivers/input/misc/hp_sdc_rtc.c
  drivers/rtc/rtc-lib.c
  arch/ia64/hp/sim/boot/fw-emu.c
  arch/m68k/mac/misc.c
  arch/powerpc/kernel/time.c
  arch/parisc/include/asm/rtc.h
  ...

We can make a common function for this type of conversion, At least we
can get following benefit:

1: Make kernel simple and unify
2: Easy to fix bug in converting code
3: Reduce clone of code in future
   For example, I'm trying to make ftrace display walltime,
   this patch will make me easy.

This code is based on code from glibc-2.6

Signed-off-by: Zhao Lei <zhaolei@cn.fujitsu.com>
Cc: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Pavel Machek <pavel@ucw.cz>
Cc: Andi Kleen <andi@firstfloor.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

Showing 3 changed files with 156 additions and 1 deletions Side-by-side Diff

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