1 /*---------------------------------------------------------------------------*
2   Project:  Horizon
3   File:     fnd_DateTime.cpp
4 
5   Copyright (C)2009-2012 Nintendo Co., Ltd.  All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain
8   proprietary information of Nintendo of America Inc. and/or Nintendo
9   Company Ltd., and are protected by Federal copyright law.  They may
10   not be disclosed to third parties or copied or duplicated in any form,
11   in whole or in part, without the prior written consent of Nintendo.
12 
13   $Rev: 47227 $
14  *---------------------------------------------------------------------------*/
15 
16 #include <nn/fnd/fnd_DateTime.h>
17 #include <nn/os/os_SharedInfo.h>
18 #include <nn/assert.h>
19 #include <nn/ptm/CTR/ptm_Rtc.h>
20 #include <algorithm>
21 
22 namespace
23 {
24     static const s32 MILLISECONDS_DAY = 1000 * 60 * 60 * 24;
25 
Modulo32(s32 a,s32 b)26     inline s32 Modulo32(s32 a, s32 b)
27     {
28         return (((a % b) + b) % b);
29     }
30 
AlignedDays(s64 milliSeconds)31     inline s32 AlignedDays(s64 milliSeconds)
32     {
33         s64 ms = milliSeconds + ( 730119LL * MILLISECONDS_DAY);
34         return ms / MILLISECONDS_DAY - 730119LL;
35     }
36 
MilliSecondsOnDay(s64 milliSeconds)37     inline s32 MilliSecondsOnDay(s64 milliSeconds)
38     {
39         s64 ms = milliSeconds + (730119LL * MILLISECONDS_DAY);
40         return ms % MILLISECONDS_DAY;
41     }
42 }
43 
44 namespace nn { namespace fnd {
45 
46     const DateTime DateTime::MAX_DATETIME = DateTime(DateTime::MAX_MILLISECONDS);
47     const DateTime DateTime::MIN_DATETIME = DateTime(DateTime::MIN_MILLISECONDS);
48 
IsLeapYear(s32 year)49     s32 DateTime::IsLeapYear(s32 year)
50     {
51         if ( year % 400 == 0 )
52         {
53             return 1;
54         }
55         else if ( year % 100 == 0 )
56         {
57             return 0;
58         }
59         else if ( year % 4 == 0 )
60         {
61             return 1;
62         }
63         else
64         {
65             return 0;
66         }
67     }
68 
IsValidDate(s32 year,s32 month,s32 day)69     bool DateTime::IsValidDate(s32 year, s32 month, s32 day)
70     {
71         const s32 monthdays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
72         const s64 startYear = DateTime(MIN_MILLISECONDS).GetYear();
73         const s64 endYear   = DateTime(MAX_MILLISECONDS).GetYear();
74 
75         if ( !(startYear <= year && year <= endYear) )
76         {
77             return false;
78         }
79 
80         if ( !(1 <= month && month <= 12) )
81         {
82             return false;
83         }
84 
85         s32 mdays;
86         if ( month == 2 )
87         {
88             mdays = monthdays[month-1] + IsLeapYear(year);
89         }
90         else
91         {
92             mdays = monthdays[month-1];
93         }
94 
95         return 1 <= day && day <= mdays;
96     }
97 
IsValidParameters(s32 year,s32 month,s32 day,s32 hour,s32 minute,s32 second,s32 millisecond)98     bool DateTime::IsValidParameters(s32 year, s32 month, s32 day, s32 hour, s32 minute, s32 second, s32 millisecond)
99     {
100         return IsValidDate(year,month,day)
101             && 0 <= hour   && hour   < 24
102             && 0 <= minute && minute < 60
103             && 0 <= second && second < 60
104             && 0 <= millisecond && millisecond < 1000;
105     }
106 
IsValidParameters(const DateTimeParameters & dateTimeParameters)107     bool DateTime::IsValidParameters(const DateTimeParameters &dateTimeParameters)
108     {
109         return IsValidParameters(
110                 dateTimeParameters.year,
111                 dateTimeParameters.month,
112                 dateTimeParameters.day,
113                 dateTimeParameters.hour,
114                 dateTimeParameters.minute,
115                 dateTimeParameters.second,
116                 dateTimeParameters.milliSecond)
117             && dateTimeParameters.week
118                 == DaysToWeekday(DateToDays(
119                     dateTimeParameters.year,
120                     dateTimeParameters.month,
121                     dateTimeParameters.day));
122     }
123 
DateToDays(s32 year,s32 month,s32 day)124     s32  DateTime::DateToDays(s32 year, s32 month, s32 day)
125     {
126         NN_TASSERT_(DateTime::IsValidDate(year,month,day));
127 
128         day -= 1;
129         year -= 2000;
130 
131         if ( month <= 2 ){
132             month += (12 - 3);
133             year -= 1;
134         }
135         else{
136             month -= 3;
137         }
138 
139         int offset = 1;
140 
141         if ( year < 0 )
142         {
143             offset = IsLeapYear(year);
144         }
145 
146         return ((((365 * 4 + 1) * 25 - 1) * 4 + 1) * (year / 100) / 4)
147              + (( 365 * 4 + 1) * (year % 100) / 4)
148              + (153 * month + 2) / 5
149              + day + (31 + 28)
150              + offset;
151     }
152 
DaysToDate(s32 * pYear,s32 * pMonth,s32 * pDay,s32 days)153     void DateTime::DaysToDate(s32 *pYear, s32 *pMonth, s32 *pDay, s32 days)
154     {
155         const s32 c4days = (((365 * 4 + 1) * 25 - 1) * 4 + 1);
156         const s32 c1days =  ((365 * 4 + 1) * 25 - 1);
157         const s32 y4days =   (365 * 4 + 1);
158         const s32 y1days =    365;
159         s32 year, month, day;
160 
161         days -= 31 + 29;
162 
163         s32 c4    = days / c4days;
164         s32 c4ds  = days % c4days;
165 
166         if ( c4ds < 0)
167         {
168             c4ds += c4days;
169             c4 -= 1;
170         }
171 
172         s32 c1    = c4ds / c1days;
173         s32 c1ds  = c4ds % c1days;
174         s32 y4    = c1ds / y4days;
175         s32 y4ds  = c1ds % y4days;
176         s32 y1    = y4ds / y1days;
177         s32 ydays = y4ds % y1days;
178 
179         year = 2000 + c4 * 400 + c1 * 100 + y4 * 4 + y1;
180         month = (5 * ydays + 2) / 153;
181         day = ydays - (153 * month + 2) / 5 + 1;
182 
183         if (y1 == 4 || c1 == 4)
184         {
185             month = 2 + (12 - 3);
186             day = 29;
187             year -= 1;
188         }
189 
190         if ( month <= (12-3) )
191         {
192             month += 3;
193         }
194         else
195         {
196             month -= 12 - 3;
197             year += 1;
198         }
199 
200         if ( pYear )
201         {
202             *pYear = year;
203         }
204         if ( pMonth )
205         {
206             *pMonth = month;
207         }
208         if ( pDay )
209         {
210             *pDay = day;
211         }
212     }
213 
DaysToWeekday(s32 days)214     Week DateTime::DaysToWeekday(s32 days)
215     {
216         return static_cast<Week>(Modulo32((days + WEEK_SATURDAY), WEEK_MAX));
217     }
218 
DateTime(s32 year,s32 month,s32 day,s32 hour,s32 minute,s32 second,s32 millisecond)219     DateTime::DateTime(s32 year, s32 month, s32 day, s32 hour, s32 minute, s32 second, s32 millisecond)
220         :m_MilliSeconds(
221             DateTime::FromParameters(year,month,day,hour,minute,second,millisecond).m_MilliSeconds)
222     {
223         NN_TASSERT_(MIN_MILLISECONDS <= m_MilliSeconds && m_MilliSeconds <= MAX_MILLISECONDS);
224     }
225 
226 
GetYear() const227     s32  DateTime::GetYear() const
228     {
229         s32 year;
230         DaysToDate(
231             &year,
232             NULL,
233             NULL,
234             AlignedDays(m_MilliSeconds));
235         return year;
236     }
237 
GetMonth() const238     s32  DateTime::GetMonth() const
239     {
240         s32 month;
241         DaysToDate(
242             NULL,
243             &month,
244             NULL,
245             AlignedDays(m_MilliSeconds));
246         return month;
247     }
248 
GetDay() const249     s32  DateTime::GetDay() const
250     {
251         s32 day;
252         DaysToDate(
253             NULL,
254             NULL,
255             &day,
256             AlignedDays(m_MilliSeconds));
257         return day;
258     }
259 
GetWeek() const260     Week DateTime::GetWeek() const
261     {
262         return DaysToWeekday(
263             AlignedDays(m_MilliSeconds));
264     }
265 
GetHour() const266     s32  DateTime::GetHour() const
267     {
268         return MilliSecondsOnDay(m_MilliSeconds) / 1000 / 60 / 60 % 24;
269     }
270 
GetMinute() const271     s32  DateTime::GetMinute() const
272     {
273         return MilliSecondsOnDay(m_MilliSeconds) / 1000 / 60 % 60;
274     }
275 
GetSecond() const276     s32  DateTime::GetSecond() const
277     {
278         return MilliSecondsOnDay(m_MilliSeconds) / 1000 % 60;
279     }
280 
GetMilliSecond() const281     s32  DateTime::GetMilliSecond() const
282     {
283         return MilliSecondsOnDay(m_MilliSeconds) % 1000;
284     }
285 
FromParameters(s32 year,s32 month,s32 day,s32 hour,s32 minute,s32 second,s32 millisecond)286     DateTime DateTime::FromParameters(s32 year, s32 month, s32 day, s32 hour, s32 minute, s32 second, s32 millisecond)
287     {
288         NN_TASSERT_(IsValidParameters(year, month, day, hour, minute, second, millisecond));
289         DateTime datetime;
290         datetime.m_MilliSeconds =
291             millisecond
292           + 1000LL * second
293           + 1000LL * 60 * minute
294           + 1000LL * 60 * 60 * hour
295           + 1000LL * 60 * 60 * 24 * static_cast<s64>(DateToDays(year,month,day));
296         return datetime;
297     }
298 
FromParameters(const DateTimeParameters & dateTimeParameters)299     DateTime DateTime::FromParameters(const DateTimeParameters &dateTimeParameters)
300     {
301         return FromParameters(
302             dateTimeParameters.year,
303             dateTimeParameters.month,
304             dateTimeParameters.day,
305             dateTimeParameters.hour,
306             dateTimeParameters.minute,
307             dateTimeParameters.second,
308             dateTimeParameters.milliSecond);
309     }
310 
ReplaceMilliSecond(s32 millisecond) const311     DateTime DateTime::ReplaceMilliSecond(s32 millisecond) const
312     {
313         NN_TASSERT_(0 <= millisecond && millisecond < 1000);
314         return *this + TimeSpan::FromMilliSeconds(millisecond - GetMilliSecond());
315     }
316 
ReplaceSecond(s32 second) const317     DateTime DateTime::ReplaceSecond(s32 second) const
318     {
319         NN_TASSERT_(0 <= second && second < 60);
320         return *this + TimeSpan::FromSeconds(second - GetSecond());
321     }
322 
ReplaceMinute(s32 minute) const323     DateTime DateTime::ReplaceMinute(s32 minute) const
324     {
325         NN_TASSERT_(0 <= minute && minute < 60);
326         return *this + TimeSpan::FromMinutes(minute - GetMinute());
327     }
328 
ReplaceHour(s32 hour) const329     DateTime DateTime::ReplaceHour(s32 hour) const
330     {
331         NN_TASSERT_(0 <= hour && hour < 24);
332         return *this + TimeSpan::FromHours(hour - GetHour());
333     }
334 
ReplaceDay(s32 day) const335     DateTime DateTime::ReplaceDay(s32 day) const
336     {
337         s32 year,month;
338         DaysToDate(
339             &year,
340             &month,
341             NULL,
342             AlignedDays(m_MilliSeconds));
343         return FromParameters(
344             year,
345             month,
346             day,
347             GetHour(),
348             GetMinute(),
349             GetSecond(),
350             GetMilliSecond());
351     }
352 
ReplaceMonth(s32 month) const353     DateTime DateTime::ReplaceMonth(s32 month) const
354     {
355         s32 year,day;
356         DaysToDate(
357             &year,
358             NULL,
359             &day,
360             AlignedDays(m_MilliSeconds));
361         return FromParameters(
362             year,
363             month,
364             day,
365             GetHour(),
366             GetMinute(),
367             GetSecond(),
368             GetMilliSecond());
369     }
370 
ReplaceYear(s32 year) const371     DateTime DateTime::ReplaceYear(s32 year) const
372     {
373         s32 month,day;
374         DaysToDate(
375             NULL,
376             &month,
377             &day,
378             AlignedDays(m_MilliSeconds));
379         return FromParameters(
380             year,
381             month,
382             day,
383             GetHour(),
384             GetMinute(),
385             GetSecond(),
386             GetMilliSecond());
387     }
388 
GetNow()389     DateTime DateTime::GetNow()
390     {
391         return nn::fnd::DateTime::MIN_DATETIME
392 #ifdef NN_PROCESSOR_ARM11MPCORE
393              + nn::fnd::TimeSpan::FromMilliSeconds(nn::ptm::CTR::detail::GetSwcMilliSeconds())
394 #endif
395             ;
396     }
397 
GetParameters() const398     DateTimeParameters DateTime::GetParameters() const
399     {
400         DateTimeParameters parameters;
401 
402         s32 msec = MilliSecondsOnDay(m_MilliSeconds);
403         s32 days = AlignedDays(m_MilliSeconds);
404         s32 year,month,day;
405 
406         DaysToDate(&year,&month,&day, days);
407 
408         parameters.year = year;
409         parameters.month = month;
410         parameters.day = day;
411         parameters.week = DaysToWeekday(days);
412         parameters.hour = msec / (1000 * 60 * 60);
413         parameters.minute = msec / (1000 * 60) % 60;
414         parameters.second = msec / 1000 % 60;
415         parameters.milliSecond = msec % 1000;
416 
417         return parameters;
418     }
419 }}
420 
421 /*---------------------------------------------------------------------------*
422   End of file
423  *---------------------------------------------------------------------------*/
424