So, I am trying to calculate the time between two dates that fits certain criteria (here: work / non-work) and I'm confused about the results as I can't find out why it's wrong.
But first, some code;
**Input Date A:** 2009-01-01 2:00 pm
**Input Date B:** 2009-01-02 9:00 am
So, as you can see, the total timespan (calculated e.g. by DateB.Substract(DateA)) is 19 hours.
I now want to calculate how many hours in this timespan are "non-work" hours, based on an average work day from 8am to 5pm - Result should be 15 hours (So, 19 - 15 = 4 hours total work time) (2:00 pm to 5:00 pm plus 8:00 am to 9:00 am)
But, following my code
DateTime _Temp = new DateTime(2009, 1, 1, 14, 0, 0);
DateTime _End = new DateTime(2009, 1, 2, 9, 0, 0);
int _WorkDayStart = 8;
int _WorkDayEnd = 17;
int _Return = 0;
while (_End > _Temp)
{
if (_Temp.Hour <= _WorkDayStart || _Temp.Hour >= _WorkDayEnd)
_Return++;
_Temp = _Temp.AddHours(1);
}
the result is 16 hours (19 - 16 = 3 hours total work time) - I don't see where the mistake is, so there is one hour missing?! I refactored it on paper and it should work as intended... but doesn't :-/
Anyone sees the mistake?
You're counting both ends as non-work, instead of just one. This line:
if (_Temp.Hour <= _WorkDayStart || _Temp.Hour >= _WorkDayEnd)
should probably be:
if (_Temp.Hour < _WorkDayStart || _Temp.Hour >= _WorkDayEnd)
You're effectively stepping through "start hours". So 8am itself should count as a work hour, because it's the start of a work hour, but 5pm won't because it's the start of a non-work hour.
I would also strongly advise you to either put braces around the body of the if statement or at least indent the following line. (I'd further advise you to use camelCase for local variables, but that's just a convention thing - I've never seen C# code written with that convention for local variables before now. You may want to read Microsoft's naming conventions document - it doesn't specify local variables, but they;re generally in camel case.)
Finally, I personally find it easier to read conditions where the "thing that's changing" is on the left - so I'd change your loop condition to:
while (_Temp < _End)
An alternative is to change it into a for loop. With all of these changes, the code would be:
DateTime start = new DateTime(2009, 1, 1, 14, 0, 0);
DateTime end = new DateTime(2009, 1, 2, 9, 0, 0);
int workDayStart = 8;
int workDayEnd = 17;
int nonWorkHours = 0;
for (DateTime time = start; time < end; time = time.AddHours(1))
{
if (time.Hour < workDayStart || time.Hour >= workDayEnd)
{
nonWorkHours++;
}
}
Finally, extract this into a method:
public static int CountNonWorkHours(DateTime start, DateTime end,
int workDayStart, int workDayEnd)
{
int nonWorkHours = 0;
for (DateTime time = start; time < end; time = time.AddHours(1))
{
if (time.Hour < workDayStart || time.Hour >= workDayEnd)
{
nonWorkHours++;
}
}
return nonWorkHours;
}
EDIT: Regarding konamiman's suggestion... yes, looping over each hour is very inefficient. However, it's relatively easy to get right. Unless you're going to be doing this a lot with long time periods, I'd use this fairly simple code. It would be very easy to end up with off-by-one errors in various situations if you tried to do a per-day version. While I don't like inefficient code, I don't mind it if it's not hurting me :)
If you plan to reuse this code, I would refactor it to avoid the loop. You could just multiply the number of whole days by the labour hours per day, then treat the first and the last day of the interval as special cases.
You could also use this to avoid a loop
DateTime startDate = new DateTime(2009, 1, 1, 14, 0, 0);
DateTime endDate = new DateTime(2009, 1, 2, 9, 0, 0);
int startTime = 8;
int endTime = 17;
int ret = ((endDate.Subtract(startDate).Days - 1) * 8)
+ (startDate.Hour >= startTime && startDate.Hour < endTime ? endTime - startDate.Hour : 0)
+ (endDate.Hour > startTime && endDate.Hour <= endTime ? endDate.Hour - startTime : 0);
Related
I want to set a timer to run once every minute, while I was writing the programm I found this unexpected behaviour
void main() {
final now = DateTime.now().toUtc();
final minuteAfterNow = new DateTime(now.year,now.month,now.day,now.hour,now.minute +1,now.second,now.millisecond,now.microsecond);
print(minuteAfterNow);
print(now);
print(minuteAfterNow.difference(now));
}
The output is the following:
2020-12-30 09:41:06.508
2020-12-30 09:40:06.508Z
-1:59:00.000000
Shouldn't the difference output 1 minute? What's with this output?
It is different because there are 2 issues:
1/ You are comparing between UTC and non UTC, that will take extra offset :). I will altenate you code and let you see by yourself:
final now = DateTime.now().toUtc();
final now1 = DateTime.utc(now.year, now.month, now.day,now.hour, now.minute, now.second + 1, now.millisecond, now.microsecond);
final difference = now.difference(now1);
print(difference);
2/ You missed the second, millisecond, microsecond parameters. Once you ignore it, it will be zero by default. Please take a look at the DateTime class in Dart
DateTime.utc(int year,
[int month = 1,
int day = 1,
int hour = 0,
int minute = 0,
int second = 0,
int millisecond = 0,
int microsecond = 0])
: this._internal(year, month, day, hour, minute, second, millisecond,
microsecond, true);
Btw, please add your code next time. Happy coding. :)
Yeah, I expected the difference function to calculate only the difference between two dates not checking if they are Utc or not which is not true
external DateTime._internal(int year, int month, int day, int hour,
int minute, int second, int millisecond, int microsecond, bool isUtc);
that is why the below code gives a desired output, even though the dates where the same before.
final now = DateTime.now();
final now2 = now.toUtc();
final minuteAfterNow = new DateTime(now.year,now.month,now.day,now.hour,now.minute +1,now.second,now.millisecond,now.microsecond).toUtc();
print(minuteAfterNow);
print(now2);
print(minuteAfterNow.difference(now2).inMinutes);
outputs:
2020-12-30 09:37:52.559Z
2020-12-30 09:36:52.559Z
1
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
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])));
}
}
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);
I'm using code that someone else wrote to load a file format that I can't find any docs or specs for. The file format is *.vpd which is the output of varioport readers used for EKG's.
It reads out 4 bytes for the time and 4 bytes for the date and are each stored in a 4 element array. In a test file I have, the 4 time bytes are { 19, 38, 3, 0 } and the 4 date bytes are { 38, 9, 8, 0 }. It could also be a 32-bit int, and the guy who wrote this is reading it wrong. Judging by the trailing 0 on both, I would assume little endian in which case the values of time and date as int32's are 206355 and 526630 respectively.
Do you know of any time/date formats that are expressed in 4 bytes (or a single int)? I'm hopelessly lost at the moment.
EDIT: I should add that I don't know what the values could be, apart from that the date is likely to be within the last few years.
The code, there are no comments associated with it.
s.Rstime = fread(fid, 4, 'uint8');
s.Rsdate = fread(fid, 4, 'uint8');
In the Varioport VPD file format, BCD (binary coded decimal) code is used for date and time. You had no chance to guess this, because the fread calls you posted are obviously nonsense. You are reading at the wrong places.
Try this in C/C++ (matlab code will look very similar):
typedef struct
{
int channelCount;
int scanRate;
unsigned char measureDate[3];
unsigned char measureTime[3];
...
}
VPD_FILEINFO;
...
unsigned char threeBytes[3];
size_t itemsRead = 0;
errno_t err = _tfopen_s(&fp, filename, _T("r+b"));
// n.b.: vpd files have big-endian byte order!
if (err != 0)
{
_tprintf(_T("Cannot open file %s\n"), filename);
return false;
}
// read date of measurement
fseek(fp, 16, SEEK_SET);
itemsRead = fread(&threeBytes, 1, 3, fp);
if (itemsRead != 3)
{
_tprintf(_T("Error trying to read measureDate\n"));
return false;
}
else
{
info.measureDate[0] = threeBytes[0]; // day, binary coded decimal
info.measureDate[1] = threeBytes[1]; // month, binary coded decimal
info.measureDate[2] = threeBytes[2]; // year, binary coded decimal
}
// read time of measurement
fseek(fp, 12, SEEK_SET);
itemsRead = fread(&threeBytes, 1, 3, fp);
if (itemsRead != 3)
{
_tprintf(_T("Error trying to read measureTime\n"));
return false;
}
else
{
info.measureTime[0] = threeBytes[0]; // hours, binary coded decimal
info.measureTime[1] = threeBytes[1]; // minutes, binary coded decimal
info.measureTime[2] = threeBytes[2]; // seconds, binary coded decimal
}
...
_tprintf(_T("Measure date == %x %x %x\n"),
info.measureDate[0],
info.measureDate[1],
info.measureDate[2]);
_tprintf(_T("Measure time == %x %x %x\n"),
info.measureTime[0],
info.measureTime[1],
info.measureTime[2]);
It's not important anymore, I doubt anyone would be able to answer it anyway.