I have an app that shows certain states of hardware. I know the time when the hardware entered said state (failed, active, idle, unconnected, etc). I've been experimenting with showing the state with an "ago" time. But I've struggled with how to adjust the resolution.
Under an hour is easy, just show "minutes ago". As you move into hours ago though, the minutes become less and less important. I'm not sure that suddenly jumping to "1 hour ago" at 61 minutes is the right way though. And when to switch to "2 hours ago". Do you round, or truncated that computation. The same dilemma exists at the 24 hour point. Do you show 25 hours ago, or just say 1 day ago. Or should there be a period where I show "1 hour and 13 minutes ago" and then at some point drop the minutes.
I know these "ago" labels aren't original. I'm curious how others have implemented this.
Moment.js is a very popular library used in web development that can output time from now just as you are asking. Below is a listing of how they break down their intervals into a more digestible format.
From their documentation:
0 to 45 seconds seconds ago
45 to 90 seconds a minute ago
90 seconds to 45 minutes 2 minutes ago ... 45 minutes ago
45 to 90 minutes an hour ago
90 minutes to 22 hours 2 hours ago ... 22 hours ago
22 to 36 hours a day ago
36 hours to 25 days 2 days ago ... 25 days ago
25 to 45 days a month ago
45 to 345 days 2 months ago ... 11 months ago
345 to 547 days (1.5 years) a year ago
548 days+ 2 years ago ... 20 years ago
As a sumplement to #JimmyBoh 's answer, I thought I'd add the ObjectiveC code I used:
#import "NSDate+AgoPrint.h"
#define SECONDS(s) (s)
#define MINUTES(m) ((m) * 60)
#define HOURS(h) ((h) * 3600)
#define DAYS(d) ((d) * 3600 * 24)
#define YEARS(y) ((y) * 3600 * 24 * 365)
#implementation NSDate (AgoPrint)
- (NSString*) simpleAgoString {
NSTimeInterval ago = -self.timeIntervalSinceNow;
if (ago <= SECONDS(45)) { // 0 to 45 seconds seconds ago
return [NSString stringWithFormat: #"%u seconds ago", (unsigned int)round(ago)];
}
else if (ago <= SECONDS(90)) { //45 to 90 seconds a minute ago
return #"a minute ago";
}
else if (ago <= MINUTES(45)) { //90 seconds to 45 minutes 2 minutes ago ... 45 minutes ago
return [NSString stringWithFormat: #"%u minutes ago", (unsigned int)round(ago / MINUTES(1))];
}
else if (ago <= MINUTES(90)) { // 45 to 90 minutes an hour ago
return #"an hour ago";
}
else if (ago <= HOURS(22)) { // 90 minutes to 22 hours 2 hours ago ... 22 hours ago
return [NSString stringWithFormat: #"%u hours ago", (unsigned int)round(ago / HOURS(1))];
}
else if (ago <= HOURS(36)) { // 22 to 36 hours a day ago
return #"a day ago";
}
else if (ago <= DAYS(25)) { // 36 hours to 25 days 2 days ago ... 25 days ago
return [NSString stringWithFormat: #"%u days ago", (unsigned int)round(ago / DAYS(1))];
}
else if (ago <= DAYS(45)) { // 25 to 45 days a month ago
return #"a month ago";
}
else if (ago <= DAYS(345)) { // 45 to 345 days 2 months ago ... 11 months ago
return [NSString stringWithFormat: #"%u months ago", (unsigned int)round(ago / DAYS(30))];
}
else if (ago < DAYS(547)) { // 345 to 547 days (1.5 years) a year ago
return #"a year ago";
}
else { // 548 days+ 2 years ago ... 20 years ago
return [NSString stringWithFormat: #"%u years ago", (unsigned int)round(ago / YEARS(1))];
}
}
#end
I may end up adding weeks or replacing months or weeks, but the general approach seems pretty clear how I would slot that in.
Related
I am trying to run two threads in parallel. But unfortunately , it sometime works sometime not. Here is my code
let firstQueue = DispatchQueue(label: "queue1", qos: DispatchQoS.userInitiated)
let secondQueue = DispatchQueue(label: "queue2", qos: DispatchQoS.userInitiated)
//let firstQueue = DispatchQueue(label: "queue1", qos: DispatchQoS.default , attributes: .concurrent)
firstQueue.async {
for i in 0..<10 {
print("🔷", i)
}
}
secondQueue.async {
for i in 20..<30 {
print("⚪️", i)
}
}
I have tried different everything but not achieve perfect parallelism.
First Time Output: 0
⚪️ 20
🔷 1
⚪️ 21
🔷 2
⚪️ 22
🔷 3
⚪️ 23
🔷 4
⚪️ 24
🔷 5
⚪️ 25
🔷 6
⚪️ 26
🔷 7
⚪️ 27
🔷 8
⚪️ 28
🔷 9
⚪️ 29
SecondTime Output :
🔷 0
🔷 1
🔷 2
🔷 3
🔷 4
🔷 5
🔷 6
🔷 7
🔷 8
🔷 9
⚪️ 20
⚪️ 21
⚪️ 22
⚪️ 23
⚪️ 24
⚪️ 25
⚪️ 26
⚪️ 27
⚪️ 28
⚪️ 29
The point of multithreading is that operations will be done parallel and independent from one another. That does not guaranty any order at all, there is no synchronization, no nothing. If you need to have things synchronized then you will need to use a single thread or use a third thread that dispatches work:
func foo() {
let firstQueue = DispatchQueue(label: "queue1")
let secondQueue = DispatchQueue(label: "queue2")
let mainQueue = DispatchQueue(label: "queueMain")
var firstQueueOperationsCount: Int = 10
var secondQueueOperationsCount: Int = 10
func performOperations() {
guard max(firstQueueOperationsCount, secondQueueOperationsCount) > 0 else {
return
}
mainQueue.async {
if firstQueueOperationsCount > secondQueueOperationsCount {
firstQueueOperationsCount -= 1
firstQueue.async {
print("🔷")
performOperations()
}
} else {
secondQueueOperationsCount -= 1
secondQueue.async {
print("⚪️")
performOperations()
}
}
}
}
performOperations()
}
There are many ways on how to serialize tasks dispatched on multiple threads and this being just one of them. You might try searching/reading toward that direction but just be clear: What you seem to expect from multithreading is NOT WHAT MULTITHREADING IS. It does not work this way and it does not supposed to work this way. How they do work is how we want it to work.
If you have a more specific situation I am sure community here may help you identify a good approach to solve it. But from what you have written the result is not multithreading but:
for i in 0..<10 {
print("🔷")
print("⚪️")
}
It might be dispatched on another thread but that really makes no difference.
Your code works well, but the thing you should know is that, you haven't any choice to decide which part of your code execute first, and since your code is just printing a character and its a light job to do for cpu you see some times your characters printing ordered and some times unordered. Try to give some more heavy job to cpu and then see the results!
You can use DispatchGroup, also you should read more about GCD, the code below work as expected and it will fix your issue
let dispatchGroup = DispatchGroup()
let firstQueue = DispatchQueue(label: "queue1", qos: DispatchQoS.userInitiated)
let secondQueue = DispatchQueue(label: "queue2", qos: DispatchQoS.userInitiated)
firstQueue.async(group: dispatchGroup) {
for i in 0..<10 {
print("🔷", i)
}
dispatchGroup.leave()
}
secondQueue.async(group: dispatchGroup) {
dispatchGroup.wait()
for i in 20..<30 {
print("⚪️", i)
}
}
the output always will be like this 🔷 0
🔷 1
🔷 2
🔷 3
🔷 4
🔷 5
🔷 6
🔷 7
🔷 8
🔷 9
⚪️ 20
⚪️ 21
⚪️ 22
⚪️ 23
⚪️ 24
⚪️ 25
⚪️ 26
⚪️ 27
⚪️ 28
⚪️ 29
I have this information:
Days Dec'15 Jan'16
---------------------
Sun 27
Mon 28
Tue 29
Wed 30
Thu 31
Fri 1
Sat 2
I have 1st Jan'16. So I have to get Fri and then the difference of days from the Sun. So, in this case, the difference should be 5. Because, before Friday there are 5 other days. So if I want to know it for 2nd Jan'16 it should be 6. And likewise.
How do I get it easy with date functions?
The following code may help you
extension Date {
func weekdayDiffence() -> Int {
return Calendar.current.dateComponents([Calendar.Component.weekday], from: self).weekday ?? 0
}
}
Example
let d = Date().weekdayDiffence()
print(d)
This question already has answers here:
Getting date from [NSDate date] off by a few hours
(3 answers)
Closed 6 years ago.
I saved a date in a sqlite database. Know I try to get the hours and the minutes. But the hours are shifted by 2.
print(calendar.timeZone)
while result.next() {
var hour = 0
var minute = 0
let calendar = NSCalendar.currentCalendar()
if #available(iOS 8.0, *) {
calendar.getHour(&hour, minute: &minute, second: nil, nanosecond: nil, fromDate: result.dateForColumn("time"))
print(result.dateForColumn("time"))
print("the hour is \(hour) and minute is \(minute)")
}
}
I get the following output:
Europe/Berlin (GMT+2) offset 7200 (Daylight)
2016-08-17 18:44:57 +0000
the hour is 20 and minute is 44
2016-08-18 18:44:57 +0000
the hour is 20 and minute is 44
2016-08-19 15:44:57 +0000
the hour is 17 and minute is 44
2016-08-18 16:44:57 +0000
the hour is 18 and minute is 44
2016-08-17 18:44:57 +0000
the hour is 20 and minute is 44
2016-08-18 18:44:57 +0000
the hour is 20 and minute is 44
2016-08-19 15:44:57 +0000
the hour is 17 and minute is 44
2016-08-18 16:44:57 +0000
the hour is 18 and minute is 44
The timezone is correct. I tryed two other solutions. But it is always the same problem.
The result.dateForColumn("time") is in UTC since you have +0000 whereas the second output is in another timezone (Europe/Berlin (GMT+2)), so the date is the same.
I've implemented a UIProgressView as a count down timer, its value is decreased from 1.0 to 0.0 in 1.5 seconds.
It already worked but the problem is the time it took was longer than 1.5
seconds, it was about 2.0 - 2.5 seconds until the progress view value reach 0.0
For it to run in just 1.5, I decreased the value of the progress by 0.001 each time it's called. The decrease method is call after 0.0015 second interval.
Below is how I do it. I don't know if there's anything wrong that make it run longer than just 1.5 seconds?
- (void)decreaseProgress {
if (currentProgress > 0.0000f) {
currentProgress -= 0.001f;
[_timeLineProgress setProgress:currentProgress animated:YES];
[self performSelector:#selector(decreaseProgress) withObject:self afterDelay:0.0015 inModes:#[NSDefaultRunLoopMode]];
} else {
[self stopTimer];
}
}
To animate the progress, try this code in your decreaseProgress method
[UIView animateWithDuration:1.5 animations:^{
[_timeLineProgress setProgress:0.0 animated:YES];
}];
You're attempting to update the progress view 1000 times in 1.5 seconds. That's way too fast, since the screen only updates 60 times per second. In other words, you're updating the progress bar more than 10 times between each time that the progress bar is actually redrawn on the screen.
Instead I would suggest 15 updates at 0.1 second intervals, and change the progress bar by 1/15 each time.
One way to check how well the code is performing is to use the CACurrentMediaTime function to get timestamps. Here's some sample code that demonstrates how to do that. The progressStart variable is the timestamp when the button press event occurred, and the NSLog prints the amount of time elapsed relative to the start time.
An important feature of the code is that the performSelector method is called as early as possible in the updateProgress method, to minimize slippage.
#interface ViewController ()
{
CFTimeInterval progressStart;
int progressCount;
}
#property (weak, nonatomic) IBOutlet UIProgressView *progressView;
#end
- (void)updateProgress
{
if ( progressCount > 0 )
[self performSelector:#selector(updateProgress) withObject:nil afterDelay:0.1];
self.progressView.progress = progressCount / 15.0;
NSLog( #"%2d %.3lf", progressCount, CACurrentMediaTime() - progressStart );
progressCount--;
}
- (IBAction)someButtonPressed
{
self.progressView.progress = 1.0;
progressStart = CACurrentMediaTime();
progressCount = 15;
[self updateProgress];
}
And here are the results from a typical run
2015-07-01 13:05:57.610 Progress[8354:907] 15 0.000
2015-07-01 13:05:57.711 Progress[8354:907] 14 0.101
2015-07-01 13:05:57.813 Progress[8354:907] 13 0.203
2015-07-01 13:05:57.914 Progress[8354:907] 12 0.304
2015-07-01 13:05:58.015 Progress[8354:907] 11 0.405
2015-07-01 13:05:58.116 Progress[8354:907] 10 0.506
2015-07-01 13:05:58.218 Progress[8354:907] 9 0.608
2015-07-01 13:05:58.319 Progress[8354:907] 8 0.709
2015-07-01 13:05:58.420 Progress[8354:907] 7 0.810
2015-07-01 13:05:58.520 Progress[8354:907] 6 0.910
2015-07-01 13:05:58.621 Progress[8354:907] 5 1.011
2015-07-01 13:05:58.722 Progress[8354:907] 4 1.112
2015-07-01 13:05:58.823 Progress[8354:907] 3 1.213
2015-07-01 13:05:58.924 Progress[8354:907] 2 1.314
2015-07-01 13:05:59.024 Progress[8354:907] 1 1.415
2015-07-01 13:05:59.125 Progress[8354:907] 0 1.515
Note that the performSelector:afterDelay method has about 1 millisecond of slippage on each event. The total slippage was 15 milliseconds. The device screen update rate is 60 frames/sec, which is 16.7 msec/frame. So the total slippage was less than one frame time, and won't be noticeable to the user.
As rmaddy pointed out in the comments, using an NSTimer allows you to avoid most of the slippage. However, the last timer event could still slip by an arbitrary amount of time.
I have been working on this program for hours and cannot find out how to make the numbers loop around after they hit saturday. They either go way passed it to the right or if i add and endl; they go up and down.
// This is how my output looks like (except they curve around they just go forever to the right:
Number of days: 31
Offset: 0
Su Mo Tu We Th Fr Sa
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
This what you mean?
#include <iostream>
using namespace std;
int main()
{
int i;
for (i=1; i<=31; i++) {
cout << ((i<10) ? " " : "") << i << " ";
if (i%7==0) cout << endl;
}
return 0;
}
Outputs:
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
The % sign is the modulus operator. It gives the remainder of division. So every 7th day divided by 7 is going to have a remainder of zero. That's how you check where to put the line breaks.