Splitting a number into parts in Scala - scala

I am trying to split a number, like
20130405
into three parts: year, month and date.One way is to convert it to string and use regex. Something like:
(\d{4})(\d{2})(\d{2}).r
A better way is to divide it by 100. Something like:
var date = dateNumber
val day = date % 100
date /= 100
val month = date % 100
date /= 100
val year = date
I get itchy while using 'var' in Scala. Is there a better way to do it?

I would go with the former:
scala> val regex = """(\d{4})(\d{2})(\d{2})""".r
regex: scala.util.matching.Regex = (\d{4})(\d{2})(\d{2})
scala> val regex(year, month, day) = "20130405"
year: String = 2013
month: String = 04
day: String = 05

This is probably not much better than your own solution, but it doesn't use var and doesn't require transforming the number to a string. On the other hand, it's not very safe - if you're not 100% sure that your number is going to be well formatted, better use a SimpleDateFormat - granted, it's more expensive, but at least you're safe from illegal input.
val num = 20130405
val (date, month, year) = (num % 100, num / 100 % 100, num / 10000)
println(date) // Prints 5
println(month) // Prints 4
println(year) // Prints 2013
I'd personally use a SimpleDateFormat even if I were sure the input will always be legal. The only certainty there is is that I'm wrong and the input will someday be illegal.

Better than substring would be to use the java Date and SimpleDateFormat classes, see:
https://stackoverflow.com/a/4216767/88588

Not very scala-ish but...
scala> import java.util.Calendar
import java.util.Calendar
scala> val format = new java.text.SimpleDateFormat("yyyyMMdd")
format: java.text.SimpleDateFormat = java.text.SimpleDateFormat#ef87e460
scala> format.format(new java.util.Date())
res0: String = 20131025
scala> val d=format.parse("20130405")
d: java.util.Date = Fri Apr 05 00:00:00 CEST 2013
scala> val calendar = Calendar.getInstance
calendar: java.util.Calendar = [cut...]
scala> calendar.setTime(d)
scala> calendar.get(Calendar.YEAR)
res1: Int = 2013

Related

scala get difference between two dates in number of days

I was trying to calculate the number of days between 2 dates in scala. I tried using compareTo and import java.time.Period function. But it is not giving the exact value of days when comparing two dates from different months.
val date1 = "2022-04-01"
date1: String = 2022-04-01
val date2 = "2022-04-04"
date2: String = 2022-04-04
date2.compareTo(date1)
res37: Int = 3
val date2 = "2022-05-04"
date2: String = 2022-05-04
date2.compareTo(date1)
res38: Int = 1
val date1 = LocalDate.parse("2022-04-01")
date1: java.time.LocalDate = 2022-04-01
val date2 = LocalDate.parse("2022-04-04")
date1: java.time.LocalDate = 2022-04-04
val p = Period.between(date1, date2)
p: java.time.Period = P3D
p.getDays
res39: Int = 3
val date2 = LocalDate.parse("2022-05-04")
date2: java.time.LocalDate = 2022-05-04
val p = Period.between(date1, date2)
p: java.time.Period = P1M3D
p.getDays
res40: Int = 3
I want to get the difference as 33 days while comparing the dates 2022-04-01 and 2022-05-04. Is there a different way to achieve this?
What you are looking for is the LocalDate.until() method with unit set to ChronoUnit.DAYS:
import java.time.LocalDate
import java.time.temporal.ChronoUnit
val date1 = LocalDate.parse("2022-04-01")
val date2 = LocalDate.parse("2022-05-03")
val daysBetween = date1.until(date2, ChronoUnit.DAYS)
// scala> date1.until(date2, ChronoUnit.DAYS)
// res1: Long = 32
Note that your first attempt does simple lexicographical string comparison which is oblivious to the fact that your strings are dates. The return value of String.compareTo() (and Comparable<T>.compareTo() in general) is to be interpreted only on the basis whether it is 0, < 0, or > 0. The actual value is meaningless in most cases. The fact that it matches the actual number of days in your first example is due to how your dates are formatted and to how String.compareTo() works:
In this case, compareTo returns the difference of the two character values at position k in the two string [sic]
Here, k refers to the position where the two strings first differ.
Your second attempt with Period doesn't work either because Period represents conceptual differences. Adding P1M3D to some date means incrementing the month by one and the day by three, therefore the length in days is dependent on the month to which it is being applied:
2022-04-01 + P1M3D = 2022-05-04 -> 33 days
2022-05-01 + P1M3D = 2022-06-04 -> 34 days
Thus, a freestanding Period object has no length in days (unless it only has days but not months or years) and the getDays accessor returns only the days component, not the full number of days.

Iterating over timestamp (date and hour)

