Last active
July 6, 2022 04:22
-
-
Save nimaiwalsh/e3fe152a6ed07e40c00e8b4e40e9b388 to your computer and use it in GitHub Desktop.
DateFormatter - Custom date and time formatter implementing custom formatting rules
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.content.Context | |
import java.time.temporal.ChronoField.DAY_OF_YEAR | |
import java.time.temporal.ChronoField.INSTANT_SECONDS | |
import java.time.temporal.ChronoField.YEAR | |
import java.time.temporal.TemporalAccessor | |
import java.util.Locale | |
import javax.inject.Inject | |
/** | |
* Custom date and time formatter implementing custom formatting rules. | |
* For more information on the rules | |
* [see here][https://www.figma.com/file/UvEcD4fWXvkuJnBbaw4EhRxs/Design-System-Mobile-Library?node-id=1414%3A4062]. | |
*/ | |
class DateFormatter @Inject constructor( | |
val context: Context, | |
/** The selected [Locale] to use for this DateFormatter instance */ | |
val locale: Locale, | |
/** The date & time format to be used */ | |
private val formatProvider: DateTimePatternProvider, | |
) { | |
// Long date format | |
private val longDateFormat = formatProvider.getDateTimeFormatterForSkeletonPattern(locale, "EEE MMM d yyyy") | |
// Short date format (omits the year) | |
private val dateFormat = formatProvider.getDateTimeFormatterForSkeletonPattern(locale, "EEE MMM d") | |
// Time format driven purely by locale | |
private val timeFormat = formatProvider.getDateTimeFormatterForSkeletonPattern(locale, "jmma") | |
// Time format used when the 24-hour setting is enabled (i.e. overrides the locale format) | |
private val timeFormat24Hour = formatProvider.getDateTimeFormatterForSkeletonPattern(locale, "HH:mm") | |
companion object { | |
const val DATE_TIME_SEPARATOR = ", " | |
const val RANGE_SEPARATOR = " - " | |
} | |
/** @return the formatted time component of the given temporal value, e.g "10:00am", "1:00pm", "19:15" */ | |
fun formatTime(temporal: TemporalAccessor): String { | |
return if (formatProvider.is24HourFormat(context)) { | |
timeFormat24Hour.format(temporal) | |
} else { | |
timeFormat.format(temporal) | |
} | |
} | |
/** | |
* @return the formatted time range given the start and end temporal values, | |
* e.g "8:00am - 9:45pm", 11:15am - 1:00pm | |
*/ | |
fun formatTimeRange(startTemporal: TemporalAccessor, endTemporal: TemporalAccessor): String { | |
require(startTemporal.getInstantSeconds() <= endTemporal.getInstantSeconds()) { | |
"startTemporal must be before endTemporal" | |
} | |
return "${formatTime(startTemporal)}$RANGE_SEPARATOR${formatTime(endTemporal)}" | |
} | |
/** @return the formatted date component of the given temporal value, e.g "Fri Jan 1 */ | |
fun formatDate(temporal: TemporalAccessor): String { | |
return dateFormat.format(temporal) | |
} | |
/** */ | |
fun formatLongDateAndTime(temporal: TemporalAccessor): String { | |
return "${longDateFormat.format(temporal)}$DATE_TIME_SEPARATOR${formatTime(temporal)}" | |
} | |
/** @return the formatted date and time components of the given temporal value, e.g. "Fri Jan 1, 3:30pm" */ | |
fun formatDateAndTime(temporal: TemporalAccessor): String { | |
return "${formatDate(temporal)}$DATE_TIME_SEPARATOR${formatTime(temporal)}" | |
} | |
/** | |
* @return the formatted date and time range of the given start and end temporal values, | |
* e.g "Wed Mar 3, 8am - 9am", "Thu Apr 14, 9:30pm - Fri Apr 15, 6am" | |
*/ | |
fun formatDateAndTimeRange(startTemporal: TemporalAccessor, endTemporal: TemporalAccessor): String { | |
require(startTemporal.getInstantSeconds() <= endTemporal.getInstantSeconds()) { | |
"startTemporal must be before endTemporal" | |
} | |
val isMultiDay = startTemporal[DAY_OF_YEAR] != endTemporal[DAY_OF_YEAR] || | |
startTemporal[YEAR] != endTemporal[YEAR] | |
val startString = formatDateAndTime(startTemporal) | |
val endString = if (isMultiDay) { | |
formatDateAndTime(endTemporal) | |
} else { | |
formatTime(endTemporal) | |
} | |
return "$startString$RANGE_SEPARATOR$endString" | |
} | |
} | |
/** @return the [INSTANT_SECONDS] value of this [TemporalAccessor] */ | |
private fun TemporalAccessor.getInstantSeconds(): Long = getLong(INSTANT_SECONDS) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.content.Context | |
import android.text.format.DateFormat | |
import com.skedulo.core.di.ApplicationScope | |
import com.squareup.anvil.annotations.ContributesBinding | |
import java.time.format.DateTimeFormatter | |
import java.util.Locale | |
import javax.inject.Inject | |
interface DateTimePatternProvider { | |
fun getDateTimeFormatterForSkeletonPattern(locale: Locale, skeleton: String): DateTimeFormatter | |
fun is24HourFormat(context: Context): Boolean | |
} | |
@ContributesBinding(ApplicationScope::class) | |
class DateTimePatternProviderImpl @Inject constructor() : DateTimePatternProvider { | |
override fun getDateTimeFormatterForSkeletonPattern(locale: Locale, skeleton: String): DateTimeFormatter { | |
return DateTimeFormatter.ofPattern( | |
DateFormat.getBestDateTimePattern(locale, skeleton) | |
) | |
} | |
override fun is24HourFormat(context: Context): Boolean { | |
return DateFormat.is24HourFormat(context) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment