| package li.cil.oc.util |
| |
| import scala.collection.mutable |
| |
| object GameTimeFormatter { |
| // Locale? What locale? Seriously though, since this would depend on the |
| // server's locale I think it makes more sense to keep it English always. |
| private val weekDays = Array("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday") |
| private val shortWeekDays = Array("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat") |
| private val months = Array("January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December") |
| private val shortMonths = Array("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec") |
| private val amPm = Array("AM", "PM") |
| |
| class DateTime(val year: Int, val month: Int, val day: Int, |
| val weekDay: Int, val yearDay: Int, |
| val hour: Int, val minute: Int, val second: Int) |
| |
| // See http://www.cplusplus.com/reference/ctime/strftime/ |
| private val specifiers: Map[Char, (DateTime) => String] = Map( |
| 'a' -> (t => shortWeekDays(t.weekDay - 1)), |
| 'A' -> (t => weekDays(t.weekDay - 1)), |
| 'b' -> (t => shortMonths(t.month - 1)), |
| 'B' -> (t => months(t.month - 1)), |
| 'c' -> (t => format("%a %b %d %H:%M:%S %Y", t)), |
| 'C' -> (t => f"${t.year / 100}%02d"), |
| 'd' -> (t => f"${t.day}%02d"), |
| 'D' -> (t => format("%m/%d/%y", t)), |
| 'e' -> (t => f"${t.day}% 2d"), |
| 'F' -> (t => format("%Y-%m-%d", t)), |
| //'g' -> (t => ""), |
| //'G' -> (t => ""), |
| 'h' -> (t => format("%b", t)), |
| 'H' -> (t => f"${t.hour}%02d"), |
| 'I' -> (t => f"${(t.hour + 11) % 12 + 1}%02d"), |
| 'j' -> (t => f"${t.yearDay}%03d"), |
| 'm' -> (t => f"${t.month}%02d"), |
| 'M' -> (t => f"${t.minute}%02d"), |
| 'n' -> (t => "\n"), |
| 'p' -> (t => amPm(if (t.hour < 12) 0 else 1)), |
| 'r' -> (t => format("%I:%M:%S %p", t)), |
| 'R' -> (t => format("%H:%M", t)), |
| 'S' -> (t => f"${t.second}%02d"), |
| 't' -> (t => "\t"), |
| 'T' -> (t => format("%H:%M:%S", t)), |
| //'u' -> (t => ""), |
| //'U' -> (t => ""), |
| //'V' -> (t => ""), |
| 'w' -> (t => s"${t.weekDay - 1}"), |
| //'W' -> (t => ""), |
| 'x' -> (t => format("%D", t)), |
| 'X' -> (t => format("%T", t)), |
| 'y' -> (t => f"${t.year % 100}%02d"), |
| 'Y' -> (t => f"${t.year}%04d"), |
| //'z' -> (t => ""), |
| //'Z' -> (t => ""), |
| '%' -> (t => "%") |
| ) |
| |
| private val monthLengths = Array( |
| Array(31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31), |
| Array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)) |
| |
| private def monthLengthsForYear(year: Int) = { |
| if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) monthLengths(0) else monthLengths(1) |
| } |
| |
| def parse(time: Double) = { |
| var day = (time / 24000).toLong |
| val weekDay = ((4 + day) % 7).toInt |
| val year = 1970 + (day / 364.2425).toInt |
| val yearDay = (day % 364.2425).toInt |
| day = yearDay |
| val monthLengths = monthLengthsForYear(year) |
| var month = 0 |
| while (day >= monthLengths(month)) { |
| day = day - monthLengths(month) |
| month = month + 1 |
| } |
| |
| var seconds = ((time % 24000) * 60 * 60 / 1000).toInt |
| var minutes = seconds / 60 |
| seconds = seconds % 60 |
| val hours = (minutes / 60) % 24 |
| minutes = minutes % 60 |
| |
| new DateTime(year, month + 1, day.toInt + 1, weekDay + 1, yearDay + 1, hours, minutes, seconds) |
| } |
| |
| def format(format: String, time: DateTime) = { |
| val result = new mutable.StringBuilder() |
| val iterator = format.iterator |
| while (iterator.hasNext) { |
| iterator.next() match { |
| case '%' if iterator.hasNext => |
| specifiers.get(iterator.next()) match { |
| case Some(specifier) => result.append(specifier(time)) |
| case _ => |
| } |
| case c => result.append(c) |
| } |
| } |
| result.toString() |
| } |
| |
| def mktime(year: Int, mon: Int, mday: Int, hour: Int, min: Int, sec: Int): Option[Int] = { |
| if (year < 1970 || mon < 1 || mon > 12) return None |
| val monthLengths = monthLengthsForYear(year) |
| val days = ((year - 1970) * 365.2425).ceil.toInt + (0 until mon - 1).foldLeft(0)((d, m) => d + monthLengths(m)) + mday - 1 |
| val secs = sec + (min + (hour - 1 + days * 24) * 60) * 60 |
| Option(secs) |
| } |
| } |