The 3DS system can handle two kinds of time: ticks as a unit of time, calculated from the system clock; and the real-time clock (RTC) that comes with a battery backup.
The system provides a timer and alarm feature that uses system ticks to allow applications to measure the passage of time.
In addition to a feature to get the current time from the RTC, there is also a feature to set off an alarm at a specified time and date.
8.1. Class for Representing Time
The SDK is designed to use the nn::fnd::TimeSpan
class as an argument to time functions, in part to avoid any confusion about units. Within this class, time is expressed in nanoseconds using 64-bit integers. To prevent ambiguity in the units, no implicit conversion from integers to this data type is provided. However, 0 can be converted implicitly to this data type.
The nn::fnd::TimeSpan
class has static member functions (the From*Seconds
functions) for generating instances of this class from integer values expressing the time in various time units (seconds, milliseconds, microseconds, or nanoseconds). It also has member functions (the Get*Seconds
functions) that can obtain the time value of the instance (measured in seconds, milliseconds, microseconds, or nanoseconds) as an s64
type.
You can also perform comparison (==, !=, <, >, <=, ,>=) and arithmetic (+, -, +=, -=) operations between instances of this class.
// From*Seconds() static nn::fnd::TimeSpan FromSeconds(s64 seconds); static nn::fnd::TimeSpan FromMilliSeconds(s64 milliSeconds); static nn::fnd::TimeSpan FromMicroSeconds(s64 microSeconds); static nn::fnd::TimeSpan FromNanoSeconds(s64 nanoSeconds); // Get*Seconds() s64 GetSeconds() const; s64 GetMilliSeconds() const; s64 GetMicroSeconds() const; s64 GetNanoSeconds() const;
8.2. Ticks
A tick is the time it takes for the CPU to go through one clock cycle while the CPU is operating at 268 MHz (roughly 3.73 nanoseconds). The number of ticks in one second is defined by the nn::os::Tick::TICKS_PER_SECOND
constant.
When running in extended mode on SNAKE, the tick value remains the same as in standard mode. In other words, one tick is the equivalent to three CPU clock cycles while in extended mode.
Tick values are available using the nn::os::Tick
class, and the conversion constructors and conversion operators let you convert from this class to the nn::fnd::TimeSpan
class (which represents actual time), and vice versa.
Two constructors are available: one generates an instance from the tick value represented by an s64
type, and the other generates an instance from the tick value converted from the time represented by an object of the nn::fnd::TimeSpan
class.
The conversion operators include one to convert from a tick value to an s64
-type value and another to convert to an object of the nn::fnd::TimeSpan
class. You can also perform arithmetic (+, -, +=, -=) operations between instances of the nn::os::Tick
class.
In addition, you can call the ToTimeSpan
function to generate an instance of the nn::fnd::TimeSpan
class that expresses the time span converted from a tick value.
Call the GetSystemCurrent
function to get the time elapsed since the system was started up as a tick value (an instance of the nn::os::Tick
class).
8.3. Timers
The timer feature sends a notification when the specified amount of time has passed. A timer object may be in the signaled or non-signaled state, and it transitions from the non-signaled to the signaled state when the specified time has passed. Timer instances are limited to eight at any one time.
Timer objects are defined by the nn::os::Timer
class. Generate an instance and then call the Initialize
or TryInitialize
function to initialize. When initializing a timer object, you can choose whether to manually or automatically reset its signaled state. After a manual-reset timer enters the signaled state, all threads waiting for that timer to enter the signaled state are released and the timer remains in the signaled state until it is cleared. After an automatic-reset timer enters the signaled state, of those threads that are waiting for it to enter the signaled state, only the highest-priority thread is released, after which the object resets itself to the non-signaled state.
There are single-use timers that only transition to the signaled state once after the allotted time, and multi-use periodic timers that transition to the signaled state in cycles of the specified period. Call the StartOneShot
function to start a single-use timer. Call the StartPeriodic
function to start a periodic timer. You can stop both timer types by calling the Stop
function.
Call the Wait
function to cause a thread to wait until a timer enters the signaled state. Call the Signal
function to put a timer in the signaled state without waiting the specified time. After a manually resetting timer enters the signaled state, it remains that way until you call the ClearSignal
function.
Call the Finalize
function to explicitly destroy an instance.
For more information about applications that use timers, see 5.3.1.1. Cautions When Shutting Down. Be sure to carry out all proper processing, such as destroying object instances, at this time.
8.4. Alarms
An alarm is a feature that calls the registered handler after the specified time has passed. Alarms generate threads internally to call the handler. Consequently, you must call the nn::os::InitializeAlarmSystem
function to initialize the alarm system prior to using alarms.
Alarm objects are defined by the nn::os::Alarm
class. Generate an instance and then call the Initialize
or TryInitialize
function to initialize.
There are single-use alarms that only call the handler once after the allotted time, and multi-use periodic timers that call the handler in cycles of the specified period. Call the SetOneShot
function to set a single-use alarm. Call the SetPeriodic
function to set a periodic alarm. You can cancel both alarm types by calling the Cancel
function.
Call the CanSet
function to check whether alarms can be set. This returns false
if an alarm is set and the handler has yet to be called. Even after canceling an alarm by calling Cancel
, this check will not return true
until the handler has been called once.
Handler types are defined below.
typedef void(* nn::os::AlarmHandler)(void *param, bool cancelled);
Any arguments needed when setting the alarm are passed in the param
parameter. Generally, false
is passed in the cancelled
parameter, but true
is passed when the handler has been called after the alarm was canceled with the Cancel
function.
Call the Finalize
function to explicitly destroy an instance.
In the current implementation, the alarm system uses two threads internally. As a result, the alarm system becomes unstable if you register many handlers that take a long time to complete.
For more information about applications that use alarms, see 5.3.1.1. Cautions When Shutting Down. Be sure to carry out all proper processing, such as destroying object instances, at this time.
8.5. RTC
RTC stands for “real-time clock,” the hardware clock included in the system. The clock has its own battery backup and continues keeping time even if the system loses power.
You can only set the clock from the system settings. The clock can be set forward as far as 2049/12/31, but the clock itself can keep time until it reaches 2099/12/31. Because the clock will not reach its upper limit until at least 50 years have passed since it was set, there is no need to check whether the clock has reset itself while an application is running.
This section explains how to use RTC time in an application.
8.5.1. Class for Representing Dates and Times
The CTR-SDK system provides the nn::fnd::DateTime
class for representing dates and times. You can get the current time of the RTC by calling the nn::fnd::DateTime::GetNow
function, which returns an instance of this class.
The constructor with arguments allows you to specify milliseconds in addition to the year, month, day, hours, minutes, and seconds. The constructor with no parameters generates an instance representing 2000/01/01 00:00:00.000.
The dates and times that can be handled by this class are in the range from nn::fnd::DateTime::MIN_DATE_TIME
(1900/01/01 00:00:00.000) to nn::fnd::DateTime::MAX_DATE_TIME
(2189/12/31 23:59:59.999). All years are calculated in the Gregorian calendar with 2000/01/01 (Sat) as the standard, and the dates and times that are calculated assume that one day is exactly 86,400 seconds in length.
When subtracting one nn:fnd::DateTime
class instance from another, the date and time difference is returned as an instance of the nn::fnd::TimeSpan
class. When adding an nn::fnd::TimeSpan
instance to an nn::fnd::DateTime
instance, the resulting date and time is returned as an instance of the nn::fnd::DateTime
class.
The year, month, day, day of the week, hour (in 24-hour notation), minute, second, and millisecond are available as date and time parameters. All of these parameters can be obtained using Get*
functions, and all of them except for the day of the week can be replaced using Replace*
functions. The Get*
function for the day of the week parameter returns an enumerated type (nn::fnd::Week
), whereas the Get*
functions for all other parameters return s32
values. The Replace*
functions return new instances that have the replaced parameters. The parameters of the original instance will not be overwritten.
In the versions of the GetParameters
and FromParameters
functions that take the nn::fnd::DateTimeParameters
structure as arguments, you can get or replace all parameters at the same time. In calls to FromParameters
, the member of the structure that indicates the day of the week are ignored. The result is indeterminate if the date/time parameters are replaced with invalid values. You can check whether a particular set of date/time parameters is valid by calling the IsValidParameters
function. In functions that take a structure as an argument, the day of the week is also checked for validity, so you must be careful when setting the parameters for the structures. If the day of the week is unknown, check the validity using an overloaded version of the function that does not take the day of the week as an argument.
The DateToDays
function returns a value indicating how many days have passed between the reference date (2000/01/01) and the specified date. The result is indeterminate if an invalid date was specified. To check whether a given date is valid, call the IsValidDate
function. To check whether a particular year is a leap year, call the IsLeapYear
function, which returns 1
if the specified year is a leap year.
The DaysToDate
function returns the number of days that have elapsed since the reference date. The DaysToWeekday
function returns the number of days that have elapsed since the reference day of the week.
8.5.2. RTC Alarm Feature
The system includes an RTC alarm feature. This feature sends a notification to the running application when the time set for the alarm is reached. Alarm times can be set by the minute. Depending on the clock settings, the alarm notification may be delayed by around one minute. The alarm feature is suitable as an alarm clock or for similar uses, but it is not suitable for precise time-based notification applications, such as an hourglass. For these uses, see 8.3. Timers or 8.4. Alarms.
Use the RTC alarm feature in the PTM library. You must first initialize the library by calling nn::ptm::Initialize
before you can use it. After you are done using it, call nn::ptm::Finalize
.
nn::Result nn::ptm::Initialize(); nn::Result nn::ptm::Finalize();
The RTC alarm feature notifies the running application that the clock has reached the set time by setting the specified event to the signaled state.
The application cannot receive notification while in Sleep Mode. When displaying the HOME Menu or a library applet, the thread that started the HOME Menu or library applet remains suspended and the event enters the signaled state. Care must be taken not to implement an RTC alarm in a standby thread because it will not have permission to perform graphics or sound operations.
Use the following functions to specify the event to use and to set the alarm time.
nn::Result nn::ptm::RegisterAlarmEvent(nn::os::Event &event); nn::Result nn::ptm::SetRtcAlarm(nn::fnd::DateTime datetime); nn::Result nn::ptm::GetRtcAlarm(nn::fnd::DateTime *pDatetime); nn::Result nn::ptm::CancelRtcAlarm();
Call nn::ptm::RegisterAlarmEvent
to specify the event to receive the RTC alarm feature notification. The application must generate and initialize the instance of the nn::os::Event
class passed in the event
parameter.
Call nn::ptm::SetRtcAlarm
to set the alarm time. For the datetime
parameter, specify an instance of the nn::fnd::DateTime
class with the instance’s time specified in minutes. This function returns nn::ptm::ResultOverWriteAlarm
if the alarm time is already set, but it does indeed overwrite the alarm time with the new value. There is no immediate notification if the time set is in the past.
Call nn::ptm::GetRtcAlarm
to get the alarm’s current setting. For the pDatetime
parameter, pass a pointer to an instance of the nn::fnd::DateTime
class to receive the alarm time. The function returns nn::ptm::ResultNoAlarm
if the alarm is not set.
Call nn::ptm::CancelRtcAlarm
to cancel a previously set RTC alarm. The function returns nn::ptm::ResultNoAlarm
if the alarm is not set.
8.5.3. Handling Time Modification Offset Values
The system records the cumulative offset value in seconds when the user changes the clock time in system settings either forward or back. Call nn::cfg::GetUserTimeOffset
to get this offset value. For more information, see 11.2.8. RTC Modification Offset Value.
When starting an application on the same system, you can compare this offset value with the offset from the last time the application was started to check whether the user has changed the clock in the meantime. You can then use this in your application, but note that, when started on a different system, the application must not impede player progress due to different values.
8.6. Mixed Use of Ticks and RTCs Prohibited
Ticks (the nn::os::Tick
class) and RTCs (the nn::fnd::DateTime
class) handle time similarly, but how time advances and the accuracy is different. If the same time period is measured by tick and RTC, the results are not necessarily the same. For this reason, values obtained with ticks must not be used in combination with values obtained using an RTC.
Ticks differ on individual systems and vary significantly with temperature. They can have a margin of error up to ±300 seconds over a period of a month.
RTCs also differ on individual systems and vary significantly with temperature. They can have a margin of error up to ±60 seconds over a period of a month. nn::fnd::DateTime::GetNow
returns an interpolated current time value so its speed of advance can vary by up to ±1 s per hour.
Most of the time that time is handled within libraries, such as for sound, animation or streaming playback, the speed of advancing time is again different from that for ticks and an RTC. Because of this, when an application must synchronize with a performance or sound playback, you must understand the specifications of individual libraries and use speeds that match those specifications.