@@ -48,6 +48,7 @@ pub mod secp256k1;
4848pub mod securechip;
4949pub mod ui;
5050
51+ use :: util:: c_types:: c_int;
5152use core:: time:: Duration ;
5253
5354pub use bitbox02_sys:: buffer_t;
@@ -116,45 +117,103 @@ pub fn reset(status: bool) {
116117 unsafe { bitbox02_sys:: reset_reset ( status) }
117118}
118119
119- pub fn strftime ( timestamp : u32 , format : & str ) -> String {
120- let mut out = [ 0u8 ; 100 ] ;
121- unsafe {
122- bitbox02_sys:: strftime (
123- out. as_mut_ptr ( ) ,
124- out. len ( ) as _ ,
125- crate :: util:: str_to_cstr_vec ( format) . unwrap ( ) . as_ptr ( ) ,
126- bitbox02_sys:: localtime ( & ( timestamp as bitbox02_sys:: time_t ) ) ,
127- ) ;
128- }
129- crate :: util:: str_from_null_terminated ( & out[ ..] )
130- . unwrap ( )
131- . into ( )
120+ pub struct Tm {
121+ tm : bitbox02_sys:: tm ,
132122}
133123
134- #[ cfg( not( feature = "testing" ) ) ]
135- pub fn format_datetime ( timestamp : u32 , timezone_offset : i32 , date_only : bool ) -> String {
136- let mut out = [ 0u8 ; 100 ] ;
137- unsafe {
138- bitbox02_sys:: util_format_datetime (
139- timestamp,
140- timezone_offset,
141- date_only,
142- out. as_mut_ptr ( ) ,
143- out. len ( ) as _ ,
124+ fn range ( low : c_int , item : c_int , high : c_int ) -> c_int {
125+ core:: cmp:: max ( low, core:: cmp:: min ( item, high) )
126+ }
127+
128+ impl Tm {
129+ /// Returns the weekday, one of "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
130+ pub fn weekday ( & self ) -> String {
131+ // Same as '%a' in strftime:
132+ // https://github.com/arnoldrobbins/strftime/blob/2011b7e82365d25220b8949e252eb5f28c0994cd/strftime.c#435
133+ let wday = self . tm . tm_wday ;
134+ if !( 0 ..=6 ) . contains ( & wday) {
135+ return "?" . into ( ) ;
136+ }
137+ [ "Sun" , "Mon" , "Tue" , "Wed" , "Thu" , "Fri" , "Sat" ] [ wday as usize ] . into ( )
138+ }
139+
140+ /// Returns 'year-month-day', e.g. 2024-07-16, equivalent of '%Y-%m-%d' in strftime.
141+ pub fn date ( & self ) -> String {
142+ // Same as strftime:
143+ // %Y - https://github.com/arnoldrobbins/strftime/blob/2011b7e82365d25220b8949e252eb5f28c0994cd/strftime.c#L712
144+ // %m - https://github.com/arnoldrobbins/strftime/blob/2011b7e82365d25220b8949e252eb5f28c0994cd/strftime.c#L600
145+ // %d - https://github.com/arnoldrobbins/strftime/blob/2011b7e82365d25220b8949e252eb5f28c0994cd/strftime.c#L498
146+ format ! (
147+ "{}-{:02}-{:02}" ,
148+ 1900 + self . tm. tm_year,
149+ range( 0 , self . tm. tm_mon, 11 ) + 1 ,
150+ range( 1 , self . tm. tm_mday, 31 )
144151 )
145152 }
146- crate :: util:: str_from_null_terminated ( & out[ ..] )
147- . unwrap ( )
148- . into ( )
153+
154+ /// Returns the zero-padded hour from 00-23, e.g. "07".
155+ pub fn hour ( & self ) -> String {
156+ // Same as '%H' in strftime:
157+ // https://github.com/arnoldrobbins/strftime/blob/2011b7e82365d25220b8949e252eb5f28c0994cd/strftime.c#582
158+ format ! ( "{:02}" , range( 0 , self . tm. tm_hour, 23 ) )
159+ }
160+
161+ /// Returns the zero-padded minute from 00-59, e.g. "07".
162+ pub fn minute ( & self ) -> String {
163+ // Same as '%M' in strftime:
164+ // https://github.com/arnoldrobbins/strftime/blob/2011b7e82365d25220b8949e252eb5f28c0994cd/strftime.c#L605
165+ format ! ( "{:02}" , range( 0 , self . tm. tm_min, 59 ) )
166+ }
167+
168+ /// Returns the zero-padded second from 00-60, e.g. "07".
169+ pub fn second ( & self ) -> String {
170+ // Same as '%S' in strftime:
171+ // https://github.com/arnoldrobbins/strftime/blob/2011b7e82365d25220b8949e252eb5f28c0994cd/strftime.c#L645
172+ format ! ( "{:02}" , range( 0 , self . tm. tm_sec, 60 ) )
173+ }
149174}
150175
151- #[ cfg( feature = "testing" ) ]
152- pub fn format_datetime ( _timestamp : u32 , _timezone_offset : i32 , date_only : bool ) -> String {
153- if date_only {
154- "<date>" . into ( )
155- } else {
156- "<datetime>" . into ( )
176+ pub fn get_datetime ( timestamp : u32 ) -> Result < Tm , ( ) > {
177+ Ok ( Tm {
178+ tm : unsafe {
179+ let localtime = bitbox02_sys:: localtime ( & ( timestamp as bitbox02_sys:: time_t ) ) ;
180+ if localtime. is_null ( ) {
181+ return Err ( ( ) ) ;
182+ }
183+
184+ * localtime
185+ } ,
186+ } )
187+ }
188+
189+ /// Formats the timestamp in the local timezone.
190+ /// timestamp is the unix timestamp in seconds.
191+ /// timezone_offset is added to the timestamp, timezone part.
192+ /// date_only: if true, only the date is formatted. If false, both date and time are.
193+ pub fn format_datetime (
194+ timestamp : u32 ,
195+ timezone_offset : i32 ,
196+ date_only : bool ,
197+ ) -> Result < String , ( ) > {
198+ const MAX_EAST_UTC_OFFSET : i32 = 50400 ; // 14 hours in seconds
199+ const MAX_WEST_UTC_OFFSET : i32 = -43200 ; // 12 hours in seconds
200+
201+ if !( MAX_WEST_UTC_OFFSET ..=MAX_EAST_UTC_OFFSET ) . contains ( & timezone_offset) {
202+ return Err ( ( ) ) ;
157203 }
204+ let ts = ( ( timestamp as i64 ) + ( timezone_offset as i64 ) ) as u32 ;
205+ let tm = get_datetime ( ts) ?;
206+ Ok ( if date_only {
207+ format ! ( "{} {}" , tm. weekday( ) , tm. date( ) )
208+ } else {
209+ format ! (
210+ "{} {}\n {}:{}" ,
211+ tm. weekday( ) ,
212+ tm. date( ) ,
213+ tm. hour( ) ,
214+ tm. minute( )
215+ )
216+ } )
158217}
159218
160219#[ cfg( not( feature = "testing" ) ) ]
@@ -201,10 +260,25 @@ mod tests {
201260 use super :: * ;
202261
203262 #[ test]
204- fn test_strftime ( ) {
263+ fn test_format_datetime ( ) {
205264 assert_eq ! (
206- strftime ( 1601281809 , "%a %Y-%m-%d \n %H:%M" ) . as_str ( ) ,
207- "Mon 2020-09-28\n 08:30" ,
265+ format_datetime ( 1601281809 , 0 , true ) ,
266+ Ok ( "Mon 2020-09-28" . into ( ) )
208267 ) ;
268+ assert_eq ! (
269+ format_datetime( 1601281809 , 0 , false ) ,
270+ Ok ( "Mon 2020-09-28\n 08:30" . into( ) ) ,
271+ ) ;
272+ assert_eq ! (
273+ format_datetime( 1601281809 , 18000 , false ) ,
274+ Ok ( "Mon 2020-09-28\n 13:30" . into( ) ) ,
275+ ) ;
276+ assert_eq ! (
277+ format_datetime( 1601281809 , -32400 , false ) ,
278+ Ok ( "Sun 2020-09-27\n 23:30" . into( ) ) ,
279+ ) ;
280+
281+ assert ! ( format_datetime( 1601281809 , 50401 , false ) . is_err( ) ) ;
282+ assert ! ( format_datetime( 1601281809 , -43201 , false ) . is_err( ) ) ;
209283 }
210284}
0 commit comments