oblivion
oblivion

Reputation: 6548

How to generate all weeks between two week numbers of same/different year?

In my application, a week is represented as a String with format:

{year}-{week-no} // week-no ranges from 1 to 52

For eg: 7th week of year 2017 is represented as: 2017-07

I want to write a method that takes two week dates and generate all weeks in between those two given weeks. So, if I pass:

I have developed the following solution so far which works:

def weeksGenerator(fromWeek: String,toWeek: String): List[String] = {
    val upperWeekNummber = fromWeek.split("-").toList.last.toInt
    val upperYearNummber = fromWeek.split("-").toList.head.toInt
    val lowerWeekNummber = toWeek.split("-").toList.last.toInt
    val lowerYearNummber = toWeek.split("-").toList.head.toInt

    (lowerYearNummber - upperYearNummber) match {
      case 0L => Range.inclusive(upperWeekNummber,lowerWeekNummber).map{weekNum =>
        s"${upperYearNummber.toString}-${weekNum}"
      }.toList
      case diff: Int =>
        Range.inclusive(0,diff).foreach(println)
        Range.inclusive(0,diff).flatMap{
        d => if(d==diff){
          // this is the last element
          Range.inclusive(1,lowerWeekNummber).map{weekNum =>
            s"${(upperYearNummber+d).toString}-${weekNum}"
          }
        }
        else if(d ==0) {
          // first element
          Range.inclusive(upperWeekNummber,52).map{weekNum =>
            s"${(upperYearNummber+d).toString}-${weekNum}"
          }
        }
        else {
          Range.inclusive(1,52).map{weekNum =>
            s"${(upperYearNummber+d).toString}-${weekNum}"
          }
        }
      }.toList
    }
  }

I think there are two problems with my solution:

So, what would be the best approach here ?

Upvotes: 1

Views: 280

Answers (3)

Alejandro
Alejandro

Reputation: 31

I think the best way is to rely on the java.time api

The code could look like this (I don't include the parsing of the input):

import java.time.DayOfWeek
import java.time.LocalDate
import java.time.temporal.IsoFields
import java.time.temporal.ChronoUnit
import java.time.format.DateTimeFormatter
import java.time.temporal.TemporalAdjusters

val initialDate = LocalDate.ofYearDay(2017, 50).`with`(IsoFields.WEEK_OF_WEEK_BASED_YEAR, 50).`with`(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY))
val finalDate = LocalDate.ofYearDay(2017, 50).`with`(IsoFields.WEEK_OF_WEEK_BASED_YEAR, 8).`with`(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY))

for {
  i <- 0 to ChronoUnit.WEEKS.between(initialDate, finalDate).toInt
} yield initialDate.plusWeeks(i).format(DateTimeFormatter.ofPattern("YYYY-ww"))

Here you have a demo

Upvotes: 2

sarveshseri
sarveshseri

Reputation: 13985

You can leverage the java time API to do the hard work for you (handling of leap week etc). Just parse your inputs into LocalDate, then use Chrono API to get the number of weeks in between.

import java.time.LocalDate
import java.time.temporal.ChronoUnit
import java.time.format.DateTimeFormatter

def listOfWeeks(initialWeekString: String, finalWeekString: String): List[String] = {
  val dateTimeFormatter = DateTimeFormatter.ofPattern("YYYY-ww-EEE")
  val dateTimeFormatter2 = DateTimeFormatter.ofPattern("YYYY-ww")

  val initialDate = LocalDate.parse(initialWeekString + "-Mon", dateTimeFormatter)
  val finalDate = LocalDate.parse(finalWeekString + "-Mon", dateTimeFormatter)

  val weeksCount = ChronoUnit.WEEKS.between(initialDate, finalDate).toInt

  (0 to weeksCount)
    .map(i => initialDate.plusWeeks(i).format(dateTimeFormatter2))
    .toList
}

println(listOfWeeks("2017-05", "2017-06"))

// List(2017-05, 2017-06)

Upvotes: 2

nicodp
nicodp

Reputation: 2392

I would probably do the following:

def weeksGenerator(fromWeek: String,toWeek: String): List[String] = {
  val maxWeeks = 52
  val minWeeks = 1

  val year1 = fromWeek.split("-")(0).toInt
  val year2 = toWeek.split("-")(0).toInt
  val week1 = fromWeek.split("-")(1).toInt
  val week2 = toWeek.split("-")(1).toInt

  val wholeWeeks = minWeeks to maxWeeks
  val years = year1 to year2
  val headWeeks = week1 to maxWeeks
  val lastWeeks = minWeeks to week2

  val enumeratedYears = years.zip(1 to years.length)
  val yearsAndWeeksZip = enumeratedYears.map{case (year, index) => if (index == 1) (year, headWeeks) else if (index == years.length) (year, lastWeeks) else (year, wholeWeeks)}
  val yearsAndWeeks = yearsAndWeeksZip.flatMap{case (year, weeks) => weeks.map(week => year.toString + "-" + "%02d".format(week))}

  yearsAndWeeks.toList
}

You can use format to properly show a number likewise:

scala> "%02d".format(4)
res15: String = 04

scala> "%02d".format(50)
res16: String = 50

On the REPL:

scala> weeksGenerator("2017-10", "2018-01")
res3: List[String] = List(2017-10, 2017-11, 2017-12, 2017-13, 2017-14, 2017-15, 2017-16, 2017-17, 2017-18, 2017-19, 2017-20, 2017-21, 2017-22, 2017-23, 2017-24, 2017-25, 2017-26, 2017-27, 2017-28, 2017-29, 2017-30, 2017-31, 2017-32, 2017-33, 2017-34, 2017-35, 2017-36, 2017-37, 2017-38, 2017-39, 2017-40, 2017-41, 2017-42, 2017-43, 2017-44, 2017-45, 2017-46, 2017-47, 2017-48, 2017-49, 2017-50, 2017-51, 2017-52, 2018-01)

I think this solution portrays a concise and clear way of using high order functions to approach this problem. It could be shortened, but I guess it will lose clearness.

Upvotes: 2

Related Questions