diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/AutoCompleteAdapter.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/AutoCompleteAdapter.kt
index 0be424b74c623b863b904c75cbd1c6767a24ed4b..7f295e55e985e74ce0140cc9a12dfd0306466c0b 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/AutoCompleteAdapter.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/AutoCompleteAdapter.kt
@@ -112,11 +112,15 @@ class AutoCompleteAdapter(
         nick.text = SpanFormatter.format("%s%s", data.modes, data.displayNick ?: data.nick)
         realname.text = data.realname
 
-        GlideApp.with(itemView)
-          .load(data.avatarUrl)
-          .apply(RequestOptions.circleCropTransform())
-          .placeholder(data.fallbackDrawable)
-          .into(avatar)
+        if (data.avatarUrl != null) {
+          GlideApp.with(itemView)
+            .load(data.avatarUrl)
+            .apply(RequestOptions.circleCropTransform())
+            .placeholder(data.fallbackDrawable)
+            .into(avatar)
+        } else {
+          avatar.setImageDrawable(data.fallbackDrawable)
+        }
       }
     }
 
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/DayChangeItemDecoration.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/DayChangeItemDecoration.kt
new file mode 100644
index 0000000000000000000000000000000000000000..6605794381a4ab05a5ef9ab36b360570b4eb73d0
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/DayChangeItemDecoration.kt
@@ -0,0 +1,102 @@
+package de.kuschku.quasseldroid.ui.chat.messages
+
+import android.graphics.Canvas
+import android.graphics.Rect
+import android.support.v7.widget.RecyclerView
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import de.kuschku.quasseldroid.R
+import org.threeten.bp.ZoneId
+import org.threeten.bp.format.DateTimeFormatter
+import org.threeten.bp.format.FormatStyle
+import org.threeten.bp.temporal.ChronoUnit
+
+class DayChangeItemDecoration(private val adapter: MessageAdapter) :
+  RecyclerView.ItemDecoration() {
+  private val dayChangeFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)
+  private val mBounds = Rect()
+
+  override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
+    c.save()
+    val left: Int
+    val right: Int
+    if (parent.clipToPadding) {
+      left = parent.paddingLeft
+      right = parent.width - parent.paddingRight
+      c.clipRect(left, parent.paddingTop, right, parent.height - parent.paddingBottom)
+    } else {
+      left = 0
+      right = parent.width
+    }
+
+    val childCount = parent.childCount
+    for (i in 0 until childCount) {
+      val child = parent.getChildAt(i)
+      if (child.getTag(R.id.tag_daychange) == true) {
+        parent.getDecoratedBoundsWithMargins(child, mBounds)
+        val bottom = mBounds.bottom + Math.round(child.translationY)
+        val top = mBounds.top + Math.round(child.translationY)
+        val layout = child.getTag(R.id.tag_daychange_layout) as View
+        c.save()
+        c.clipRect(left.toFloat(), top.toFloat(), right.toFloat(), bottom.toFloat())
+        c.translate(left.toFloat(), top.toFloat())
+        layout.draw(c)
+        c.restore()
+      }
+    }
+    c.restore()
+  }
+
+  private fun fixLayoutSize(view: View, parent: ViewGroup) {
+    val widthSpec = View.MeasureSpec.makeMeasureSpec(
+      parent.width,
+      View.MeasureSpec.EXACTLY
+    )
+    val heightSpec = View.MeasureSpec.makeMeasureSpec(
+      parent.height,
+      View.MeasureSpec.UNSPECIFIED
+    )
+
+    val childWidthSpec = ViewGroup.getChildMeasureSpec(
+      widthSpec,
+      parent.paddingLeft + parent.paddingRight,
+      view.layoutParams.width
+    )
+    val childHeightSpec = ViewGroup.getChildMeasureSpec(
+      heightSpec,
+      parent.paddingTop + parent.paddingBottom,
+      view.layoutParams.height
+    )
+
+    view.measure(childWidthSpec, childHeightSpec)
+    view.layout(0, 0, view.measuredWidth, view.measuredHeight)
+  }
+
+  override fun getItemOffsets(outRect: Rect, v: View, parent: RecyclerView,
+                              state: RecyclerView.State) {
+    adapter[parent.getChildAdapterPosition(v)]?.let {
+      if (it.hasDayChange) {
+        if (v.getTag(R.id.tag_daychange_layout) == null) {
+          val layout = LayoutInflater.from(parent.context).inflate(
+            R.layout.widget_chatmessage_daychange, parent, false
+          )
+          val content = layout.findViewById<TextView>(R.id.combined)
+          content?.text = dayChangeFormatter.format(
+            it.content.time.atZone(ZoneId.systemDefault()).truncatedTo(ChronoUnit.DAYS)
+          )
+          fixLayoutSize(layout, parent)
+
+          v.setTag(R.id.tag_daychange_layout, layout)
+          v.setTag(R.id.tag_daychange_content, content)
+        }
+        v.setTag(R.id.tag_daychange, true)
+        val layout = v.getTag(R.id.tag_daychange_layout) as View
+        outRect.set(0, layout.measuredHeight, 0, 10)
+      } else {
+        v.setTag(R.id.tag_daychange, false)
+      }
+    }
+  }
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/DisplayMessage.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/DisplayMessage.kt
index 8a3f6fb4313e750ff4fb983fc6739658de52b952..ab5633dfad1b70f38f8da2ce702f05dc3cb96807 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/DisplayMessage.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/DisplayMessage.kt
@@ -6,22 +6,25 @@ import de.kuschku.quasseldroid.persistence.QuasselDatabase
 
 data class DisplayMessage(
   val content: QuasselDatabase.DatabaseMessage,
+  val hasDayChange: Boolean,
+  val isFollowUp: Boolean,
   val isSelected: Boolean,
   val isExpanded: Boolean,
   val isMarkerLine: Boolean
 ) {
   data class Tag(
     val id: MsgId,
+    val hasDayChange: Boolean,
     val isFollowUp: Boolean,
     val isSelected: Boolean,
     val isExpanded: Boolean,
     val isMarkerLine: Boolean
   )
 
-  val tag = Tag(content.messageId, content.followUp, isSelected, isExpanded, isMarkerLine)
+  val tag = Tag(content.messageId, hasDayChange, isFollowUp, isSelected, isExpanded, isMarkerLine)
   val avatarUrl = content.sender.let {
     Regex("[us]id(\\d+)").matchEntire(HostmaskHelper.user(it))?.groupValues?.lastOrNull()?.let {
       "https://www.irccloud.com/avatar-redirect/$it"
     }
   }
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageAdapter.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageAdapter.kt
index 191abda65561250227084ce21d69f34b6c33dc9e..0cbe4e1dbdf7ad80dd3752f8975c3afe579c5294 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageAdapter.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageAdapter.kt
@@ -61,7 +61,7 @@ class MessageAdapter(
   override fun getItemViewType(position: Int) = getItem(position)?.let {
     viewType(Message_Flags.of(it.content.type),
              Message_Flags.of(it.content.flag),
-             it.content.followUp)
+             it.isFollowUp)
   } ?: 0
 
   private fun viewType(type: Message_Types, flags: Message_Flags, followUp: Boolean) =
@@ -200,11 +200,15 @@ class MessageAdapter(
       this.itemView.isSelected = message.isSelected
 
       avatar?.let { avatarView ->
-        GlideApp.with(itemView)
-          .load(message.avatarUrl)
-          .apply(RequestOptions.circleCropTransform())
-          .placeholder(message.fallbackDrawable)
-          .into(avatarView)
+        if (message.avatarUrl != null) {
+          GlideApp.with(itemView)
+            .load(message.avatarUrl)
+            .apply(RequestOptions.circleCropTransform())
+            .placeholder(message.fallbackDrawable)
+            .into(avatarView)
+        } else {
+          avatarView.setImageDrawable(message.fallbackDrawable)
+        }
       }
     }
   }
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageListFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageListFragment.kt
index 8d7fa43c87b5216f6dd8240e19647b01f7009ef3..22d11cf696125aa2dcaaf6311bc2bb61999e4e88 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageListFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageListFragment.kt
@@ -26,7 +26,6 @@ import de.kuschku.libquassel.util.helpers.value
 import de.kuschku.quasseldroid.GlideApp
 import de.kuschku.quasseldroid.R
 import de.kuschku.quasseldroid.persistence.QuasselDatabase
