

Swift4
起動している時間の取得方法を教えていただき、本当にありがとうございます。実際にコードを書いてみると、マイナスになっていたり、とんでもない数値になっています。  
質問1.systemUptimeの数値は、スリープ時を含めているのでしょうか？電源を落としていればカウントされないのでしょうか？詳しくわからないので教えていただきたいです。  
質問2.教えたいただいたコードで計算式  
day = uptime / 86400  
hour = (uptime - (day ＊ 86400)) / 3600  
minute = (uptime - (day ＊ 86400 + hour ＊ 3600)) / 60  
second = (uptime - (day ＊ 86400 + hour ＊ 3600 + minute ＊ 60))  
としたのですが、表示されたのはマイナスがついたりとんでもなく大きい数値でした。間違えているのでしょうか。お教えください。


CC BY-SA 4.0
2018-06-28T18:29:59.680
45135
2018-07-08T12:10:37.480
2018-07-03T20:39:51.893
29104
29104
question
swift swift4
Swift4 systemUptimeメソッド


質問の内容変更に伴い修正です。

まず、`ProcessInfo().systemUptime`が返す値は、`TimeInterval`という`Duble`の別名型で、systemが起動してから現在までの秒数を`Double`型で返します。  
なので、この数字を直接操作すれば良いです。

秒数を日、時間、分、秒にするのは割り算と引き算なので省略しますが、それぞれを計算し、  
日を`day`、時間を`hour`、分を`minute`、秒を`second`という変数に代入が終わったとして、  
それぞれを個別に文字列にしたいのであれば、`String(day)`の様に、数字型を`String()`の引数にすることで可能です。  
また、決まった形にフォーマットしたいのであれば、  
`String(format:"%d日, %d時間 %d分 %d秒", day, hour, minute, second)`  
や  
`String(format:%d日, %d:%d:%d", day, hour, minute, second)`  
とすることで全体を一つの文字列にすることも可能です。

あとは、日、時、分、秒でそれぞれ別なラベルを配置して、それぞれに`.stringValue =`で代入するか、  
下のサンプルのように一つのラベルを配置して、そこに全体を一つの文字列に下文字列を`.stringValue
=`で代入すれば、ラベルの表示が代入した文字になると思います。

（LabelがIBOutletでView上のラベルとOutlet接続されているのが前提で）

[code]

    class UptimeLabelViewController: UIViewController {
        @IBOutlet weak uptimeLabel:UILabel? // UptimeLabelViewのLabelとOutlet接続する
                                            // 日、時、分を別なラベルにするならラベルとOutlet変数を必要個用意する
    
        override viewDidLoad() {
            let uptime:TimeInterval = ProcessInfo().systemUptime
              // 時間の計算方法は省略するが、uptimeを元に、day, hour, minute,
              // secondという変数に必要な値がセットされている事
            var uptimeString:String
            if (day != 0) {
                uptimeString = String(format:"%d日 %d時間 %d分 %d秒", day, hour, minute, second)
            } else {  // 0日と表示されるのは格好良くないので
                uptimeString = String(format:"%d時間 %d分 %d秒", hour, minute, second)
            } // end if day is present or not
            uptimeLabel.text = uptimeString
        } // end override view didLoad
    } // end class UptimeLabelView
    
[/code]

こんな感じでいかがでしょうか。


CC BY-SA 4.0
2018-06-29T06:03:38.557
45144
2018-07-02T12:07:29.177
2018-07-02T12:07:29.177
14745
14745
45135
answer


更新後のご質問に掲載された以下のようなコード

>
[code]

>     day = uptime / 86400
>     hour = (uptime - (day ＊ 86400)) / 3600
>     minute = (uptime - (day ＊ 86400 + hour ＊ 3600)) / 60
>     second = (uptime - (day ＊ 86400 + hour ＊ 3600 + minute ＊ 60))
>  
[/code]

(一部全角文字が入ってますね。Xcodeからコピペするなどして、できるだけ実際に使ったコードを掲載していただかないと、思わぬ誤解を招くことになります。)

`uptime`は`systemUptime`の値をそのまま保持しているとすると`Double`型なんで、そもそも日数が小数点付きになってしまいませんか?

秒数を日・時・分・秒に変換するような処理では、割り算(商)と余りを求める演算(剰余)を併用するのが常道ですが、これが浮動小数展(`Double`型)のままだとすこぶる面倒になります。