I have some function that gets as an input specific starting-timestamp and end-timestamp (e.g. "2018-01-01 16:00:00" and "2018-01-01 17:00:00") (in the beginning of the code i import java.sql.Timestamp)
I want to iterate this function over time (e.g. - between 2018-01-01 until 2018-01-10, over each hour separately).
The furthest I got so far was iterating over the date, using import java.time.{LocalDate, Period}
but when I tried to change my code to import java.time.{LocalDateTime, Period}, it didn't work:
import java.time.temporal.ChronoUnit
import java.time.temporal.ChronoField.HOUR_OF_DAY
import java.time.{LocalDateTime, Period}
val start = LocalDateTime.of(2018, 1, 1,6,20)
val end = LocalDateTime.of(2018, 1, 11,6,30)
val dates: IndexedSeq[LocalDateTime] =
(0L to (end.toEpochSecond() - start.toEpochSecond())).map(hours =>
start.plusHours(hours)
)
dates.foreach(println)
Would highly appreciate your help!
You can take advantage of both scala streams and localdatetime API to make things easier than what you tried, which is let's say, a bit too low level ^^ !
val allDatesBeforeEnd = Stream.iterate(start)(_.plusHours(1)).takeWhile(_.isBefore(end)).toList
import java.time.{LocalDateTime, Period}
val start = LocalDateTime.of(2018, 1, 1,6,20)
val end = LocalDateTime.of(2018, 1, 11,6,30)
val periodInHours = Period.between(start.toLocalDate(), end.toLocalDate()).getDays*24
val dates: IndexedSeq[LocalDateTime] = (0L to periodInHours).map(start.plusHours(_))
dates.foreach(println)
You could first get the number of hours between the two datetimes and then loop over the range formed by this number of hours to create the range of datetimes:
val allDatesBeforeEnd =
(0L until ChronoUnit.HOURS.between(start, end)).map(start.plusHours(_))
Although I do prefer reading C4stor's solution this solution might be slightly better in terms of performance as it doesn't perform the takeWhile isBefore check for each iteration.
Try this, it prints all the hours between yesterday and today, just adapt it you use case :
val yesterday = LocalDateTime.now().minusDays(1)
val today = LocalDateTime.now()
Stream.iterate(yesterday){
h => h.plusHours(1)
}.takeWhile(_.isBefore(today)).foreach(println(_))

How to use split function in spark scala

I am supplying line by line to the program and each line consists of date in the format MM/DD/YYYY, how I can use split function here.
val data = line.split("/")
val year = data[2]
println(year)
I am not getting any output can anyone explain me where I am wrong.
You are not working on Java. Please look at the code snippet and make required changes in your code.
scala> val str = "12/05/2018"
str: String = 12/05/2018
scala> str.split("/")
res0: Array[String] = Array(12, 05, 2018)
scala> res0(2)
res1: String = 2018
So make below changes in your code:
val data = line.split("/")
val year = data(2)
println(year)

Setting number of decimals in formatting programmatically

I have the following Double in Scala:
val value: Double = 12.34
and get the formatted value, like so:
val formatted = f"$value%1.5f"
But I need to set the number of decimals (above 5) programmatically. I tried this, but it doesn't work:
val dec = 8
val formatted = f"$value%1.decf"
Any ideas?
val value: Double = 12.34
val dec = 8
val formatted = s"%1.${dec}f".format(value) // 12.34000000
You can use the scala BigDecimal with its setScale def then convert to a Double if necessary:
BigDecimal(12.35564126).setScale(5, BigDecimal.RoundingMode.HALF_UP).toDouble
// res0: Double = 12.35564
How about
fmt="%."+n+"f"
fmt.format(12.34)
Too obvious?

How do I write a script in Scala that converts the current time to the number of seconds since midnight?

I'm new to all of this; I'm currently taking CSC 101 and learning how to use Scala. This is our first assignment, and I don't know where to start. I was wondering if you could explain what each variable means and what it stands for?
I've been seeing the code below a lot, and I don't know what the percent signs mean or what they do.
val s = System.currentTimeMillis / 1000
val m = (s/60) % 60
val h = (s/60/60) % 24
Unfortunately it is not very straightforward. Here is some code for you:
import java.text.SimpleDateFormat
import java.util.Calendar
val formatter = new SimpleDateFormat("yyyy/MM/dd")
val cal = Calendar.getInstance()
val now = cal.getTime()
val thisDay = formatter.format(now)
val midnight = formatter.parse(thisDay)
// milliseconds since midnight
val msSinceMidnight = now.getTime - midnight.getTime
val secSinceMidnight = msSinceMidnight / 1000
println(secSinceMidnight)
You have to use Java APIs as shown above, or you could choose to use JodaTime library: http://www.joda.org/joda-time/.