Back to Repositories

Validating UTC Date Operations in dayjs

This test suite validates the UTC plugin functionality in Day.js, comparing its behavior with Moment.js for time zone handling and UTC operations. The tests ensure accurate UTC conversions, parsing, and manipulation of dates across different time zones.

Test Coverage Overview

The test suite provides comprehensive coverage of UTC-related functionality, including:
  • UTC getters and setters for date components (year, month, day, etc.)
  • UTC parsing with various date string formats
  • UTC to local time conversions and vice versa
  • UTC offset calculations and manipulations
  • Edge cases with millisecond precision and custom parsing formats

Implementation Analysis

The testing approach uses Jest’s describe/it blocks to organize tests systematically. Each test compares Day.js operations against equivalent Moment.js functionality to ensure compatibility. MockDate is utilized to control date-dependent tests, with proper setup and teardown in beforeEach/afterEach hooks.

Technical Details

Testing tools and configuration:
  • Jest as the testing framework
  • MockDate for date manipulation
  • Custom parse format plugin integration
  • Moment.js for comparison testing
  • UTC plugin extension setup

Best Practices Demonstrated

The test suite exemplifies several testing best practices:
  • Isolated test cases with clear descriptions
  • Consistent comparison with industry standard (Moment.js)
  • Comprehensive edge case coverage
  • Proper test setup and cleanup
  • Modular test organization by functionality

iamkun/dayjs

test/plugin/utc.test.js

            
import MockDate from 'mockdate'
import moment from 'moment'
import dayjs from '../../src'
import customParseFormat from '../../src/plugin/customParseFormat'
import utc from '../../src/plugin/utc'

dayjs.extend(utc)

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

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

describe('UTC Get', () => {
  it('UTC Year', () => {
    expect(dayjs().utc().year()).toBe(moment().utc().year())
  })

  it('UTC Month', () => {
    expect(dayjs().utc().month()).toBe(moment().utc().month())
  })

  it('UTC Day of Week', () => {
    expect(dayjs().utc().day()).toBe(moment().utc().day())
  })

  it('UTC Date', () => {
    expect(dayjs().utc().date()).toBe(moment().utc().date())
  })

  it('UTC Hour', () => {
    expect(dayjs().utc().hour()).toBe(moment().utc().hour())
  })

  it('UTC Minute', () => {
    expect(dayjs().utc().minute()).toBe(moment().utc().minute())
  })

  it('UTC Second', () => {
    expect(dayjs().utc().second()).toBe(moment().utc().second())
  })

  it('UTC Millisecond', () => {
    expect(dayjs().utc().millisecond()).toBe(moment().utc().millisecond())
  })
})