[code]

    import UIKit
    
    class ViewController: UIViewController {
    
        @IBOutlet weak var label: UILabel!
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            //初回表示、表示を更新し続けたい場合にはTimerなどを使うこと
            showSystemUptime()
        }
    
        func showSystemUptime() {
            //ProcessInfo.processInfo と書くと毎回新しいインスタンスを作らず、共用のインスタンスを取得できる
            let uptime = ProcessInfo.processInfo.systemUptime
            print(uptime) //->274694.647071527
            //秒を86400で割って小数点以下を切り捨てて整数値にしたのが日数
            let days = Int(floor(uptime/86400))
            //同じ割り算の余りが1日の中の秒数、整数除算に近いのはremainder(dividingBy:)ではなくtruncatingRemainder(dividingBy:)
            var rest = uptime.truncatingRemainder(dividingBy: 86400)
            //同様に時間(整数値)と1時間内の残り秒数を計算
            let hours = Int(floor(rest/3600))
            rest = rest.truncatingRemainder(dividingBy: 3600)
            //同じく分(整数値)と1分内の残り秒数を計算
            let minutes = Int(floor(rest/60))
            let seconds = rest.truncatingRemainder(dividingBy: 60)
            //時分秒は整数部2桁表示が良さそうなので、String.init(format:)を使う
            let uptimeStr = String(format: "%d days %02d:%02d:%06.3f", days, hours, minutes, seconds)
            print(uptimeStr) //->3 days 04:18:14.647
            label.text = uptimeStr
        }
    
    }
    
[/code]

精度もよくわからない`systemUptime`を秒以下まで表示しても仕方ないでしょうから、最初に秒単位に四捨五入してしまった方が良いでしょう。(もうそのようにされているかもしれませんが、あなたの質問文ではどうなっているのかさっぱりわかりません。)

最初に整数型に変換するなら、`showSystemUptime()`メソッドはこんな感じになります。

[code]

    func showSystemUptime() {
        let uptime = Int(ProcessInfo.processInfo.systemUptime)
        print(uptime) //->275469
        //秒を86400で割ったのが日数、整数型だと切り捨てなど考える必要がない
        let days = uptime / 86400
        //同じ割り算の余りが1日の中の秒数、整数剰余は%演算だけで済む
        var rest = uptime % 86400
        //同様に時間と1時間内の残り秒数を計算
        let hours = rest / 3600
        rest = rest % 3600
        //同じく分と1分内の残り秒数を計算
        let minutes = rest / 60
        let seconds = rest % 60
        //時分秒は整数部2桁表示が良さそうなので、String.init(format:)を使う
        let uptimeStr = String(format: "%d days %02d:%02d:%02d", days, hours, minutes, seconds)
        print(uptimeStr) //->3 days 04:31:09
        label.text = uptimeStr
    }
    
[/code]

* * *

余談になりますが、ご質問を書かれる際には根本的な目的も含めて何をしたいのか具体的に誤解のないように書くこと、コードなどを示されてうまくいかないと言う場合には文脈(`uptime`は何型でどこからとってきた値なのか)やうまくいかないと言う具体的な結果、それに自分が「こうなってほしい」と考えている内容を含めるようにしてください。

出来るだけ適切な質問文を書くことで、回答者に余分な時間を使わせることもなくなりますし、あなた自身もより早く的確な回答を得られることにつながります。何が「適切な質問文」かは慣れも必要でしょうが、2〜3行しかなかったら、誤解を招くかもしれないと思って見直していただいた方が良いでしょう。

* * *

自分ではあまり使わないのですっかり忘れていたのですが、本家StackOverflowで[類似の質問](https://stackoverflow.com/a/51231526/6541007)がでてました。`DateComponentsFormatter`ですね。

[code]

    func showSystemUptime() {
        let uptime = ProcessInfo.processInfo.systemUptime
        print(uptime) //->611924.107341427
        let formatter = DateComponentsFormatter()
        formatter.allowedUnits = [.day, .hour, .minute, .second]
        let uptimeStr = formatter.string(from: uptime)!
        print(uptimeStr) //->7d 1:58:44
        label.text = uptimeStr
    }
    
[/code]

書式を文字列で指定できる`DateFormatter`と違って、あれこれのパラメータを限られた選択肢の中から選ぶ必要があるので、細かい書式設定はできないようです。その代わり時・分・秒といった単位はユーザの設定に合わせてローカライズしてくれる機能があるようですが。

* * *

質問1についてですが、[Appleのドキュメント](https://developer.apple.com/documentation/foundation/processinfo/1414553-systemuptime)はあまり役に立つことが書いてないんですが、定義は「前回OSを起動してからの経過時間」です。ただしシステムがスリープしている間はカウントアップされていないと言う報告があります。より実際の経過時間に近い値を取れないかと言う試みはいくつか見つかりますが、`systemUptime`を経過時間として表示したい、と言う内容からはかけ離れてくるので、興味があれば別質問としていただいた方が良いでしょう。


CC BY-SA 4.0
2018-07-03T23:00:46.760
45288
2018-07-08T12:10:37.480
2018-07-08T12:10:37.480
13972
13972
45135
answer