1 /* _PDCLIB_timesub( const time_t *, int_fast32_t, const struct state *, struct tm * )
2
3 This file is part of the Public Domain C Library (PDCLib).
4 Permission is granted to use, modify, and / or redistribute at will.
5 */
6
7 #ifndef REGTEST
8
9 #include "pdclib/_PDCLIB_tzcode.h"
10
11 /* Return the number of leap years through the end of the given year
12 where, to make the math easy, the answer for year zero is defined as zero.
13 */
leaps_thru_end_of_nonneg(int y)14 static int leaps_thru_end_of_nonneg( int y )
15 {
16 return y / 4 - y / 100 + y / 400;
17 }
18
leaps_thru_end_of(const int y)19 static int leaps_thru_end_of( const int y )
20 {
21 return ( y < 0
22 ? -1 - leaps_thru_end_of_nonneg( -1 - y )
23 : leaps_thru_end_of_nonneg( y ) );
24 }
25
_PDCLIB_timesub(const time_t * timep,int_fast32_t offset,const struct state * sp,struct tm * tmp)26 struct tm * _PDCLIB_timesub( const time_t * timep, int_fast32_t offset, const struct state * sp, struct tm * tmp )
27 {
28 const struct lsinfo * lp;
29 time_t tdays;
30 int idays; /* unsigned would be so 2003 */
31 int_fast64_t rem;
32 int y;
33 const int * ip;
34 int_fast64_t corr;
35 bool hit;
36 int i;
37
38 corr = 0;
39 hit = false;
40 i = ( sp == NULL ) ? 0 : sp->leapcnt;
41
42 while ( --i >= 0 )
43 {
44 lp = &sp->lsis[ i ];
45 if ( *timep >= lp->trans )
46 {
47 corr = lp->corr;
48 hit = ( *timep == lp->trans && ( i == 0 ? 0 : lp[ -1 ].corr ) < corr );
49 break;
50 }
51 }
52
53 y = EPOCH_YEAR;
54 tdays = *timep / SECSPERDAY;
55 rem = *timep % SECSPERDAY;
56
57 while ( tdays < 0 || tdays >= year_lengths[ _PDCLIB_is_leap( y ) ] )
58 {
59 int newy;
60 time_t tdelta;
61 int idelta;
62 int leapdays;
63
64 tdelta = tdays / DAYSPERLYEAR;
65
66 if ( ! ( ( ! _PDCLIB_TYPE_SIGNED( time_t ) || _PDCLIB_INT_MIN <= tdelta ) && tdelta <= _PDCLIB_INT_MAX ) )
67 {
68 goto out_of_range;
69 }
70
71 idelta = tdelta;
72
73 if ( idelta == 0 )
74 {
75 idelta = ( tdays < 0 ) ? -1 : 1;
76 }
77
78 newy = y;
79
80 if ( _PDCLIB_increment_overflow( &newy, idelta ) )
81 {
82 goto out_of_range;
83 }
84
85 leapdays = leaps_thru_end_of( newy - 1 ) - leaps_thru_end_of( y - 1 );
86 tdays -= ( (time_t)newy - y ) * DAYSPERNYEAR;
87 tdays -= leapdays;
88 y = newy;
89 }
90
91 /* Given the range, we can now fearlessly cast... */
92 idays = tdays;
93 rem += offset - corr;
94
95 while ( rem < 0 )
96 {
97 rem += SECSPERDAY;
98 --idays;
99 }
100
101 while ( rem >= SECSPERDAY )
102 {
103 rem -= SECSPERDAY;
104 ++idays;
105 }
106
107 while ( idays < 0 )
108 {
109 if ( _PDCLIB_increment_overflow( &y, -1 ) )
110 {
111 goto out_of_range;
112 }
113
114 idays += year_lengths[ _PDCLIB_is_leap( y ) ];
115 }
116
117 while ( idays >= year_lengths[ _PDCLIB_is_leap( y ) ] )
118 {
119 idays -= year_lengths[ _PDCLIB_is_leap( y ) ];
120
121 if ( _PDCLIB_increment_overflow( &y, 1 ) )
122 {
123 goto out_of_range;
124 }
125 }
126
127 tmp->tm_year = y;
128
129 if ( _PDCLIB_increment_overflow( &tmp->tm_year, -TM_YEAR_BASE ) )
130 {
131 goto out_of_range;
132 }
133
134 tmp->tm_yday = idays;
135 /* The "extra" mods below avoid overflow problems. */
136 tmp->tm_wday = EPOCH_WDAY +
137 ( ( y - EPOCH_YEAR ) % DAYSPERWEEK ) *
138 ( DAYSPERNYEAR % DAYSPERWEEK ) +
139 leaps_thru_end_of( y - 1 ) -
140 leaps_thru_end_of( EPOCH_YEAR - 1 ) +
141 idays;
142 tmp->tm_wday %= DAYSPERWEEK;
143
144 if ( tmp->tm_wday < 0 )
145 {
146 tmp->tm_wday += DAYSPERWEEK;
147 }
148
149 tmp->tm_hour = (int)( rem / SECSPERHOUR );
150 rem %= SECSPERHOUR;
151 tmp->tm_min = (int)( rem / SECSPERMIN );
152
153 /* A positive leap second requires a special
154 representation. This uses "... ??:59:60" et seq.
155 */
156 tmp->tm_sec = (int) ( rem % SECSPERMIN ) + hit;
157 ip = mon_lengths[ _PDCLIB_is_leap( y ) ];
158
159 for ( tmp->tm_mon = 0; idays >= ip[ tmp->tm_mon ]; ++( tmp->tm_mon ) )
160 {
161 idays -= ip[ tmp->tm_mon ];
162 }
163
164 tmp->tm_mday = (int)( idays + 1 );
165 tmp->tm_isdst = 0;
166 #ifdef TM_GMTOFF
167 tmp->TM_GMTOFF = offset;
168 #endif /* defined TM_GMTOFF */
169 return tmp;
170
171 out_of_range:
172 *_PDCLIB_errno_func() = _PDCLIB_EOVERFLOW;
173 return NULL;
174 }
175
176 #endif
177
178 #ifdef TEST
179
180 #include "_PDCLIB_test.h"
181
main(void)182 int main( void )
183 {
184 #ifndef REGTEST
185 #endif
186
187 return TEST_RESULTS;
188 }
189
190 #endif
191