ronantakizawa/codereview-bench huggingface dataset:
Repository: iamkun/dayjs
File: src/plugin/weekOfYear/index.js (JavaScript)

Code before:
import { MS, Y, D, W } from '../../constant'

export default (o, c, d) => {
  const proto = c.prototype
  proto.week = function (week = null) {
    if (week !== null) {
      return this.add((week - this.week()) * 7, 'day')
    }
    const endOfYear = this.endOf(Y)
    if (endOfYear.day() !== 6 && this.month() === 11 && (31 - this.date()) <= endOfYear.day()) {
      return 1
    }
    const startOfYear = d(this.$d).startOf(Y)
    const compareDay = startOfYear.subtract(startOfYear.day(), D).subtract(1, MS)
    const diffInWeek = this.diff(compareDay, W, true)
    return Math.ceil(diffInWeek)
  }
  proto.weeks = function (week = 0) {
    return this.week(week)
  }
}


Review comment (suggestion):
Be aware that, if user call `weeks` alias, without arguments, the default value `week = 0` will set the week to 0. 

```javascript
dayjs('2018-01-01').week() // 1
dayjs('2018-01-01').weeks() // 0
```

You should change the default value to `null`, the same for `week()`
```javascript
proto.weeks = function (week = null) {
    return this.week(week)
}
```

Code after:
import { MS, Y, D, W } from '../../constant'

export default (o, c, d) => {
  const proto = c.prototype
  proto.week = function (week = null) {
    if (week !== null) {
      return this.add((week - this.week()) * 7, 'day')
    }
    const endOfYear = this.endOf(Y)
    if (endOfYear.day() !== 6 && this.month() === 11 && (31 - this.date()) <= endOfYear.day()) {
      return 1
    }
    const startOfYear = d(this.$d).startOf(Y)
    const compareDay = startOfYear.subtract(startOfYear.day(), D).subtract(1, MS)
    const diffInWeek = this.diff(compareDay, W, true)
    return Math.ceil(diffInWeek)
  }
  proto.weeks = function (week = null) {
    return this.week(week)
  }
}
Repository: iamkun/dayjs
File: src/locale/pl.js (JavaScript)

Code before:
import dayjs from 'dayjs'

const locale = {
  name: 'pl',
  weekdays: 'Niedziela_Poniedziałek_Wtorek_Środa_Czwartek_Piątek_Sobota'.split('_'),
  weekdaysMin: 'Nd_Pn_Wt_Śr_Cz_Pt_So'.split('_'),
  months: 'Styczeń_Luty_Marzec_Kwiecień_Maj_Czerwiec_Lipiec_Sierpień_Wrzesień_Październik_Listopad_Grudzień'.split('_'),
  ordinal: n => `${n}.`,
  relativeTime: {
    future: 'za %s',
    past: 'po %s',
    s: 'kilka sekund',
    m: 'minuta',
    mm: '%d minut',
    h: 'godzina',
    hh: '%d godzin',
    d: 'tydzień',
    dd: '%d tygodni',
    M: 'miesiąc',
    MM: '%d miesięcy',
    y: 'rok',
    yy: '%d lat'
  }
}

dayjs.locale(locale, null, true)

export default locale


Review comment (suggestion):
`weekdaysMain` should be added with this PR https://github.com/iamkun/dayjs/pull/392

Code after:
import dayjs from 'dayjs'

const locale = {
  name: 'pl',
  weekdays: 'Niedziela_Poniedziałek_Wtorek_Środa_Czwartek_Piątek_Sobota'.split('_'),
  weekdaysShort: 'Ndz_Pon_Wt_Śr_Czw_Pt_Sob'.split('_'),
  weekdaysMin: 'Nd_Pn_Wt_Śr_Cz_Pt_So'.split('_'),
  months: 'Styczeń_Luty_Marzec_Kwiecień_Maj_Czerwiec_Lipiec_Sierpień_Wrzesień_Październik_Listopad_Grudzień'.split('_'),
  ordinal: n => `${n}.`,
  relativeTime: {
    future: 'za %s',
    past: 'po %s',
    s: 'kilka sekund',
    m: 'minuta',
    mm: '%d minut',
    h: 'godzina',
    hh: '%d godzin',
    d: 'tydzień',
    dd: '%d tygodni',
    M: 'miesiąc',
    MM: '%d miesięcy',
    y: 'rok',
    yy: '%d lat'
  }
}