describe('Parse UTC ', () => {
  it('Parse Now', () => {
    expect(dayjs.utc().format()).toBe(moment.utc().format())
    expect(dayjs().utc().format()).toBe(moment().utc().format())
  })

  it('Parse date string without timezone', () => {
    const d = '2018-09-06'
    expect(dayjs.utc(d).format()).toEqual(moment.utc(d).format())
    expect(dayjs.utc(d).format()).toEqual('2018-09-06T00:00:00Z')
    expect(dayjs(d).utc().format()).toEqual(moment(d).utc().format())
    const d2 = '2018-09'
    expect(dayjs.utc(d2).format()).toEqual(moment.utc(d2).format())
    expect(dayjs.utc(d2).format()).toEqual('2018-09-01T00:00:00Z')
    expect(dayjs(d2).utc().format()).toEqual(moment(d2).utc().format())
    const d3 = '2018'
    expect(dayjs.utc(d3).format()).toEqual(moment.utc(d3).format())
    expect(dayjs.utc(d3).format()).toEqual('2018-01-01T00:00:00Z')
    expect(dayjs(d3).utc().format()).toEqual(moment(d3).utc().format())
  })

  it('creating with utc with timezone', () => {
    const d = '2011-02-02T03:04:05+00:00'
    expect(dayjs.utc(d).format()).toEqual(moment.utc(d).format())
    const d2 = '2012-01-02T08:20:00+09:00'
    expect(dayjs.utc(d2).format()).toEqual(moment.utc(d2).format())
  })

  it('Parse date string without timezone', () => {
    const d = '2017-04-22 19:50:16'
    expect(dayjs.utc(d).format()).toEqual('2017-04-22T19:50:16Z')
    expect(dayjs.utc(d).format()).toEqual(moment.utc(d).format())
    expect(dayjs(d).utc().format()).toBe(moment(d).utc().format())
    const d2 = '2012-01-02T08:20:00'
    expect(dayjs.utc(d2).format()).toEqual(moment.utc(d2).format())
  })

  it('Parse date string set utc in config', () => {
    const d = '2018-09-06T19:34:28Z'
    expect(dayjs(d, { utc: true }).format()).toEqual('2018-09-06T19:34:28Z')
    expect(dayjs(d, { utc: true }).format()).toEqual(moment(d).utc().format())
    expect(dayjs(d).utc().format()).toEqual('2018-09-06T19:34:28Z')
    expect(dayjs(d).utc().format()).toEqual(moment(d).utc().format())
    expect(dayjs.utc(d).format()).toEqual('2018-09-06T19:34:28Z')
    expect(dayjs.utc(d).format()).toEqual(moment.utc(d).format())
  })

  it('parses unlimited millisecond in utc', () => {
    const date = '2019-03-25T06:41:00.999999999'
    const ds = dayjs.utc(date)
    const ms = moment.utc(date)
    expect(ds.valueOf()).toEqual(ms.valueOf())
    expect(ds.millisecond()).toEqual(ms.millisecond())
  })
})

it('Clone retains the UTC mode', () => {
  const instance = dayjs('2018-09-06').utc()
  const another = instance.clone()
  expect(another.$u).toBeTruthy()
})

it('UTC mode format tokens', () => {
  const d = '2018-09-06T19:34:28.657Z'
  const instance = dayjs(d).utc()
  const format = 'HH-hh-mm-ss-SSS-Z-ZZ'
  expect(instance.format(format)).toBe('19-07-34-28-657-+00:00-+0000')
  expect(instance.format(format)).toBe(moment.utc(d).format(format))
})

describe('local', () => {
  it('Returns a new instance', () => {
    const instance = dayjs.utc('2018-09-06T19:34:28.657Z')
    const local = instance.local()
    expect(local).not.toBe(instance)
  })

  it('UTC to local', () => {
    const d = '2018-09-06'
    expect(dayjs.utc(d).local().format()).toEqual(moment.utc(d).local().format())
  })
})

it('StartOf EndOf Year ... in UTC mode', () => {
  const testArr = ['year', 'month', 'day', 'date', 'week', 'hour', 'minute', 'second']
  testArr.forEach((d) => {
    expect(dayjs().utc().startOf(d).format()).toBe(moment().utc().startOf(d).format())
    expect(dayjs().utc().endOf(d).format()).toBe(moment().utc().endOf(d).format())
  })
})

describe('UTC Set', () => {
  it('Set UTC Day', () => {
    expect(dayjs().utc().set('date', 30).valueOf()).toBe(moment().utc().set('date', 30).valueOf())
  })

  it('Set UTC Day of Week', () => {
    expect(dayjs().utc().set('day', 0).valueOf()).toBe(moment().utc().set('day', 0).valueOf())
  })

  it('Set UTC Month', () => {
    expect(dayjs().utc().set('month', 11).valueOf()).toBe(moment().utc().set('month', 11).valueOf())
  })

  it('Set UTC Year', () => {
    expect(dayjs().utc().set('year', 2008).valueOf()).toBe(moment().utc().set('year', 2008).valueOf())
  })

  it('Set UTC Hour', () => {
    expect(dayjs().utc().set('hour', 6).valueOf()).toBe(moment().utc().set('hour', 6).valueOf())
  })

  it('Set UTC Minute', () => {
    expect(dayjs().utc().set('minute', 59).valueOf()).toBe(moment().utc().set('minute', 59).valueOf())
  })

  it('Set UTC Second', () => {
    expect(dayjs().utc().set('second', 59).valueOf()).toBe(moment().utc().set('second', 59).valueOf())
  })

  it('Set UTC Millisecond', () => {
    expect(dayjs().utc().set('millisecond', 999).valueOf()).toBe(moment().utc().set('millisecond', 999).valueOf())
  })
})

