Date intervals keeping state - scala

My task is to write function, that takes 2 LocalDateTime instances and generate list of intervals, splitted by 15 minutes
case class Interval(start:LocalDateTime, end:LocalDateTime)
so, for example if startDate = 2 Aug 2017 14:30:15 and endDate is 2 Aug 2017 15:00:00, intervals should be
List(Interval(2 Aug 2017 14:30:15, 2 Aug 2017 14:45:15), Interval(2 Aug 2017 14:45:15, 2 Aug 2017 15:00:00))
Here, is 2 main complications(at least for me)
1) If end date is less then prevDate + 15 min, then we need take min from(prevdate + 15 min, endDate)
2) We need somehow keep the state, because start of each interval is end of previous interval.
I am able to create imperative version, but I want to do it in functional style, please, help!)
x => {
var start = x.startTime
var end = min(findRightBorder(start), x.endTime)
var result = ListBuffer(Interval(start, end))
while (end.isBefore(x.endTime)) {
start = end
end = min(start.plusMinutes(QUARTER_MINUTES), x.endTime)
result += Interval(start, end)
}
result.toList
}
private def findRightBorder(dt: LocalDateTime): LocalDateTime = {
val minute = dt.getMinute
if (minute >= 0 && minute < 15) dt.withMinute(15)
else if (minute >= 15 && minute < 30) dt.withMinute(30)
else if (minute >= 30 && minute < 45) dt.withMinute(45)
else if (minute >= 45 && minute < 60) dt.withMinute(0).plusHours(1)
else dt
}
private def min(dt1: LocalDateTime, dt2: LocalDateTime): LocalDateTime = {
if (dt1.compareTo(dt2) < 0) dt1 else dt2
}

Another solution with streams:
x => {
Stream
.from(0)
.map(i => x.startTime.plusMinutes(i * QUARTER_MINUTES))
.takeWhile(_.isBefore(x.endTime))
.map(s => Interval(s, min(s.plusMinutes(QUARTER_MINUTES), x.endTime)))
.toList
}

Here's one way to go about this.
import java.time.LocalDateTime
case class Interval(start:LocalDateTime, end:LocalDateTime)
val dt1: LocalDateTime = ??? //some start DateTime
val dt2: LocalDateTime = ??? //some end DateTime
// a (potentially) infinite Stream of dates at 15 minute intervals
// starting at dt1 but ending before dt2
val dates = Stream.iterate(dt1)(_.plusMinutes(15L))
.takeWhile(_.isBefore(dt2))
val result = dates.sliding(2) //pair the dates together
.toSeq //a Seq is easier to append to
.map{case Seq(from,to) => Interval(from,to)} //make Intervals
.:+(Interval(dates.last,dt2)) //append the final Interval
.toList //if you need a List result

Related

How to calculate period between a specific date?

