Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Tree: 0541765d8e
Fetching contributors…

Cannot retrieve contributors at this time

452 lines (368 sloc) 16.587 kB
/*
* Copyright 2006-2011 WorldWide Conferencing, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.liftweb
package util
import java.text.SimpleDateFormat
import java.util.{TimeZone, Calendar, Date, Locale}
import common._
import org.joda.time.{DateTime, Duration, Period, PeriodType}
/**
* The TimeHelpers object extends the TimeHelpers. It can be imported to access all of the trait functions.
*/
object TimeHelpers extends TimeHelpers with ControlHelpers with ClassHelpers
/**
* The TimeHelpers trait provide functions to create TimeSpans (an object representing an amount of time), to manage date formats
* or general utility functions (get the date for today, get year/month/day number,...)
*/
trait TimeHelpers { self: ControlHelpers =>
// Logger must be lazy, since we cannot instantiate until after boot is complete
private lazy val logger = Logger(classOf[TimeHelpers])
/** private variable allowing the access to all TimeHelpers functions from inside the TimeSpan class */
private val outer = this
/** transforms a long to a TimeSpanBuilder object. Usage: 3L.seconds returns a TimeSpan of 3000L millis */
implicit def longToTimeSpanBuilder(in: Long): TimeSpanBuilder = TimeSpanBuilder(in)
/** transforms an int to a TimeSpanBuilder object. Usage: 3.seconds returns a TimeSpan of 3000L millis */
implicit def intToTimeSpanBuilder(in: Int): TimeSpanBuilder = TimeSpanBuilder(in)
/** transforms a long to a TimeSpan object. Usage: 3000L returns a TimeSpan of 3000L millis */
implicit def longToTimeSpan(in: Long): TimeSpan = TimeSpan(in)
/** transforms an int to a TimeSpan object. Usage: 3000 returns a TimeSpan of 3000L millis */
implicit def intToTimeSpan(in: Int): TimeSpan = TimeSpan(in)
private implicit def durToPeriod(dur: Duration): Period = dur.toPeriod(PeriodType.standard())
/** class building TimeSpans given an amount (len) and a method specify the time unit */
case class TimeSpanBuilder(val len: Long) {
def seconds = new TimeSpan(Right((new Period).plusSeconds(len.toInt)))
def second = seconds
def minutes = new TimeSpan(Right((new Period).plusMinutes(len.toInt)))
def minute = minutes
def hours = new TimeSpan(Right(Duration.standardHours(len): Period))
def hour = hours
def days = new TimeSpan(Right(Duration.standardDays(len): Period))
def day = days
def weeks = new TimeSpan(Right(Duration.standardDays(len * 7L): Period))
def week = weeks
def months = new TimeSpan(Right((new Period().plusMonths(len.toInt))))
def month = months
def years = new TimeSpan(Right((new Period().plusYears(len.toInt))))
def year = years
}
/*
/**
* transforms a TimeSpan to a date by converting the TimeSpan expressed as millis and creating
* a Date lasting that number of millis from the Epoch time (see the documentation for java.util.Date)
*/
implicit def timeSpanToDate(in: TimeSpan): Date = in.date
/** transforms a TimeSpan to its long value as millis */
implicit def timeSpanToLong(in: TimeSpan): Long = in.millis
*/
/**
* The TimeSpan class represents an amount of time.
* It can be translated to a date with the date method. In that case, the number of millis seconds will be used to create a Date
* object starting from the Epoch time (see the documentation for java.util.Date)
*/
class TimeSpan(private val dt: Either[DateTime, Period]) extends ConvertableToDate {
/** @return a Date as the amount of time represented by the TimeSpan after the Epoch date */
def this(ms: Long) =
this(if (ms < 52L * 7L * 24L * 60L * 60L * 1000L) Right(new Period(ms))
else Left(new DateTime(ms)))
def date: Date = dt match {
case Left(datetime) => new Date(datetime.getMillis())
case _ => new Date(millis)
}
/**
* Convert to a Date
*/
def toDate: Date = date
/**
* Convert to a JodaTime DateTime
*/
def toDateTime = dt match {
case Left(datetime) => datetime
case _ => new DateTime(millis)
}
def toMillis = millis
def millis = dt match {
case Left(datetime) => datetime.getMillis()
case Right(duration) => duration.toStandardDuration.getMillis()
}
/** @return a Date as the amount of time represented by the TimeSpan after now */
def later: TimeSpan = dt match {
case Right(duration) => new TimeSpan(Left(new DateTime(outer.millis).plus(duration)))
case _ => TimeSpan(millis + outer.millis)
}
/** @return a Date as the amount of time represented by the TimeSpan before now */
def ago: TimeSpan = dt match {
case Right(duration) => new TimeSpan(Left(new DateTime(outer.millis).minus(duration)))
case _ => TimeSpan(outer.millis - millis)
}
def noTime: Date = new DateExtension(this).noTime
/** @return a TimeSpan representing the addition of 2 TimeSpans */
def +[B](in: B)(implicit f: B => TimeSpan): TimeSpan =
(this.dt, f(in).dt) match {
case (Right(p1), Right(p2)) => p1.plus(p2)
case (Left(date), Right(duration)) => date.plus(duration)
case (Right(duration), Left(date)) => date.plus(duration)
case _ => TimeSpan(this.millis + f(in).millis)
}
/** @return a TimeSpan representing the addition of 2 TimeSpans */
def plus[B](in: B)(implicit f: B => TimeSpan): TimeSpan = this.+(in)(f)
/** @return a TimeSpan representing the substraction of 2 TimeSpans */
def -[B](in: B)(implicit f: B => TimeSpan): TimeSpan =
(this.dt, f(in).dt) match {
case (Right(p1), Right(p2)) => p1.minus(p2)
case (Left(date), Right(duration)) => date.minus(duration)
case (Right(duration), Left(date)) => date.minus(duration)
case _ => TimeSpan(this.millis - f(in).millis)
}
/** override the equals method so that TimeSpans can be compared to long, int and TimeSpan */
override def equals(cmp: Any) = {
cmp match {
case lo: Long => lo == this.millis
case i: Int => i == this.millis
case ti: TimeSpan => ti.dt == this.dt
case d: Date => d.getTime() == this.millis
case dt: DateTime => Left(dt) == this.dt
case dur: Duration => Right(dur: Period) == this.dt
case dur: Period => Right(dur) == this.dt
case _ => false
}
}
/** override the toString method to display a readable amount of time */
override def toString = dt match {
case Left(date) => date.toString
case Right(dur) => TimeSpan.format(millis)
}
}
/**
* The TimeSpan object provides class represents an amount of time.
* It can be translated to a date with the date method. In that case, the number of millis seconds will be used to create a Date
* object starting from the Epoch time (see the documentation for java.util.Date)
*/
object TimeSpan {
/** time units and values used when converting a total number of millis to those units (see the format function) */
val scales = List((1000L, "milli"), (60L, "second"), (60L, "minute"), (24L, "hour"), (7L, "day"), (10000L, "week"))
/** explicit constructor for a TimeSpan */
def apply(in: Long) = new TimeSpan(in)
/**
* Formats a number of millis to a string representing the number of weeks, days, hours, minutes, seconds, millis
*/
def format(millis: Long): String = {
def divideInUnits(millis: Long) = scales.foldLeft[(Long, List[(Long, String)])]((millis, Nil)){ (total, div) =>
(total._1 / div._1, (total._1 % div._1, div._2) :: total._2)
}._2
def formatAmount(amountUnit: (Long, String)) = amountUnit match {
case (amount, unit) if (amount == 1) => amount + " " + unit
case (amount, unit) => amount + " " + unit + "s"
}
divideInUnits(millis).filter(_._1 > 0).map(formatAmount(_)).mkString(", ")
}
/**
* Convert a Date to a TimeSpan
*/
implicit def dateToTS(in: Date): TimeSpan =
new TimeSpan(Left(new DateTime(in.getTime)))
/**
* Convert a DateTime to a TimeSpan
*/
implicit def dateTimeToTS(in: DateTime): TimeSpan =
new TimeSpan(Left(in))
/**
* Convert a Duration to a TimeSpan
*/
implicit def durationToTS(in: Duration): TimeSpan =
new TimeSpan(Right(in: Period))
/**
* Convert a Period to a TimeSpan
*/
implicit def periodToTS(in: Period): TimeSpan =
new TimeSpan(Right(in))
}
/** @return the current System.nanoTime() */
def nano = System.nanoTime()
/** @return the current number of millis: System.currentTimeMillis */
def millis = System.currentTimeMillis
/** @return the number of millis corresponding to 'in' seconds */
def seconds(in: Long): Long = in * 1000L
/** @return the number of millis corresponding to 'in' minutes */
def minutes(in: Long): Long = seconds(in) * 60L
/** @return the number of millis corresponding to 'in' hours */
def hours(in: Long): Long = minutes(in) * 60L
/** @return the number of millis corresponding to 'in' days */
def days(in: Long): Long = hours(in) * 24L
/** @return the number of millis corresponding to 'in' weeks */
def weeks(in: Long): Long = days(in) * 7L
/** implicit def used to add the noTime method to the Date class */
implicit def toDateExtension(d: Date) = new DateExtension(d)
/** This class adds a noTime method the Date class, in order to get at Date object starting at 00:00 */
class DateExtension(date: Date) {
/** @return a Date object starting at 00:00 from date */
def noTime = {
val calendar = Calendar.getInstance
calendar.set(Calendar.HOUR_OF_DAY, 0)
calendar.set(Calendar.MINUTE, 0)
calendar.set(Calendar.SECOND, 0)
calendar.set(Calendar.MILLISECOND, 0)
calendar.getTime
}
}
/** implicit def used to add the setXXX methods to the Calendar class */
implicit def toCalendarExtension(c: Calendar) = new CalendarExtension(c)
/** This class adds the setXXX methods to the Calendar class. Each setter returns the updated Calendar */
class CalendarExtension(c: Calendar) {
/** set the day of the month (1 based) and return the calendar */
def setDay(d: Int) = { c.set(Calendar.DAY_OF_MONTH, d); c }
/** set the month (0 based) and return the calendar */
def setMonth(m: Int) = { c.set(Calendar.MONTH, m); c }
/** set the year and return the calendar */
def setYear(y: Int) = { c.set(Calendar.YEAR, y); c }
/** set the TimeZone and return the calendar */
def setTimezone(tz: TimeZone) = { c.setTimeZone(tz); c }
/** set the time to 00:00:00.000 and return the calendar */
def noTime = { c.setTime(c.getTime.noTime); c }
}
/** @return the date object for now */
def now = new Date
/** @return the Calendar object for today (the TimeZone is the local TimeZone). Its time is 00:00:00.000 */
def today = Calendar.getInstance.noTime
/** @return the current year */
def currentYear: Int = Calendar.getInstance.get(Calendar.YEAR)
/**
* @deprecated use now instead
* @return the current time as a Date object
*/
def timeNow = new Date
/**
* @deprecated use today instead
* @return the current Day as a Date object
*/
def dayNow: Date = 0.seconds.later.noTime
/** alias for new Date(millis) */
def time(when: Long) = new Date(when)
/** @return the month corresponding to today (0 based, relative to UTC) */
def month(in: Date): Int = {
val cal = Calendar.getInstance(utc)
cal.setTimeInMillis(in.getTime)
cal.get(Calendar.MONTH)
}
/** @return the year corresponding to today (relative to UTC) */
def year(in: Date): Int = {
val cal = Calendar.getInstance(utc)
cal.setTimeInMillis(in.getTime)
cal.get(Calendar.YEAR)
}
/** @return the day of month corresponding to the input date (1 based) */
def day(in: Date): Int = {
val cal = Calendar.getInstance(utc)
cal.setTimeInMillis(in.getTime)
cal.get(Calendar.DAY_OF_MONTH)
}
/** The UTC TimeZone */
val utc = TimeZone.getTimeZone("UTC")
/** @return the number of days since epoch converted from millis */
def millisToDays(millis: Long): Long = millis / (1000L * 60L * 60L * 24L)
/** @return the number of days since epoch */
def daysSinceEpoch: Long = millisToDays(millis)
/** @return the time taken to evaluate f in millis and the result */
def calcTime[T](f: => T): (Long, T) = {
val start = millis
val result = f
(millis - start, result)
}
/**
* Log a message with the time taken in millis to do something and return the result
* @return the result
*/
def logTime[T](msg: String)(f: => T): T = {
val (time, ret) = calcTime(f)
logger.info(msg + " took " + time + " Milliseconds")
ret
}
/**
* Call f and log the string returned together with the time taken in millis.
* @return the second result from f
*/
def logTime[T](f: => (String,T)): T = {
val (time, fret) = calcTime(f)
val (msg, ret) = fret
logger.info(msg + " took " + time + " Milliseconds")
ret
}
/**
* @return a standard format HH:mm:ss
*/
def hourFormat = new SimpleDateFormat("HH:mm:ss")
/**
* @return the formatted time for a given Date
*/
def hourFormat(in: Date): String = hourFormat.format(in)
/** @return a standard format for the date yyyy/MM/dd */
def dateFormatter = new SimpleDateFormat("yyyy/MM/dd")
/** @return a format for the time which includes the TimeZone: HH:mm zzz*/
def timeFormatter = new SimpleDateFormat("HH:mm zzz")
/** @return today's date formatted as yyyy/MM/dd */
def formattedDateNow = dateFormatter.format(now)
/** @return now's time formatted as HH:mm zzz */
def formattedTimeNow = timeFormatter.format(now)
/** @return a formatter for internet dates (RFC822/1123) including:
* the day of week, the month, day of month, time and time zone */
def internetDateFormatter = {
val ret = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss 'GMT'", Locale.US)
ret.setTimeZone(utc)
ret
}
/** @return a date from a string using the internet format. Return the Epoch date if the parse is unsuccesfull */
def boxParseInternetDate(dateString: String): Box[Date] = tryo {
internetDateFormatter.parse(dateString)
}
/** @return a date from a string using the internet format. Return the Epoch date if the parse is unsuccesfull */
def parseInternetDate(dateString: String): Date = tryo {
internetDateFormatter.parse(dateString)
} openOr new Date(0L)
/** @return a date formatted with the internet format */
def toInternetDate(in: Date): String = internetDateFormatter.format(in)
/** @return a date formatted with the internet format (from a number of millis) */
def toInternetDate(in: Long): String = internetDateFormatter.format(new Date(in))
/** @return the current time as an internet date */
def nowAsInternetDate: String = toInternetDate(millis)
/** @return a Full(date) or a failure if the input couldn't be translated to date (or Empty if the input is null)*/
def toDate(in: Any): Box[Date] = {
try {
in match {
case null => Empty
case d: Date => Full(d)
case lng: Long => Full(new Date(lng))
case lng: Number => Full(new Date(lng.longValue))
case Nil | Empty | None | Failure(_, _, _) => Empty
case Full(v) => toDate(v)
case Some(v) => toDate(v)
case v :: vs => toDate(v)
case s : String => tryo(internetDateFormatter.parse(s)) or tryo(dateFormatter.parse(s))
case o => toDate(o.toString)
}
} catch {
case e => logger.debug("Error parsing date "+in, e); Failure("Bad date: "+in, Full(e), Empty)
}
}
}
trait ConvertableToDate {
def toDate: Date
def toDateTime: DateTime
def millis: Long
}
object ConvertableToDate {
implicit def toDate(in: ConvertableToDate): Date = in.toDate
implicit def toDateTime(in: ConvertableToDate): DateTime = in.toDateTime
implicit def toMillis(in: ConvertableToDate): Long = in.millis
}
Jump to Line
Something went wrong with that request. Please try again.