it('isUTC', () => {
  expect(dayjs().isUTC()).toBe(false)
  expect(dayjs().utc().isUTC()).toBe(true)
  expect(dayjs.utc().isUTC()).toBe(true)
})

describe('UTC and local', () => {
  const localDay = dayjs(Date.UTC(2011, 1, 2, 3, 4, 5, 6))
  const utcDay = localDay.utc()

  it('utc', () => {
    expect(utcDay.date()).toBe(2)
    expect(utcDay.day()).toBe(3)
    expect(utcDay.hour()).toBe(3)
  })

  const localAnainDay = utcDay.local()
  it('local', () => {
    if (localAnainDay.utcOffset() < -180) {
      expect(localAnainDay.date()).toBe(1)
      expect(localAnainDay.day()).toBe(2)
    } else {
      expect(localAnainDay.date()).toBe(2)
      expect(localAnainDay.day()).toBe(3)
    }
  })

  const offset = Math.floor(localAnainDay.utcOffset() / 60)
  const expected = (24 + 3 + offset) % 24
  it('utcOffset', () => {
    expect(localAnainDay.hour()).toBe(expected)
    expect(dayjs().utc().utcOffset()).toBe(0)
  })
})


describe('UTC with customParseFormat', () => {
  it('Custom Parse Format', () => {
    dayjs.extend(customParseFormat)
    const instant = dayjs.utc('2011-02-02 03:04:05', 'YYYY-MM-DD HH:mm:ss')
    const momentInstant = moment.utc('2011-02-02 03:04:05', 'YYYY-MM-DD HH:mm:ss')
    expect(instant.date()).toBe(2)
    expect(instant.hour()).toBe(3)
    expect(instant.format()).toBe('2011-02-02T03:04:05Z')
    expect(instant.format()).toBe(momentInstant.format())
  })
})