I'm studying Flutter and in my application I need to calculate how many days are left until the 17th of the next month. There is no way to have a specific date, so it would be crucial to have today's date and add 1 month and calculate how many days are left.
For example: Today is 2021-09-09 and there are 38 days to 2021-10-17. The function does not calculate the 17th of the current month, but the following month.
Any idea how to make a function that takes the current date, adds 1 month and calculates how many days are left until the 17th? Thanks.
Basically copy the DateTime object with 1 added to the current month number and the day set to the 17th. Then subtract the two DateTime objects and convert the resulting Duration to days. It's important to perform all calculations in UTC so that Daylight Saving Time adjustments aren't a factor.
/// Returns the number of days to the specified [day] in the month following
/// [startDate].
int daysToNextMonthDay(DateTime startDate, int day) {
// Recreate `startDate` as a UTC `DateTime`. We don't care about the
// time.
//
// Note that this isn't the same as `startDate.toUtc()`. We want a UTC
// `DateTime` with specific values, not necessarily the UTC time
// corresponding to same moment as `startDate`.
startDate = DateTime.utc(startDate.year, startDate.month, startDate.day);
var nextMonthDay = DateTime.utc(startDate.year, startDate.month + 1, day);
// Verify that the requested day exists in the month.
if (nextMonthDay.day != day) {
throw ArgumentError(
'Day $day does not exist for the month following ${startDate.month}',
);
}
return nextMonthDay.difference(startDate).inDays;
}
void main() {
var now = DateTime(2021, 9, 9);
print(daysToNextMonthDay(now, 17)); // Prints: 38
}
try this:
void main() {
DateTime date = DateTime.now();
print(daysLeft(date.subtract(Duration(days: 1))));
}
bool isLeapYear(int year) {
if (year % 4 == 0) {
if (year % 100 == 0) {
if (year % 400 == 0) {
return true;
} else {
return false;
}
} else {
return false;
}
} else {
return false;
}
}
int daysLeft(DateTime date){
int daysLeft = 0;
switch(date.month){
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12: daysLeft = 31 - date.day + 17; break;
case 4:
case 6:
case 9:
case 11: daysLeft = 30 - date.day + 17; break;
case 2: if(isLeapYear(date.year)){
daysLeft = 29 - date.day + 17;
break;
}else{
daysLeft = 28 - date.day + 17;
}
}
return daysLeft;
}

DateTime: Difference between Hour and Integer

I have some mistakes, in my code in the 2 lines where a comment above them:
import java.time.temporal.ChronoUnit
import java.time.LocalTime
import scala.concurrent.duration._
val t = LocalTime.now()
def toStart(t: LocalTime) = {
val start = LocalTime.of(9, 0)
val midEnd = LocalTime.of(13, 0)
val midStart = LocalTime.of(14, 0)
val end = LocalTime.of(18, 0)
if (t.isBefore(start)) 0.hours
// if (9 > myHour < 13 ==> myHour + 9 Hours, I wrote: - 9.hours instead of + 4.hours
else if (t.isBefore(midEnd)) t.until(midEnd, ChronoUnit.MILLIS).millis - 9.hours
else if (t.isBefore(midStart)) 4.hours
// if (14 > myHour < 18 Then (myhour - 14) + 4
else if (t.isBefore(end)) t.until(end, ChronoUnit.MILLIS).millis
else 8.hours
}
implicit class formatter(d: FiniteDuration) {
def withMinutes = {
val l = d.toMinutes
s"${l / 60}:${l % 60}"
}
def withSeconds = s"${d.toHours}:${d.toMinutes % 60}:${d.toSeconds % 60}"
}
The test of the function ToStart, is false in these tow cases:
scala> toStart(LocalTime.of(9, 30, 24)).withSeconds
res89: String = -5:-30:-24
scala> toStart(LocalTime.of(12, 30, 32)).withSeconds
res90: String = -8:-30:-32
scala> toStart(LocalTime.of(14, 30, 45)).withSeconds
res92: String = 3:29:15
scala> toStart(LocalTime.of(16, 22, 44)).withSeconds
res93: String = 1:37:16
How can I change my code to find the best result ?
Code should be similar to my answer to you here, but you need to understand what I did. You definitely need to check api calls I used, but I added some additional comments:
import java.time.temporal.ChronoUnit
import java.time.LocalTime
import scala.concurrent.duration._
val t = LocalTime.now()
// start of the day
val start = LocalTime.of(9, 0)
// end of first half
val midEnd = LocalTime.of(13, 0)
// start of second half
val midStart = LocalTime.of(14, 0)
// end of the day
val end = LocalTime.of(18, 0)
// here we define duration of first half a day: diff between start of a day and midEnd (end of first half)
val firstHalf = start.until(midEnd, ChronoUnit.MILLIS).millis
// here we define duration of second half a day: diff between start of second half a day and end of a day
val secondHalf = midStart.until(end, ChronoUnit.MILLIS).millis
def toStart(t: LocalTime) = {
// when checked time is before start of a day
if (t.isBefore(start)) 0.hours
// otherwise when checked time is before end of first half (will be diff between start time and checked time)
else if (t.isBefore(midEnd)) start.until(t, ChronoUnit.MILLIS).millis
// otherwise when checked time is before start of second half (will be duration of first half)
else if (t.isBefore(midStart)) firstHalf
// otherwise when checked time is before end of a day (will be duration of first half + duration of diff between checked time and start of second half)
else if (t.isBefore(end)) firstHalf + midStart.until(t, ChronoUnit.MILLIS).millis
// otherwise sum of durations
else firstHalf + secondHalf
}
// here you can add any specific format for evaluated duration
implicit class formatter(d: FiniteDuration) {
def withMinutes = {
// convert to minutes
val l = d.toMinutes
// format
s"${l / 60}:${l % 60}"
}
}
toStart(t).withMinutes
toStart(LocalTime.of(9, 30)).withMinutes
toStart(LocalTime.of(12, 30)).withMinutes
toStart(LocalTime.of(13, 30)).withMinutes
toStart(LocalTime.of(14, 30)).withMinutes
Spend some time and check java.time api (specifically LocalTime.until). Check FiniteDuration api to understand .millis suffix I used

