Friends,
I have an array containing checkins and checkouts times and need to calculate the spent time between these operations. For that I iterate through this table partitioning checkins from checkouts in two separated arrays, like that:
var checkins = [];
var checkouts = [];
var results = [];
var index;
for (index = 0; index < $scope.data.registries.length; ++index){
if ($scope.data.registries[index].rType == 0) {
checkins.push(moment($scope.data.registries[index].savedHour, "HH:mm:ss"));
}
else {
checkouts.push(moment($scope.data.registries[index].savedHour, "HH:mm:ss"));
}
}
After that I just iterate over checkins array, calculating the diffs and pushing it into the results array, like this:
for (index = 0; index < checkins.length; ++index){
if (index <= checkouts.length) {
results.push(moment.utc(checkouts[index]).diff(moment(checkins[index])));
}
}
So, now I have an array containing only the diff times, for each pair of checkin checkout. Now I just
make a sum of theses diffs... like this:
var total = null;
for (index = 0; index < results.length; ++index) {
if (index == 0){
total = moment(results[index]);
}
else{
total.add(results[index]);
}
}
if (results.length == 0) {
return "00 hour(s) and 00 minute(s)";
}
else {
return moment.utc(total).format("HH [ hour(s) and ] mm [ minute(s)]");
}
I'm not getting the correct amount of time.... for the following sample data:
checkin 07:32
checkout 07:34 ->
difference: 2 minutes
checkin 08:20
checkout 08:53 ->
difference: 33 minutes
I should have a total of 35 minutes, but
its always changing according to current time... for example, here now is 10:51 (am)
and this function is returning 2h and 37m
I dont see what is wrong... could someone point it out?
A few things:
You're subtracting UTC against local time:
moment.utc(checkouts[index]).diff(moment(checkins[index]))
^^^^^^^^^^ ^^^^^^
Either both values should be UTC, or both values should be local.
You said your inputs were time-only values. Keep in mind that when you don't specify a date, the current date is assumed. If you are working with local times on a day that has a daylight saving time transition, you may get results that are adjusted by up to an hour. If you intend to work with the current day and want accurate elapsed times, then this should be expected. Otherwise, you'll want to fix the time to a particular date.
You aren't considering time ranges that cross midnight, for example 22:00 to 02:00 should be 4 hours (on most days). You should check if the values are in sequence, and add a day to the checkout value if not.
I did a short example in my Chrome console and got the correct answer with the following:
var start = moment('07:32', "HH:mm:ss")
var stop = moment('07:34', "HH:mm:ss")
var diff1 = moment.utc(stop).diff(moment(start))
start = moment("08:03", "HH:mm:ss")
stop = moment("08:33", "HH:mm:ss")
var diff2 = moment.utc(stop).diff(moment(start))
var total = moment(diff1)
total.add(diff2)
moment.utc(total).format("HH [ hour(s) and ] mm [ minute(s)]");
"00 hour(s) and 32 minute(s)"
So looks like your logic is correct without the arrays and for loops.
Friends... logic problems in my loops hehehheh
I was using
for (index = 0; index < checkins.length; ++index){
if (index <= checkouts.length) {
results.push(moment.utc(checkouts[index]).diff(moment(checkins[index])));
}
}
the correct is
for (index = 0; index < checkins.length; ++index){
if (index <= checkouts.length-1) {
results.push(moment.utc(checkouts[index]).diff(moment(checkins[index])));
}
}
Related
TimeOfDay documentation has no comparison operator and primitive comparison does not work. My only solution that I can thinking of right now is to convert TimeOfDay to DateTime and use DateTime's difference method.
Does anyone have a better solution?
Convert it to a double then compare.
double toDouble(TimeOfDay myTime) => myTime.hour + myTime.minute/60.0
extension TimeOfDayExtension on TimeOfDay {
int compareTo(TimeOfDay other) {
if (hour < other.hour) return -1;
if (hour > other.hour) return 1;
if (minute < other.minute) return -1;
if (minute > other.minute) return 1;
return 0;
}
}
Thanks from #Lucas idea, you can calculate hour and minute by
TimeOfDay yourTime ;
TimOfDay nowTime = TimeOfDay.now()
double _doubleYourTime = yourTime.hour.toDouble() +
(yourTime.minute.toDouble() / 60);
double _doubleNowTime = nowTime.hour.toDouble() +
(nowTime.minute.toDouble() / 60);
double _timeDiff = _doubleYourTime - _doubleNowTime;
double _hr = _timeDiff.truncate();
double _minute = (_timeDiff - _timeDiff.truncate()) * 60;
print('Here your Happy $_hr Hour and also $_minute min');
I calculated the difference by turning both values into minute-counts, and comparing those :)
TimeOfDay now = TimeOfDay.now();
int nowInMinutes = now.hour * 60 + now.minute;
TimeOfDay testDate = TimeOfDay(hour: 2, minute: 20);
int testDateInMinutes = testDate.hour * 60 + testDate.minute;
You can use this method. Where you have to provide starttime and endTime in TimesofDay format.
getTime(startTime, endTime) {
bool result = false;
int startTimeInt = (startTime.hour * 60 + startTime.minute) * 60;
int EndTimeInt = (endTime.hour * 60 + endTime.minute) * 60;
int dif = EndTimeInt - startTimeInt;
if (EndTimeInt > startTimeInt) {
result = true;
} else {
result = false;
}
return result;
}
LikeThis
getTime(v1, v2);
TimeOfDay n = TimeOfDay.now();
int nowSec = (n.hour * 60 + n.minute) * 60;
int veiSec = (t.hour * 60 + t.minute) * 60;
int dif = veiSec - nowSec;
Card(
child: ListTile(
onTap: () {
showTimePicker(
context: context,
initialTime: TimeOfDay.now(),
).then((TimeOfDay time) {
double _doubleyourTime =
time.hour.toDouble() + (time.minute.toDouble() /60);
double _doubleNowTime = TimeOfDay.now().hour.toDouble() +
(TimeOfDay.now().minute.toDouble() / 60);`enter code here`
if (_doubleyourTime > _doubleNowTime) {
print('correct format')
});
} else {
print('Sorry You can not set the time')
}
});
},
//dense: true,
leading: Icon(Icons.timer),
title: Text(
'Today On Time',`enter code here`
),
),
),
We can actually use the subtract operator.
Code to make magic happen :
Here I wanted to get the difference in time after the user selects the time (in TimeOfDay format) using showTimePicker()
// current time will be used to find the difference between the time selected by the user.
TimeOfDay _cur_time = TimeOfDay(hour: DateTime.now().hour, minute: DateTime.now().minute);
// scheduled time will be updated as soon as the user inputs a new time using the showTimePicker() function available in Flutter Material librabry.
TimeOfDay _scheduled_time = TimeOfDay(hour: DateTime.now().hour, minute: DateTime.now().minute);
// toDouble Function to convert time to double so that we can compare time and check that the time selected is greater than current time.
double toDouble(TimeOfDay myTime) => myTime.hour + myTime.minute / 60.0;
void _selectTime() async {
Flutter Material widget to select time.
final TimeOfDay? newTime = await showTimePicker(
context: context,
initialTime: _scheduled_time,
);
//Check if the selected time is greater than the cur time
if (toDouble(newTime!) > toDouble(_cur_time)) {
setState(() {
_scheduled_time = newTime;
});
}
}
Function to get the difference between cur time and selected time.
Duration _getDelayedDuration(){
var hourDelay = _scheduled_time.hour - _cur_time.hour;
print(hourDelay);
var minuteDelay = _scheduled_time.minute - _cur_time.minute;
print(minuteDelay);
return Duration(hours: hourDelay, minutes: minuteDelay);
}
Solution for negative duration calculations
All these answers are pretty good but they didn't help me when I had a user select a range of times in my app. In order to calculate the total duration of the specified time period, I tried all the solutions which work pretty well but fail in certain scenarios. The scenarios are:
When the start time comes after the end time (i.e- when the duration is supposed to be over 12 hours)
When the start time is before 12am at the night and end time is after that
And the code to overcome it is:
String durationFromTimeOfDay(TimeOfDay? start, TimeOfDay? end) {
if (start == null || end == null) return '';
// DateTime(year, month, day, hour, minute)
final startDT = DateTime(9, 9, 9, start.hour, start.minute);
final endDT = DateTime(9, 9, 10, end.hour, end.minute);
final range = DateTimeRange(start: startDT, end: endDT);
final hours = range.duration.inHours % 24;
final minutes = range.duration.inMinutes % 60;
final _onlyHours = minutes == 0;
final _onlyMinutes = hours == 0;
final hourText = _onlyMinutes
? ''
: '$hours${_onlyHours ? hours > 1 ? ' hours' : ' hour' : 'h'}';
final minutesText = _onlyHours
? ''
: '$minutes${_onlyMinutes ? minutes > 1 ? ' mins' : ' min' : 'm'}';
return hourText + minutesText;
}
It is important to note that you need to prefill the DateTime for end TimeOfDay with a day value which is greater than the same in start DateTime. The other parameters (for year and month) can be anything you want.
This outputs a really nicely formatted string that is short, concise, and extremely legible
This, however, doesn't satisfy the requirement that the solution is devoid of conversion to DateTime. But at least it uses a different approach over the difference method. And this makes the correct duration calculation more reliable in a few lines of code comparatively.
I don't think this is possible. You can use .subtract in DateTime as also .difference
The following function returns the difference between two date time values in words (as a string). Can it be written more efficiently/elegantly?
/**
* #hint Returns the difference between two time strings in words.
*/
public string function timeAgoInWords(required date fromTime, date toTime=now())
{
local.secondDiff = dateDiff("s", arguments.fromTime, arguments.toTime);
if (local.secondDiff <= 60)
return "#local.secondDiff# seconds ago";
local.minuteDiff = dateDiff("n", arguments.fromTime, arguments.toTime);
if (local.minuteDiff <= 60)
if (local.minuteDiff < 2)
return "1 minute ago";
else return "#local.minuteDiff# minutes ago";
if (local.minuteDiff <= 1440)
if (local.minuteDiff <= 120)
return "1 hour ago";
else return "#int(local.minuteDiff/60)# hours ago";
if (local.minuteDiff <= 2880)
return "yesterday";
if (local.minuteDiff <= 4320)
return "2 days ago";
local.monthDiff = dateDiff("m", arguments.fromTime, arguments.toTime);
if (local.monthDiff <= 12)
return "#dateFormat(arguments.fromTime, "mmm dd")# at #timeFormat(arguments.fromTime, "h:mm")#";
return "#dateFormat(arguments.fromTime, "mmm dd 'yy")# at #timeFormat(arguments.fromTime, "h:mm")#";
}
This is what I wrote a few months ago, based on the UDF Al Everett posted above in the comment and written in CF9 script style. It won't be more efficient. In fact, it should be slower then your implementation 'cause it has multiple calls to dateDiff(), and needs to set up 2 arrays up front, but the overall line count is shorter and easily understandable.
string function ago(required Date dateThen)
{
var dateparts = ["yyyy","m","d","h","n"];
var datepartNames = ["year","month","day","hour","minute"];
var rightNow = Now();
for (var i = 1; i <= 5; i++) // 5 == arrayLen(dateparts)
{
var diff = dateDiff(variables.dateparts[i], dateThen, rightNow);
if (diff > 1)
return "#diff# #datepartNames[i]#s ago";
if (diff == 1)
return "#diff# #datepartNames[i]# ago";
}
return "Just Now";
}
It looks good to me. You could instead use your first diff (local.secondDiff) for all your tests rather than re-diffing, but this is probably easier to read.
I need a method which would give me the number of hours and minutes from any random number.For example if the random number is 500 i would like to have information as 5 hrs and 0 minutes.Another example is 2359 is 23 hrs 59 minutes.The random number is entered by the user and i am only concerned with the hours and minutes(not seconds).I need not even worry about rounding off of minutes .So i wrote this method ,which to me is not efficient.Can any one suggest a better way of doing it ?or is this good enough?
private void calculateDateTime(int someNumber){
if(someNumber<=0){
return;
}
String number = Integer.toString(someNumber);
String hrs ="";
String mins ="00";
if(number.length()>4){
hrs =number.substring(0, 2);
mins = number.substring(2,4);
}
else{
float f =((float)someNumber)/100;
String s = Float.toString(f);
String [] splitArray = s.split("\\.");
if(splitArray.length>1) {
hrs = splitArray[0];
mins = splitArray[1];
}
}
int hr = Integer.valueOf(hrs);
int min = Integer.valueOf(mins);
if(hr>=24||min>=60){
return;
}
Thats how i am getting the hr and mins respectively.Suggest me a better approach if you have one.
Thanks
Pad the input string with zeros until length is 4 and then spilt the string in the middle?
Maybe:
int hours = someNumber / 100;
int minutes = someNumber % 100;
if(hours >= 24 || minutes >= 60) {
//Do whatever
}
If you want your values to be repeatable, I would use the passed in value as the seed into a Random generator.
Random r = new Random(someNumber);
int hr = r.nextInt(24);
int min = r.nextInt(60);
Project Euler problem 14:
The following iterative sequence is
defined for the set of positive
integers:
n → n/2 (n is even) n → 3n + 1 (n is
odd)
Using the rule above and starting with
13, we generate the following
sequence: 13 → 40 → 20 → 10 → 5 → 16 →
8 → 4 → 2 → 1
It can be seen that this sequence
(starting at 13 and finishing at 1)
contains 10 terms. Although it has not
been proved yet (Collatz Problem), it
is thought that all starting numbers
finish at 1.
Which starting number, under one
million, produces the longest chain?
My first instinct is to create a function to calculate the chains, and run it with every number between 1 and 1 million. Obviously, that takes a long time. Way longer than solving this should take, according to Project Euler's "About" page. I've found several problems on Project Euler that involve large groups of numbers that a program running for hours didn't finish. Clearly, I'm doing something wrong.
How can I handle large groups of numbers quickly?
What am I missing here?
Have a read about memoization. The key insight is that if you've got a sequence starting A that has length 1001, and then you get a sequence B that produces an A, you don't to repeat all that work again.
This is the code in Mathematica, using memoization and recursion. Just four lines :)
f[x_] := f[x] = If[x == 1, 1, 1 + f[If[EvenQ[x], x/2, (3 x + 1)]]];
Block[{$RecursionLimit = 1000, a = 0, j},
Do[If[a < f[i], a = f[i]; j = i], {i, Reverse#Range#10^6}];
Print#a; Print[j];
]
Output .... chain length´525´ and the number is ... ohhhh ... font too small ! :)
BTW, here you can see a plot of the frequency for each chain length
Starting with 1,000,000, generate the chain. Keep track of each number that was generated in the chain, as you know for sure that their chain is smaller than the chain for the starting number. Once you reach 1, store the starting number along with its chain length. Take the next biggest number that has not being generated before, and repeat the process.
This will give you the list of numbers and chain length. Take the greatest chain length, and that's your answer.
I'll make some code to clarify.
public static long nextInChain(long n) {
if (n==1) return 1;
if (n%2==0) {
return n/2;
} else {
return (3 * n) + 1;
}
}
public static void main(String[] args) {
long iniTime=System.currentTimeMillis();
HashSet<Long> numbers=new HashSet<Long>();
HashMap<Long,Long> lenghts=new HashMap<Long, Long>();
long currentTry=1000000l;
int i=0;
do {
doTry(currentTry,numbers, lenghts);
currentTry=findNext(currentTry,numbers);
i++;
} while (currentTry!=0);
Set<Long> longs = lenghts.keySet();
long max=0;
long key=0;
for (Long aLong : longs) {
if (max < lenghts.get(aLong)) {
key = aLong;
max = lenghts.get(aLong);
}
}
System.out.println("number = " + key);
System.out.println("chain lenght = " + max);
System.out.println("Elapsed = " + ((System.currentTimeMillis()-iniTime)/1000));
}
private static long findNext(long currentTry, HashSet<Long> numbers) {
for(currentTry=currentTry-1;currentTry>=0;currentTry--) {
if (!numbers.contains(currentTry)) return currentTry;
}
return 0;
}
private static void doTry(Long tryNumber,HashSet<Long> numbers, HashMap<Long, Long> lenghts) {
long i=1;
long n=tryNumber;
do {
numbers.add(n);
n=nextInChain(n);
i++;
} while (n!=1);
lenghts.put(tryNumber,i);
}
Suppose you have a function CalcDistance(i) that calculates the "distance" to 1. For instance, CalcDistance(1) == 0 and CalcDistance(13) == 9. Here is a naive recursive implementation of this function (in C#):
public static int CalcDistance(long i)
{
if (i == 1)
return 0;
return (i % 2 == 0) ? CalcDistance(i / 2) + 1 : CalcDistance(3 * i + 1) + 1;
}
The problem is that this function has to calculate the distance of many numbers over and over again. You can make it a little bit smarter (and a lot faster) by giving it a memory. For instance, lets create a static array that can store the distance for the first million numbers:
static int[] list = new int[1000000];
We prefill each value in the list with -1 to indicate that the value for that position is not yet calculated. After this, we can optimize the CalcDistance() function:
public static int CalcDistance(long i)
{
if (i == 1)
return 0;
if (i >= 1000000)
return (i % 2 == 0) ? CalcDistance(i / 2) + 1 : CalcDistance(3 * i + 1) + 1;
if (list[i] == -1)
list[i] = (i % 2 == 0) ? CalcDistance(i / 2) + 1: CalcDistance(3 * i + 1) + 1;
return list[i];
}
If i >= 1000000, then we cannot use our list, so we must always calculate it. If i < 1000000, then we check if the value is in the list. If not, we calculate it first and store it in the list. Otherwise we just return the value from the list. With this code, it took about ~120ms to process all million numbers.
This is a very simple example of memoization. I use a simple list to store intermediate values in this example. You can use more advanced data structures like hashtables, vectors or graphs when appropriate.
Minimize how many levels deep your loops are, and use an efficient data structure such as IList or IDictionary, that can auto-resize itself when it needs to expand. If you use plain arrays they need to be copied to larger arrays as they expand - not nearly as efficient.
This variant doesn't use an HashMap but tries only to not repeat the first 1000000 numbers. I don't use an hashmap because the biggest number found is around 56 billions, and an hash map could crash.
I have already done some premature optimization. Instead of / I use >>, instead of % I use &. Instead of * I use some +.
void Main()
{
var elements = new bool[1000000];
int longestStart = -1;
int longestRun = -1;
long biggest = 0;
for (int i = elements.Length - 1; i >= 1; i--) {
if (elements[i]) {
continue;
}
elements[i] = true;
int currentStart = i;
int currentRun = 1;
long current = i;
while (current != 1) {
if (current > biggest) {
biggest = current;
}
if ((current & 1) == 0) {
current = current >> 1;
} else {
current = current + current + current + 1;
}
currentRun++;
if (current < elements.Length) {
elements[current] = true;
}
}
if (currentRun > longestRun) {
longestStart = i;
longestRun = currentRun;
}
}
Console.WriteLine("Longest Start: {0}, Run {1}", longestStart, longestRun);
Console.WriteLine("Biggest number: {0}", biggest);
}
Basically I've been trying to make a working hours calculator and I've run into a problem. When the start time's value is greater than the finish time (eg. start is 23 and finish is 19), the result comes up as a negative. So what I want it to do in that scenario is to then multiply the negative number by -1 to make it positive again. However, this code below doesn't seem to have any effect in my app.
-(IBAction)done:(id)sender {
int result = [finishHours.text intValue] - [startHours.text intValue];
totalHours.text = [NSString stringWithFormat:#"%i", result];
if (result < 0) {
result = result * -1;
}
You set totalHours.text before you change the sign of your result variable.
what do you mean it doesn't have any effect? is it because you have changed result after you have set totalHours? would this fix your issue?
-(IBAction)done:(id)sender {
int result = [finishHours.text intValue] - [startHours.text intValue];
if (result < 0) {
result = result * -1;
}
totalHours.text = [NSString stringWithFormat:#"%i", result];