describe('UTC Offset', () => {
  it('get utcOffset', () => {
    expect(dayjs().utcOffset()).toBe(moment().utcOffset())
    expect(dayjs().utc().utcOffset()).toBe(moment().utc().utcOffset())
  })

  it('get utc offset with a number value', () => {
    const time = '2021-02-28 19:40:10'
    const hoursOffset = -8
    const daysJS = dayjs(time).utc().utcOffset(hoursOffset * 60, true)
    const momentJS = moment(time).utc(true).utcOffset(hoursOffset, true)

    expect(daysJS.toISOString()).toEqual(momentJS.toISOString())
    expect(daysJS.utcOffset()).toEqual(hoursOffset * 60)
    expect(daysJS.utcOffset()).toEqual(momentJS.utcOffset())
  })

  it('get utc offset with a negative valid string value, format: HH:mm', () => {
    const time = '2021-02-28 19:40:10'
    const hoursOffset = -8
    const daysJS = dayjs(time).utc().utcOffset(`-0${Math.abs(hoursOffset)}:00`, true)
    const momentJS = moment(time).utc(true).utcOffset(`-0${Math.abs(hoursOffset)}:00`, true)

    expect(daysJS.toISOString()).toEqual(momentJS.toISOString())
    expect(daysJS.utcOffset()).toEqual(hoursOffset * 60)
    expect(daysJS.utcOffset()).toEqual(momentJS.utcOffset())
  })

  it('get utc offset with a positive valid string value, format: HH:mm', () => {
    const time = '2021-02-28 19:40:10'
    const hoursOffset = 8
    const daysJS = dayjs(time).utc().utcOffset(`+0${hoursOffset}:00`, true)
    const momentJS = moment(time).utc(true).utcOffset(`+0${hoursOffset}:00`, true)

    expect(daysJS.toISOString()).toEqual(momentJS.toISOString())
    expect(daysJS.utcOffset()).toEqual(hoursOffset * 60)
    expect(daysJS.utcOffset()).toEqual(momentJS.utcOffset())
  })

  it('get utc offset with a negative valid string value, format: HHmm', () => {
    const time = '2021-02-28 19:40:10'
    const hoursOffset = -8
    const daysJS = dayjs(time).utc().utcOffset(`-0${Math.abs(hoursOffset)}00`, true)
    const momentJS = moment(time).utc(true).utcOffset(`-0${Math.abs(hoursOffset)}00`, true)

    expect(daysJS.toISOString()).toEqual(momentJS.toISOString())
    expect(daysJS.utcOffset()).toEqual(hoursOffset * 60)
    expect(daysJS.utcOffset()).toEqual(momentJS.utcOffset())
  })

  it('get utc offset with a positive valid string value, format: HHmm', () => {
    const time = '2021-02-28 19:40:10'
    const hoursOffset = 8
    const daysJS = dayjs(time).utc().utcOffset(`+0${hoursOffset}00`, true)
    const momentJS = moment(time).utc(true).utcOffset(`+0${hoursOffset}00`, true)

    expect(daysJS.toISOString()).toEqual(momentJS.toISOString())
    expect(daysJS.utcOffset()).toEqual(hoursOffset * 60)
    expect(daysJS.utcOffset()).toEqual(momentJS.utcOffset())
  })

  it('get utc offset with an invalid string value, value: random', () => {
    const time = '2021-02-28 19:40:10'
    const daysJS = dayjs(time, { utc: true }).utc(true).utcOffset('random')
    const momentJS = moment(time).utc(true).utcOffset('random')

    expect(daysJS.toISOString()).toEqual(momentJS.toISOString())
    expect(daysJS.utcOffset()).toEqual(0)
    expect(daysJS.utcOffset()).toEqual(momentJS.utcOffset())
  })

  it('get utc offset with an invalid string value, value: 0', () => {
    const time = '2021-02-28 19:40:10'
    const daysJS = dayjs(time, { utc: true }).utc(true).utcOffset('+0000')
    const momentJS = moment(time).utc(true).utcOffset('+0000')

    expect(daysJS.toISOString()).toEqual(momentJS.toISOString())
    expect(daysJS.utcOffset()).toEqual(0)
    expect(daysJS.utcOffset()).toEqual(momentJS.utcOffset())
  })
})

describe('Diff', () => {
  const d1 = '2021-06-07'
  const d2 = '2021-06-06'
  it('utc.diff(utc)', () => {
    [dayjs, moment].forEach((_) => {
      expect(_.utc(d1).diff(_.utc(d2), 'days')).toBe(1)
      expect(_.utc(d1).diff(_.utc(d2), 'm')).toBe(1440)
    })
  })
  it('default diff', () => {
    expect(dayjs().diff()).toBeDefined()
  })
  it('local.diff(utc)', () => {
    expect(dayjs(d1).diff(dayjs.utc(d2), 'days'))
      .toBe(moment(d1).diff(moment.utc(d2), 'days'))
    expect(dayjs(d1).diff(dayjs.utc(d2), 'm'))
      .toBe(moment(d1).diff(moment.utc(d2), 'm'))
  })
  it('utc.diff(local)', () => {
    expect(dayjs.utc(d1).diff(d2, 'days'))
      .toBe(moment.utc(d1).diff(d2, 'days'))
    expect(dayjs.utc(d1).diff(d2, 'm'))
      .toBe(moment.utc(d1).diff(d2, 'm'))
  })
})

it('utc keepLocalTime', () => {
  const t = '2016-05-03 22:15:01'
  const d = dayjs(t).utc(true)
  const m = moment(t).utc(true)
  const fd = d.format()
  const dd = d.toDate()
  const vd = d.valueOf()
  const fm = m.format()
  const dm = m.toDate()
  const vm = m.valueOf()
  expect(fd).toEqual(fm)
  expect(fd).toEqual('2016-05-03T22:15:01Z')
  expect(dd).toEqual(dm)
  expect(vd).toEqual(vm)
})

it('utc diff undefined edge case', () => {
  expect(dayjs().diff(undefined, 'seconds')).toBeDefined()
})