dayjs.locale(locale, null, true)

export default locale
Repository: iamkun/dayjs
File: src/locale/fa.js (JavaScript)

Code before:
import dayjs from 'dayjs'

const locale = {
  name: 'fa',
  weekdays: 'یک‌شنبه_دوشنبه_سه‌شنبه_چهارشنبه_پنج‌شنبه_جمعه_شنبه'.split('_'),
  months: 'ژانویه_فوریه_مارس_آوریل_مه_ژوئن_ژوئیه_اوت_سپتامبر_اکتبر_نوامبر_دسامبر'.split('_'),
  ordinal: n => n,
  relativeTime: {
    // relative time format strings, keep %s %d as the same
    future: '%s در',
    past: 'پیش %s',
    s: 'چند ثانیه',
    m: 'یک دقیقه',
    mm: 'دقیقه %d',
    h: 'یک ساعت',
    hh: 'ساعت %d',
    d: 'یک روز',
    dd: 'روز %d',
    M: 'یک ماه',
    MM: 'ماه %d',
    y: 'یک سال',
    yy: 'سال %d',
  },
}

dayjs.locale(locale, null, true)

export default locale


Review comment (suggestion):
please remove these two `Unexpected trailing comma`

Code after:
import dayjs from 'dayjs'

const locale = {
  name: 'fa',
  weekdays: 'یک‌شنبه_دوشنبه_سه‌شنبه_چهارشنبه_پنج‌شنبه_جمعه_شنبه'.split('_'),
  months: 'ژانویه_فوریه_مارس_آوریل_مه_ژوئن_ژوئیه_اوت_سپتامبر_اکتبر_نوامبر_دسامبر'.split('_'),
  ordinal: n => n,
  relativeTime: {
    future: '%s در',
    past: 'پیش %s',
    s: 'چند ثانیه',
    m: 'یک دقیقه',
    mm: 'دقیقه %d',
    h: 'یک ساعت',
    hh: 'ساعت %d',
    d: 'یک روز',
    dd: 'روز %d',
    M: 'یک ماه',
    MM: 'ماه %d',
    y: 'یک سال',
    yy: 'سال %d'
  }
}

dayjs.locale(locale, null, true)

export default locale
Repository: iamkun/dayjs
File: types/index.d.ts (TypeScript)

Code before:
export = dayjs;

declare function dayjs (date?: dayjs.DateType, option?: dayjs.OptionType): dayjs.Dayjs

declare namespace dayjs {
  export type DateType = string | number | Date | Dayjs

  export type OptionType = { locale: string }

  export type UnitType = 'millisecond' | 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month' | 'quarter' | 'year' | 'date'

  interface DayjsObject {
    years: number
    months: number
    date: number
    hours: number
    minutes: number
    seconds: number
    milliseconds: number
  }

  class Dayjs {
    constructor (date?: DateType)

    clone(): Dayjs

    isValid(): boolean

    year(): number


Review comment (suggestion):
this could be a breaking change for those, who use TS and rely on semver. I suppose it is better to increase at least minor version, or to not change naming right now at all, but leave comments that it is deprecated and will be replaced in newer versions

Code after:
export = dayjs;
declare function dayjs (date?: dayjs.DateType, option?: dayjs.OptionType): dayjs.Dayjs

declare namespace dayjs {
  export type DateType = string | number | Date | Dayjs

  /** @deprecated Renamed to DateType. */
  export type ConfigType = DateType

  export type OptionType = { locale?: string, format?: string } | string

  type UnitTypeShort = 'd' | 'M' | 'y' | 'h' | 'm' | 's' | 'ms'
  export type UnitType = 'millisecond' | 'second' | 'minute' | 'hour' | 'day' | 'month' | 'quarter' | 'year' | 'date' | UnitTypeShort;

  type OpUnitTypeShort = 'w'
  export type OpUnitType = UnitType | "week" | OpUnitTypeShort;

  interface DayjsObject {
    years: number
    months: number
    date: number
    hours: number
    minutes: number
    seconds: number
    milliseconds: number
  }