Passing an object of a class to a method in the class

I wrote a simple date class for practice but I can't seem to pass the objects in the class to the methods within the class. The class is supposed to be able to accept test dates, correct them if they are out of range and then print the correct date. Here is the code
class Date:
month = int
day = int
year = int
def setDate(self, month, day, year)
HIGH_MONTH = 12
HIGHEST_DAYS = ['none',31,29,31,30,31,30,31,31,30,31,30,31]
if month > HIGH_MONTH:
month = HIGH_MONTH
elif month < 1:
month = 1
else:
month = month
if day > HIGHEST_DAYS[month]:
day = HIGHEST_DAYS[month]
elif day < 1:
day = 1
else:
day = day
def showDate(self):
print 'Date:',month,'/',day,'/',year
#These are my tests
birthday=Date()
graduation=Date()
birthday.month=6
birthday.day=24
birthday.year=1984
graduation.setDate(5,36,2016)
birthday.showDate()
graduation.showDate()
What I get is a NameError: global name 'month' is not defined
In order for you to use a "global" variable in your class, assign the variables to self.variable_name as such:
class Date:
month = int
day = int
year = int
def setDate(self, month, day, year):
HIGH_MONTH = 12
HIGHEST_DAYS = [None,31,29,31,30,31,30,31,31,30,31,30,31]
if month > HIGH_MONTH:
month = HIGH_MONTH
elif month < 1:
month = 1
else:
month = month
if day > HIGHEST_DAYS[month]:
day = HIGHEST_DAYS[month]
elif day < 1:
day = 1
else:
day = day
self.year = year
self.month = month
self.day = day
def showDate(self):
print 'Date:',self.month,'/',self.day,'/',self.year
>>> birthday=Date()
>>> graduation=Date()
>>> birthday.month=6
>>> birthday.day=24
>>> birthday.year=1984
>>> graduation.setDate(5,36,2016)
>>> birthday.showDate()
Date: 6 / 24 / 1984
>>> graduation.showDate()
Date: 5 / 31 / 2016
>>>

How to calculate the time difference between 2 date time values

