1 /*
2 Copyright (c) 1994 Cygnus Support.
3 All rights reserved.
4 
5 Redistribution and use in source and binary forms are permitted
6 provided that the above copyright notice and this paragraph are
7 duplicated in all such forms and that any documentation,
8 and/or other materials related to such
9 distribution and use acknowledge that the software was developed
10 at Cygnus Support, Inc.  Cygnus Support, Inc. may not be used to
11 endorse or promote products derived from this software without
12 specific prior written permission.
13 THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14 IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17 /*
18  * localtime_r.c
19  * Original Author: Adapted from tzcode maintained by Arthur David Olson.
20  * Modifications:
21  * - Changed to mktm_r and added __tzcalc_limits - 04/10/02, Jeff Johnston
22  * - Fixed bug in mday computations - 08/12/04, Alex Mogilnikov <alx@intellectronika.ru>
23  * - Fixed bug in __tzcalc_limits - 08/12/04, Alex Mogilnikov <alx@intellectronika.ru>
24  * - Implement localtime_r() with gmtime_r() and the conditional code moved
25  *   from _mktm_r() - 05/09/14, Freddie Chopin <freddie_chopin@op.pl>
26  *
27  * Converts the calendar time pointed to by tim_p into a broken-down time
28  * expressed as local time. Returns a pointer to a structure containing the
29  * broken-down time.
30  */
31 
32 #include "local.h"
33 
34 struct tm *
localtime_r(const time_t * __restrict tim_p,struct tm * __restrict res)35 localtime_r (const time_t *__restrict tim_p,
36 	struct tm *__restrict res)
37 {
38   long offset;
39   int hours, mins, secs;
40   int year;
41   __tzinfo_type *const tz = __gettzinfo ();
42   const uint8_t *ip;
43 
44   res = gmtime_r (tim_p, res);
45 
46   year = res->tm_year + YEAR_BASE;
47   ip = __month_lengths[isleap(year)];
48 
49   TZ_LOCK;
50   _tzset_unlocked ();
51   if (_daylight)
52     {
53       if (year == tz->__tzyear || __tzcalc_limits (year))
54 	res->tm_isdst = (tz->__tznorth
55 	  ? (*tim_p >= tz->__tzrule[0].change
56 	  && *tim_p < tz->__tzrule[1].change)
57 	  : (*tim_p >= tz->__tzrule[0].change
58 	  || *tim_p < tz->__tzrule[1].change));
59       else
60 	res->tm_isdst = -1;
61     }
62   else
63     res->tm_isdst = 0;
64 
65   offset = (res->tm_isdst == 1
66     ? tz->__tzrule[1].offset
67     : tz->__tzrule[0].offset);
68 
69   hours = (int) (offset / SECSPERHOUR);
70   offset = offset % SECSPERHOUR;
71 
72   mins = (int) (offset / SECSPERMIN);
73   secs = (int) (offset % SECSPERMIN);
74 
75   res->tm_sec -= secs;
76   res->tm_min -= mins;
77   res->tm_hour -= hours;
78 
79   if (res->tm_sec >= SECSPERMIN)
80     {
81       res->tm_min += 1;
82       res->tm_sec -= SECSPERMIN;
83     }
84   else if (res->tm_sec < 0)
85     {
86       res->tm_min -= 1;
87       res->tm_sec += SECSPERMIN;
88     }
89   if (res->tm_min >= MINSPERHOUR)
90     {
91       res->tm_hour += 1;
92       res->tm_min -= MINSPERHOUR;
93     }
94   else if (res->tm_min < 0)
95     {
96       res->tm_hour -= 1;
97       res->tm_min += MINSPERHOUR;
98     }
99   if (res->tm_hour >= HOURSPERDAY)
100     {
101       ++res->tm_yday;
102       ++res->tm_wday;
103       if (res->tm_wday > 6)
104 	res->tm_wday = 0;
105       ++res->tm_mday;
106       res->tm_hour -= HOURSPERDAY;
107       if (res->tm_mday > ip[res->tm_mon])
108 	{
109 	  res->tm_mday -= ip[res->tm_mon];
110 	  res->tm_mon += 1;
111 	  if (res->tm_mon == 12)
112 	    {
113 	      res->tm_mon = 0;
114 	      res->tm_year += 1;
115 	      res->tm_yday = 0;
116 	    }
117 	}
118     }
119   else if (res->tm_hour < 0)
120     {
121       res->tm_yday -= 1;
122       res->tm_wday -= 1;
123       if (res->tm_wday < 0)
124 	res->tm_wday = 6;
125       res->tm_mday -= 1;
126       res->tm_hour += 24;
127       if (res->tm_mday == 0)
128 	{
129 	  res->tm_mon -= 1;
130 	  if (res->tm_mon < 0)
131 	    {
132 	      res->tm_mon = 11;
133 	      res->tm_year -= 1;
134 	      res->tm_yday = 364 + isleap(res->tm_year + YEAR_BASE);
135 	    }
136 	  res->tm_mday = ip[res->tm_mon];
137 	}
138     }
139   TZ_UNLOCK;
140 
141   return (res);
142 }
143