-import de.kuschku.quasseldroid.persistence.findByBufferIdPagedWithDayChange
 import de.kuschku.quasseldroid.settings.AppearanceSettings
 import de.kuschku.quasseldroid.settings.BacklogSettings
 import de.kuschku.quasseldroid.settings.MessageSettings
@@ -34,6 +33,9 @@ import de.kuschku.quasseldroid.util.helper.*
 import de.kuschku.quasseldroid.util.service.ServiceBoundFragment
 import de.kuschku.quasseldroid.util.ui.SpanFormatter
 import io.reactivex.BackpressureStrategy
+import org.threeten.bp.ZoneId
+import org.threeten.bp.ZonedDateTime
+import org.threeten.bp.temporal.ChronoUnit
 import java.util.concurrent.TimeUnit
 import javax.inject.Inject
 
@@ -234,11 +236,17 @@ class MessageListFragment : ServiceBoundFragment() {
     fun processMessages(list: List<QuasselDatabase.DatabaseMessage>, selected: Set<MsgId>,
                         expanded: Set<MsgId>, markerLine: MsgId?): List<DisplayMessage> {
       var previous: QuasselDatabase.DatabaseMessage? = null
+      var previousDate: ZonedDateTime? = null
       return list.asReversed().map {
-        it.followUp = previous?.sender == it.sender
+        val date = it.time.atZone(ZoneId.systemDefault()).truncatedTo(ChronoUnit.DAYS)
+        val isSameDay = previousDate?.isEqual(date) ?: false
+        val isFollowUp = previous?.sender == it.sender && isSameDay
         previous = it
+        previousDate = date
         DisplayMessage(
           content = it,
+          hasDayChange = !isSameDay,
+          isFollowUp = isFollowUp,
           isSelected = selected.contains(it.messageId),
           isExpanded = expanded.contains(it.messageId),
           isMarkerLine = markerLine == it.messageId
@@ -253,7 +261,7 @@ class MessageListFragment : ServiceBoundFragment() {
       .toLiveData().switchMapNotNull { (buffer, selected, expanded, markerLine) ->
         database.filtered().listen(accountId, buffer).switchMapNotNull { filtered ->
           LivePagedListBuilder(
-            database.message().findByBufferIdPagedWithDayChange(buffer, filtered).mapByPage {
+            database.message().findByBufferIdPaged(buffer, filtered).mapByPage {
               processMessages(it, selected.keys, expanded, markerLine.orNull())
             },
             PagedList.Config.Builder()
@@ -368,6 +376,7 @@ class MessageListFragment : ServiceBoundFragment() {
     val preloader = RecyclerViewPreloader(Glide.with(this), preloadModelProvider, sizeProvider, 10)
 
     messageList.addOnScrollListener(preloader)
+    messageList.addItemDecoration(DayChangeItemDecoration(adapter))
 
     return view
   }
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/nicks/NickListAdapter.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/nicks/NickListAdapter.kt
index 4f07b965b39b47b1f6a5e5ea785b1e52456c9272..92d862977c2ad2ab9de2deb4775912a3d2f96603 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/nicks/NickListAdapter.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/nicks/NickListAdapter.kt
@@ -86,12 +86,15 @@ class NickListAdapter(
       nick.text = SpanFormatter.format("%s%s", data.modes, data.displayNick ?: data.nick)
       realname.text = data.realname
 
-
-      GlideApp.with(itemView)
-        .load(data.avatarUrl)
-        .apply(RequestOptions.circleCropTransform())
-        .placeholder(data.fallbackDrawable)
-        .into(avatar)
+      if (data.avatarUrl != null) {
+        GlideApp.with(itemView)
+          .load(data.avatarUrl)
+          .apply(RequestOptions.circleCropTransform())
+          .placeholder(data.fallbackDrawable)
+          .into(avatar)
+      } else {
+        avatar.setImageDrawable(data.fallbackDrawable)
+      }
     }
   }
 
@@ -99,4 +102,4 @@ class NickListAdapter(
     const val VIEWTYPE_ACTIVE = 0
     const val VIEWTYPE_AWAY = 1
   }
-}
\ No newline at end of file
+}
diff --git a/app/src/main/res/layout/widget_chatmessage_daychange.xml b/app/src/main/res/layout/widget_chatmessage_daychange.xml
index fedd0454cf76ee91120ce04008f9b84848b08369..eda4795f9550594c9b441a1c8edf5fc559a8f6df 100644
--- a/app/src/main/res/layout/widget_chatmessage_daychange.xml
+++ b/app/src/main/res/layout/widget_chatmessage_daychange.xml
@@ -7,6 +7,10 @@
   android:orientation="vertical"
   android:textAppearance="?android:attr/textAppearanceListItemSmall">
 
+  <Space
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/message_vertical" />
+
   <View
     android:layout_width="match_parent"
     android:layout_height="1dp"
@@ -33,4 +37,4 @@
       android:textStyle="bold"
       tools:text="27.03.2018" />
   </LinearLayout>
-</LinearLayout>
\ No newline at end of file
+</LinearLayout>
diff --git a/app/src/main/res/values/ids.xml b/app/src/main/res/values/ids.xml
new file mode 100644
index 0000000000000000000000000000000000000000..07001df1e193982c5ed30cca5399f9a632325d0b
--- /dev/null
+++ b/app/src/main/res/values/ids.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+  <item name="tag_daychange" type="id" />
+  <item name="tag_daychange_layout" type="id" />
+  <item name="tag_daychange_content" type="id" />
+</resources>
diff --git a/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselBacklogStorage.kt b/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselBacklogStorage.kt
index 7ee165112f725703a92fdf3c16ac4783965331d6..2c79f758b54bd447d84d50170f5814c61261835e 100644
--- a/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselBacklogStorage.kt
+++ b/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselBacklogStorage.kt
@@ -29,8 +29,7 @@ class QuasselBacklogStorage(private val db: QuasselDatabase) : BacklogStorage {
           bufferId = message.bufferInfo.bufferId,
           sender = message.sender,
           senderPrefixes = message.senderPrefixes,
-          content = message.content,
-          followUp = false
+          content = message.content
         )
       )
     }
@@ -48,4 +47,4 @@ class QuasselBacklogStorage(private val db: QuasselDatabase) : BacklogStorage {
     db.message().clearMessages()
   }
 
-}
\ No newline at end of file
+}
diff --git a/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselDatabase.kt b/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselDatabase.kt
index a72a63436ab5a7f722a6cf6678026996ac7397f7..857f984fa85d75a81ec5ca64702dd9eb70521ab4 100644
--- a/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselDatabase.kt
+++ b/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselDatabase.kt
@@ -2,7 +2,6 @@ package de.kuschku.quasseldroid.persistence
 
 import android.arch.lifecycle.LiveData
 import android.arch.paging.DataSource
-import android.arch.persistence.db.SimpleSQLiteQuery
 import android.arch.persistence.db.SupportSQLiteDatabase
 import android.arch.persistence.db.SupportSQLiteQuery
 import android.arch.persistence.room.*
@@ -17,7 +16,7 @@ import de.kuschku.quasseldroid.persistence.QuasselDatabase.Filtered
 import io.reactivex.Flowable
 import org.threeten.bp.Instant
 
-@Database(entities = [DatabaseMessage::class, Filtered::class], version = 4)
+@Database(entities = [DatabaseMessage::class, Filtered::class], version = 5)
 @TypeConverters(DatabaseMessage.MessageTypeConverters::class)
 abstract class QuasselDatabase : RoomDatabase() {
   abstract fun message(): MessageDao
@@ -32,8 +31,7 @@ abstract class QuasselDatabase : RoomDatabase() {
     var bufferId: Int,
     var sender: String,
     var senderPrefixes: String,
-    var content: String,
-    var followUp: Boolean
+    var content: String
   ) {
     class MessageTypeConverters {
       @TypeConverter
@@ -177,6 +175,12 @@ abstract class QuasselDatabase : RoomDatabase() {
                     "ALTER TABLE message ADD followUp INT DEFAULT 0 NOT NULL;"
                   )
                 }
+              },
+              object : Migration(4, 5) {
+                override fun migrate(database: SupportSQLiteDatabase) {
+                  database.execSQL("drop table message;")
+                  database.execSQL("create table message (messageId INTEGER not null primary key, time INTEGER not null, type INTEGER not null, flag INTEGER not null, bufferId INTEGER not null, sender TEXT not null, senderPrefixes TEXT not null, content TEXT not null);")
+                }
               }
             ).build()
           }
@@ -197,93 +201,3 @@ fun QuasselDatabase.MessageDao.clearMessages(
 ) {
   this.clearMessages(bufferId, idRange.first, idRange.last)
 }
-
-fun QuasselDatabase.MessageDao.findByBufferIdPagedWithDayChange(bufferId: Int, type: Int) =
-  this.findMessagesRawPaged(SimpleSQLiteQuery("""
-SELECT t.*
-FROM
-  (
-    SELECT
-      messageId,
-      time,
-      type,
-      flag,
-      bufferId,
-      sender,
-      senderPrefixes,
-      content,
-      followUp
-    FROM message
-    WHERE bufferId = ?
-          AND type & ~? > 0
-    UNION ALL
-    SELECT DISTINCT
-      strftime('%s', date(datetime(time / 1000, 'unixepoch', 'localtime')), 'utc') * -1000 AS messageId,
-      strftime('%s', date(datetime(time / 1000, 'unixepoch', 'localtime')), 'utc') * 1000  AS time,
-      8192                                                                                 AS type,
-      0                                                                                    AS flag,
-      ?                                                                                    AS bufferId,
-      ''                                                                                   AS sender,
-      ''                                                                                   AS senderPrefixes,
-      ''                                                                                   AS content,
-      0                                                                                    AS followUp
-    FROM message
-    WHERE bufferId = ?
-          AND type & ~? > 0
-  ) t
-ORDER BY TIME
-  DESC, messageId
-  DESC
-  """, arrayOf(bufferId, type, bufferId, bufferId, type)))
-
-fun QuasselDatabase.MessageDao.findByBufferIdPagedWithDayChangeSlow(bufferId: Int, type: Int) =
-  this.findMessagesRawPaged(SimpleSQLiteQuery("""
-SELECT t.*
-FROM
-  (
-    SELECT
-      messageId,
-      time,
-      type,
-      flag,
-      bufferId,
-      sender,
-      senderPrefixes,
-      content,
-      (SELECT 1
-       FROM
-         (SELECT *
-          FROM message m
-          WHERE m.messageId < message.messageId
-                AND bufferId = ?
-                AND type & ~? > 0
-          ORDER BY m.messageId
-            DESC
-          LIMIT 1) t
-       WHERE t.sender = message.sender
-             AND strftime('%s', date(datetime(t.time / 1000, 'unixepoch', 'localtime')), 'utc') * 1000 =
-                 strftime('%s', date(datetime(message.time / 1000, 'unixepoch', 'localtime')), 'utc') * 1000
-             AND t.type = message.type
-      ) = 1 AS followUp
-    FROM message
-    WHERE bufferId = ?
-          AND type & ~? > 0
-    UNION ALL
-    SELECT DISTINCT
-      strftime('%s', date(datetime(time / 1000, 'unixepoch', 'localtime')), 'utc') * -1000 AS messageId,
-      strftime('%s', date(datetime(time / 1000, 'unixepoch', 'localtime')), 'utc') * 1000  AS time,
-      8192                                                                                 AS type,
-      0                                                                                    AS flag,
-      ?                                                                                    AS bufferId,
-      ''                                                                                   AS sender,
-      ''                                                                                   AS senderPrefixes,
-      ''                                                                                   AS content,
-      0                                                                                    AS followUp
-    FROM message
-    WHERE bufferId = ?
-          AND type & ~? > 0
-  ) t
-ORDER BY TIME
-  DESC, messageId
-  DESC
-  """, arrayOf(bufferId, type, bufferId, type, bufferId, bufferId, type)))