I am trying to calculate the time difference between 2 date time strings.
I have 2 inputs where the input string is something like this "1:00 PM" and the second one "3:15 PM". I want to know the time difference. So for the above example I want to display 3.15
What I have done:
Converted the time to a 24 hours format. So "1:00 PM" becomes "13:00:00"
Appended the new time to a date like so: new Date("1970-1-1 13:00:00")
Calculated the difference like so:
Code:
var total = Math.round(((new Date("1970-1-1 " + end_time) -
new Date("1970-1-1 " + start_time) ) / 1000 / 3600) , 2 )
But the total is always returning integers and not decimals, so the difference between "1:00 PM" and "3:15 PM" is 2 not 2.15.
I have also tried this (using jQuery, but that is irrelevant):
$('#to_ad,#from_ad').change(function(){
$('#total_ad').val( getDiffTime() );
});
function fixTimeString(time){
var hours = Number(time.match(/^(\d+)/)[1]);
var minutes = Number(time.match(/:(\d+)/)[1]);
var AMPM = time.match(/\s(.*)$/)[1];
if(AMPM == "PM" && hours<12) hours = hours+12;
if(AMPM == "AM" && hours==12) hours = hours-12;
var sHours = hours.toString();
var sMinutes = minutes.toString();
if(hours<10) sHours = "0" + sHours;
if(minutes<10) sMinutes = "0" + sMinutes;
return sHours + ':' + sMinutes + ':00';
}
function getDiffTime(){
var start_time = fixTimeString($('#from_ad').val());
var end_time = fixTimeString($('#to_ad').val());
var start = new Date("1970-1-1 " + end_time).getTime(),
end = new Date("1970-1-1 " + start_time).getTime();
return parseInt(((start - end) / 1000 / 3600, 10)*100) / 100;
}
But the total_ad input is displaying only integer values.
How can I fix this problem?
Math.round rounds to the nearest integer, multiply and divide instead
var start = new Date("1970-1-1 " + start_time).getTime(),
end = new Date("1970-1-1 " + end_time).getTime();
var total = (parseInt(((start-end) / 1000 / 3600)*100, 10)) / 100;
FIDDLE
When you take the time 15:15:00 and subtract 13:00:00, you're left with 2.15 hours, not 3.15, and this example would return 2.15 even without making sure there is only two decimals, but for other times that might not be the case.
You could also use toFixed(2), but that would leave you with 3.00 and not 3 etc.
This is how I calculate it:
calculateDiff();
function calculateDiff(){
_start = "7:00 AM";
_end = "1:00 PM";
_start_time = parseAMDate(_start);
_end_time = parseAMDate(_end);
if (_end_time < _start_time){
_end_time = parseAMDate(_end,1);
}
var difference= _end_time - _start_time;
var hours = Math.floor(difference / 36e5),
minutes = Math.floor(difference % 36e5 / 60000);
if (parseInt(hours) >= 0 ){
if (minutes == 0){
minutes = "00";
}
alert(hours+":"+minutes);
}
}
function parseAMDate(input, next_day) {
var dateReg = /(\d{1,2}):(\d{2})\s*(AM|PM)/;
var hour, minute, result = dateReg.exec(input);
if (result) {
hour = +result[1];
minute = +result[2];
if (result[3] === 'PM' && hour !== 12) {
hour += 12;
}
}
if (!next_day) {
return new Date(1970, 01, 01, hour, minute).getTime();
}else{
return new Date(1970, 01, 02, hour, minute).getTime();
}
}

How to subtract dates from each other

I am using Groovy. I have parsed a textfile whose lines contain information, including dates. I now have just the dates, for example:
08:13:16,121
09:32:42,102
10:43:47,153
I want to compare the deltas between these values; how can I do this? i.e, I want to subtract the first from the second, and compare that value to the difference between the third and the second. I will save the largest value.
You can use TimeCategory to add methods for time differences to date classes:
import groovy.time.TimeCategory
use(TimeCategory) {
println date1 - date2
}
Subtracting one date from another will result in a TimeDuration object.
Assuming your times are in a file times.txt, you can do this:
def parseDate = { str -> new Date().parse( 'H:m:s,S', str ) }
def prevDate = null
def deltas = []
use( groovy.time.TimeCategory ) {
new File( 'times.txt' ).eachLine { line ->
if( line ) {
if( !prevDate ) {
prevDate = parseDate( line )
}
else {
def nextDate = parseDate( line )
deltas << nextDate - prevDate
prevDate = nextDate
}
}
}
}
println deltas
println( deltas.max { it.toMilliseconds() } )
Which will print:
[1 hours, 19 minutes, 25.981 seconds, 1 hours, 11 minutes, 5.051 seconds]
1 hours, 19 minutes, 25.981 seconds