  class Dayjs {
    constructor (date?: DateType)
Repository: iamkun/dayjs
File: src/plugin/customParseFormat/index.js (JavaScript)

Code before:
    this[property] = +input
  }
}

const zoneExpressions = [matchOffset, function (input) {
  const zone = this.zone || (this.zone = {})
  zone.offset = offsetFromString(input)
}]

function correctHours(time) {
  const { afternoon } = time
  if (afternoon !== undefined) {
    const { hours } = time
    if (afternoon) {
      if (hours < 12) {
        time.hours += 12
      }
    } else if (hours === 12) {
      time.hours = 0
    }
    delete time.afternoon
  }
}

function makeParser(format, instance) {
  const expressions = {
    A: [matchUpperCaseAMPM, function (input) {
      this.afternoon = input === 'PM'
    }],
    a: [matchLowerCaseAMPM, function (input) {
      this.afternoon = input === 'pm'
    }],
    S: [match1, function (input) {
      this.milliseconds = +input * 100
    }],
    SS: [match2, function (input) {
      this.milliseconds = +input * 10
    }],
    SSS: [match3, function (input) {
      this.milliseconds = +input
    }],
    s: [match1to2, addInput('seconds')],
    ss: [match2, addInput('seconds')],
    m: [match1to2, addInput('minutes')],
    mm: [match2, addInput('minutes')],
    H: [match1to2, addInput('hours')],
    h: [match1to2, addInput('hours')],
    HH: [match2, addInput('hours')],
    hh: [match2, addInput('hours')],
    D: [match1to2, addInput('day')],
    DD: [match2, addInput('day')],

Review comment (performance):
@Kreozot 

Everything else looks fine in this PR, and will be merged into next release.

Please move this object to outer scope for a better performance.

ref #441

Code after:
const addInput = function (property) {
  return function (input) {
    this[property] = +input
  }
}

const zoneExpressions = [matchOffset, function (input) {
  const zone = this.zone || (this.zone = {})
  zone.offset = offsetFromString(input)
}]

const expressions = {
  A: [matchUpperCaseAMPM, function (input) {
    this.afternoon = input === 'PM'
  }],
  a: [matchLowerCaseAMPM, function (input) {
    this.afternoon = input === 'pm'
  }],
  S: [match1, function (input) {
    this.milliseconds = +input * 100
  }],
  SS: [match2, function (input) {
    this.milliseconds = +input * 10
  }],
  SSS: [match3, function (input) {
    this.milliseconds = +input
  }],
  s: [match1to2, addInput('seconds')],
  ss: [match2, addInput('seconds')],
  m: [match1to2, addInput('minutes')],
  mm: [match2, addInput('minutes')],
  H: [match1to2, addInput('hours')],
  h: [match1to2, addInput('hours')],
  HH: [match2, addInput('hours')],
  hh: [match2, addInput('hours')],
  D: [match1to2, addInput('day')],
  DD: [match2, addInput('day')],
  M: [match1to2, addInput('month')],
  MM: [match2, addInput('month')],
  MMM: [matchWord, function (input) {
    const { months, monthsShort } = locale
    const matchIndex = monthsShort
      ? monthsShort.findIndex(month => month === input)
      : months.findIndex(month => month.substr(0, 3) === input)
    if (matchIndex < 0) {
      throw new Error()
    }
    this.month = matchIndex + 1
  }],
  MMMM: [matchWord, function (input) {
    const { months } = locale
Repository: iamkun/dayjs
File: src/plugin/customParseFormat/index.js (JavaScript)

Code before:
  YYYY: [match4, addInput('year')],
  Z: zoneExpressions,
  ZZ: zoneExpressions
}

function correctHours(time) {
  const { afternoon } = time
  if (afternoon !== undefined) {
    const { hours } = time
    if (afternoon) {
      if (hours < 12) {
        time.hours += 12
      }
    } else if (hours === 12) {
      time.hours = 0
    }
    delete time.afternoon
  }
}

function makeParser(format, instance) {
  const expressions = {
    ...baseExpressions,
    MMM: [matchWord, function (input) {
      const locale = instance.$locale()
      const { months, monthsShort } = locale
      const matchIndex = monthsShort
        ? monthsShort.findIndex(month => month === input)
        : months.findIndex(month => month.substr(0, 3) === input)
      this.month = matchIndex >= 0 ? matchIndex + 1 : NaN
    }],
    MMMM: [matchWord, function (input) {
      const locale = instance.$locale()
      const { months } = locale
      const matchIndex = months.indexOf(input)
      this.month = matchIndex >= 0 ? matchIndex + 1 : NaN
    }]
  }

  const array = format.match(formattingTokens)
  const { length } = array
  for (let i = 0; i < length; i += 1) {
    const token = array[i]
    const parseTo = expressions[token]
    const regex = parseTo && parseTo[0]
    const parser = parseTo && parseTo[1]
    if (parser) {
      array[i] = { regex, parser }
    } else {
      array[i] = token.replace(/^\[|\]$/g, '')
    }

Review comment (suggestion):
and move these out of this function,
```
 MMM: [matchWord, function (input) {
      const { months, monthsShort } = locale
```

Code after:
      ? monthsShort.findIndex(month => month === input)
      : months.findIndex(month => month.substr(0, 3) === input)
    if (matchIndex < 0) {
      throw new Error()
    }
    this.month = matchIndex + 1
  }],
  MMMM: [matchWord, function (input) {
    const { months } = locale
    const matchIndex = months.indexOf(input)
    if (matchIndex < 0) {
      throw new Error()
    }
    this.month = matchIndex + 1
  }],
  Y: [matchSigned, addInput('year')],
  YY: [match2, function (input) {
    input = +input
    this.year = input + (input > 68 ? 1900 : 2000)
  }],
  YYYY: [match4, addInput('year')],
  Z: zoneExpressions,
  ZZ: zoneExpressions
}

function correctHours(time) {
  const { afternoon } = time
  if (afternoon !== undefined) {
    const { hours } = time
    if (afternoon) {
      if (hours < 12) {
        time.hours += 12
      }
    } else if (hours === 12) {
      time.hours = 0
    }
    delete time.afternoon
  }
}

function makeParser(format) {
  const array = format.match(formattingTokens)
  const { length } = array
  for (let i = 0; i < length; i += 1) {
    const token = array[i]
    const parseTo = expressions[token]
    const regex = parseTo && parseTo[0]
    const parser = parseTo && parseTo[1]
    if (parser) {
      array[i] = { regex, parser }
    } else {
Repository: iamkun/dayjs
File: src/locale/en-gb.js (JavaScript)

Code before:
    d: 'a day',
    dd: '%d days',
    M: 'a month',
    MM: '%d months',
    y: 'a year',
    yy: '%d years'
  },
  formats: {
    LT: 'HH:mm',
    LTS: 'HH:mm:ss',
    L: 'DD/MM/YYYY',
    LL: 'D MMMM YYYY',
    LLL: 'D MMMM YYYY HH:mm',
    LLLL: 'dddd, D MMMM YYYY HH:mm'
  },
  ordinal(number) {
    const b = number % 10
    let output = 'th'
    if (b === 1) {
      output = 'st'
    } else if (b === 2) {
      output = 'nd'
    } else if (b === 3) {
      output = 'rd'
    }
    return number + output
  }
}

dayjs.locale(locale, null, true)

export default locale


Review comment (suggestion):
ordinal = (number) => {
    const s = ['th', 'st', 'nd', 'rd']
    const v = number % 100
    return `[${number}${(s[(v - 20) % 10] || s[v] || s[0])}]`
  }

This looks better

Code after:
    d: 'a day',
    dd: '%d days',
    M: 'a month',
    MM: '%d months',
    y: 'a year',
    yy: '%d years'
  },
  formats: {
    LT: 'HH:mm',
    LTS: 'HH:mm:ss',
    L: 'DD/MM/YYYY',
    LL: 'D MMMM YYYY',
    LLL: 'D MMMM YYYY HH:mm',
    LLLL: 'dddd, D MMMM YYYY HH:mm'
  },
  ordinal: (n) => {
    const s = ['th', 'st', 'nd', 'rd']
    const v = n % 100
    return `[${n}${(s[(v - 20) % 10] || s[v] || s[0])}]`
  }
}

dayjs.locale(locale, null, true)

export default locale
Repository: iamkun/dayjs
File: src/index.js (JavaScript)

Code before:
  if (!isLocal) L = l
  return l
}

const dayjs = (date, c) => {
  if (isDayjs(date)) {
    return date.clone()
  }
  // eslint-disable-next-line no-nested-ternary
  const cfg = c ? (typeof c === 'string' ? { format: c } : c) : {}
  cfg.date = date
  return new Dayjs(cfg) // eslint-disable-line no-use-before-define
}

const wrapper = (date, instance) => dayjs(date, { locale: instance.$L })

const Utils = U // for plugin use
Utils.parseLocale = parseLocale
Utils.isDayjs = isDayjs
Utils.wrapper = wrapper

const parseDate = (date) => {
  if (date === null) return new Date(NaN) // null is invalid
  if (Utils.isUndefined(date)) return new Date() // today
  if (date instanceof Date) return date
  if (typeof date === 'string' && !date.toLowerCase().endsWith('z')) {
    const d = date.match(C.REGEX_PARSE)
    if (d) {
      return new Date(d[1], d[2] - 1, d[3] || 1, d[4] || 0, d[5] || 0, d[6] || 0, d[7] || 0)
    }
  }

  return new Date(date) // everything else
}

class Dayjs {
  constructor(cfg) {
    this.parse(cfg) // for plugin
  }

  parse(cfg) {
    this.$d = parseDate(cfg.date)
    this.init(cfg)
  }

  init(cfg) {
    const { $d } = this
    this.$y = $d.getFullYear()
    this.$M = $d.getMonth()
    this.$D = $d.getDate()
    this.$W = $d.getDay()

Review comment (suggestion):
Just noticed that `endsWith` is a new added method in js, with cost some more bytes than `/.*[^Z]$/i.test` in the final es5 code. 

Code after:
  if (!isLocal) L = l
  return l
}

const dayjs = (date, c) => {
  if (isDayjs(date)) {
    return date.clone()
  }
  // eslint-disable-next-line no-nested-ternary
  const cfg = c ? (typeof c === 'string' ? { format: c } : c) : {}
  cfg.date = date
  return new Dayjs(cfg) // eslint-disable-line no-use-before-define
}

const wrapper = (date, instance) => dayjs(date, { locale: instance.$L })

const Utils = U // for plugin use
Utils.parseLocale = parseLocale
Utils.isDayjs = isDayjs
Utils.wrapper = wrapper

const parseDate = (date) => {
  if (date === null) return new Date(NaN) // null is invalid
  if (Utils.isUndefined(date)) return new Date() // today
  if (date instanceof Date) return date
  if (typeof date === 'string' && !/Z$/i.test(date)) {
    const d = date.match(C.REGEX_PARSE)
    if (d) {
      return new Date(d[1], d[2] - 1, d[3] || 1, d[4] || 0, d[5] || 0, d[6] || 0, d[7] || 0)
    }
  }

  return new Date(date) // everything else
}

class Dayjs {
  constructor(cfg) {
    this.parse(cfg) // for plugin
  }

  parse(cfg) {
    this.$d = parseDate(cfg.date)
    this.init(cfg)
  }

  init(cfg) {
    const { $d } = this
    this.$y = $d.getFullYear()
    this.$M = $d.getMonth()
    this.$D = $d.getDate()
    this.$W = $d.getDay()
Repository: iamkun/dayjs
File: src/constant.js (JavaScript)

Code before:

export const MILLISECONDS_A_SECOND = 1e3
export const MILLISECONDS_A_MINUTE = SECONDS_A_MINUTE * MILLISECONDS_A_SECOND
export const MILLISECONDS_A_HOUR = SECONDS_A_HOUR * MILLISECONDS_A_SECOND
export const MILLISECONDS_A_DAY = SECONDS_A_DAY * MILLISECONDS_A_SECOND
export const MILLISECONDS_A_WEEK = SECONDS_A_WEEK * MILLISECONDS_A_SECOND

// English locales
export const MS = 'millisecond'
export const S = 'second'
export const MIN = 'minute'
export const H = 'hour'
export const D = 'day'
export const W = 'week'
export const M = 'month'
export const Q = 'quarter'
export const Y = 'year'
export const DATE = 'date'

export const FORMAT_DEFAULT = 'YYYY-MM-DDTHH:mm:ssZ'

export const INVALID_DATE_STRING = 'Invalid Date'

// regex
export const REGEX_PARSE = /^(\d{4})-?(\d{1,2})-?(\d{0,2})[^0-9]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?.?(\d{1,3})?$/
export const REGEX_FORMAT = /(?:\[([^\]]+)])|Y{2,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g


Review comment (refactor):
@mariomc

Can we simplify here as `\[([^\]]+)]` instead of `(?:\[([^\]]+)])` ? 

Code after:

export const MILLISECONDS_A_SECOND = 1e3
export const MILLISECONDS_A_MINUTE = SECONDS_A_MINUTE * MILLISECONDS_A_SECOND
export const MILLISECONDS_A_HOUR = SECONDS_A_HOUR * MILLISECONDS_A_SECOND
export const MILLISECONDS_A_DAY = SECONDS_A_DAY * MILLISECONDS_A_SECOND
export const MILLISECONDS_A_WEEK = SECONDS_A_WEEK * MILLISECONDS_A_SECOND

// English locales
export const MS = 'millisecond'
export const S = 'second'
export const MIN = 'minute'
export const H = 'hour'
export const D = 'day'
export const W = 'week'
export const M = 'month'
export const Q = 'quarter'
export const Y = 'year'
export const DATE = 'date'

export const FORMAT_DEFAULT = 'YYYY-MM-DDTHH:mm:ssZ'

export const INVALID_DATE_STRING = 'Invalid Date'

// regex
export const REGEX_PARSE = /^(\d{4})-?(\d{1,2})-?(\d{0,2})[^0-9]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?.?(\d{1,3})?$/
export const REGEX_FORMAT = /\[([^\]]+)]|Y{2,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g
Repository: iamkun/dayjs
File: test/locale/fi.test.js (JavaScript)

Code before:
import MockDate from 'mockdate'
import dayjs from '../../src'
import relativeTime from '../../src/plugin/relativeTime'
import '../../src/locale/fi'

dayjs.extend(relativeTime)

beforeEach(() => {
  MockDate.set(new Date())
})

afterEach(() => {
  MockDate.reset()
})

it('Finnish locale relative time in past and future', () => {
  expect(dayjs().add(1, 'd').locale('fi').fromNow())
    .toBe('päivän kuluttua')
  expect(dayjs().add(2, 'd').locale('fi').fromNow())
    .toBe('2 päivän kuluttua')
  expect(dayjs().add(2, 'd').locale('fi').fromNow(true))
    .toBe('2 päivää')
  expect(dayjs().add(-1, 'd').locale('fi').fromNow())
    .toBe('päivä sitten')
  expect(dayjs().add(-2, 'd').locale('fi').fromNow())
    .toBe('2 päivää sitten')
})


Review comment (suggestion):
it will be great if you could add moment.js result in test like: 

```
expect(dayjs().add(1, 'd').locale('fi').fromNow())
    .toBe('päivän kuluttua')
expect(dayjs().add(1, 'd').locale('fi').fromNow())
    .toBe(moment().add(1, 'd').locale('fi').fromNow())
```

Code after:
import MockDate from 'mockdate'
import moment from 'moment'
import dayjs from '../../src'
import relativeTime from '../../src/plugin/relativeTime'
import '../../src/locale/fi'

dayjs.extend(relativeTime)

beforeEach(() => {
  MockDate.set(new Date())
})

afterEach(() => {
  MockDate.reset()
})

it('Finnish locale relative time in past and future', () => {
  const cases = [
    [1, 'd', 'päivän päästä'],
    [-1, 'd', 'päivä sitten'],
    [2, 'd', 'kahden päivän päästä'],
    [-2, 'd', 'kaksi päivää sitten'],
    [10, 'd', '10 päivän päästä'],
    [-10, 'd', '10 päivää sitten'],
    [6, 'm', 'kuuden minuutin päästä'],
    [-6, 'm', 'kuusi minuuttia sitten'],
    [5, 'h', 'viiden tunnin päästä'],
    [-5, 'h', 'viisi tuntia sitten'],
    [3, 'M', 'kolmen kuukauden päästä'],
    [-3, 'M', 'kolme kuukautta sitten'],
    [4, 'y', 'neljän vuoden päästä'],
    [-4, 'y', 'neljä vuotta sitten']
  ]
  cases.forEach((c) => {
    expect(dayjs().add(c[0], c[1]).locale('fi').fromNow())
      .toBe(c[2])
    expect(dayjs().add(c[0], c[1]).locale('fi').fromNow())
      .toBe(moment().add(c[0], c[1]).locale('fi').fromNow())
  })
  expect(dayjs().add(-10, 'd').locale('fi').fromNow(true))
    .toBe('10 päivää')
  expect(dayjs().add(-10, 'd').locale('fi').fromNow(true))
    .toBe(moment().add(-10, 'd').locale('fi').fromNow(true))
Repository: iamkun/dayjs
File: src/plugin/relativeTime/index.js (JavaScript)

Code before:
  { l: 'M', r: 1 },
  { l: 'MM', r: 11, d: C.M },
  { l: 'y' },
  { l: 'yy', d: C.Y }
]

export default (o, c, d) => {
  o = o || {}
  const proto = c.prototype
  d.en.relativeTime = {
    future: 'in %s',
    past: '%s ago',
    s: 'a few seconds',
    m: 'a minute',
    mm: '%d minutes',
    h: 'an hour',
    hh: '%d hours',
    d: 'a day',
    dd: '%d days',
    M: 'a month',
    MM: '%d months',
    y: 'a year',
    yy: '%d years'
  }
  if (o.strict) {
    U.x(d.en.relativeTime, {
      s: '%d second', // 0 or 1
      ss: '%d seconds',
      m: '1 minute',
      h: '1 hour',
      d: '1 day',
      M: '1 month',
      y: '1 year'
    })
  }
  const fromTo = (input, withoutSuffix, instance, isFrom) => {
    const loc = instance.$locale().relativeTime
    const T = o.thresholds || (o.strict ? STRICT_THRESHOLDS : LOOSE_THRESHOLDS)
    if (!loc.ss) {
      loc.ss = loc.s // locale like Chinese
    }
    const Tl = T.length
    let result
    let out
    let isFuture

    for (let i = 0; i < Tl; i += 1) {
      let t = T[i]
      if (t.d) {
        result = isFrom
          ? d(input).diff(instance, t.d, true)

Review comment (suggestion):
can we just do 
```
d.en.relativeTime = {
  // and copy all the relevant keys
}
```

Code after:
      { l: 'h', r: 89 },
      { l: 'hh', r: 21, d: C.H },
      { l: 'd', r: 35 },
      { l: 'dd', r: 25, d: C.D },
      { l: 'M', r: 45 },
      { l: 'MM', r: 10, d: C.M },
      { l: 'y', r: 17 },
      { l: 'yy', d: C.Y }
    ]
    const Tl = T.length
    let result
    let out
    let isFuture

    for (let i = 0; i < Tl; i += 1) {
      let t = T[i]
      if (t.d) {
        result = isFrom
          ? d(input).diff(instance, t.d, true)
          : instance.diff(input, t.d, true)
      }
      const abs = (o.rounding || Math.round)(Math.abs(result))
      isFuture = result > 0
      if (abs <= t.r || !t.r) {
        if (abs <= 1 && i > 0) t = T[i - 1] // 1 minutes -> a minute, 0 seconds -> 0 second
        const format = loc[t.l]
        if (typeof format === 'string') {
          out = format.replace('%d', abs)
        } else {
          out = format(abs, withoutSuffix, t.l, isFuture)
        }
        break
      }
    }
    if (withoutSuffix) return out
    return (isFuture ? loc.future : loc.past).replace('%s', out)
  }
  proto.to = function (input, withoutSuffix) {
    return fromTo(input, withoutSuffix, this, true)
  }
  proto.from = function (input, withoutSuffix) {
    return fromTo(input, withoutSuffix, this)
  }

  const makeNow = thisDay => (thisDay.$u ? d.utc() : d())

  proto.toNow = function (withoutSuffix) {
    return this.to(makeNow(this), withoutSuffix)
  }
  proto.fromNow = function (withoutSuffix) {
    return this.from(makeNow(this), withoutSuffix)