declare function moment(inp?: moment.MomentInput, format?: moment.MomentFormatSpecification, strict?: boolean): moment.Moment; declare function moment(inp?: moment.MomentInput, format?: moment.MomentFormatSpecification, language?: string, strict?: boolean): moment.Moment;

declare namespace moment {

type RelativeTimeKey = 's' | 'm' | 'mm' | 'h' | 'hh' | 'd' | 'dd' | 'M' | 'MM' | 'y' | 'yy';
type CalendarKey = 'sameDay' | 'nextDay' | 'lastDay' | 'nextWeek' | 'lastWeek' | 'sameElse' | string;
type LongDateFormatKey = 'LTS' | 'LT' | 'L' | 'LL' | 'LLL' | 'LLLL' | 'lts' | 'lt' | 'l' | 'll' | 'lll' | 'llll';

interface Locale {
  calendar(key?: CalendarKey, m?: Moment, now?: Moment): string;

  longDateFormat(key: LongDateFormatKey): string;
  invalidDate(): string;
  ordinal(n: number): string;

  preparse(inp: string): string;
  postformat(inp: string): string;
  relativeTime(n: number, withoutSuffix: boolean,
               key: RelativeTimeKey, isFuture: boolean): string;
  pastFuture(diff: number, absRelTime: string): string;
  set(config: Object): void;

  months(): string[];
  months(m: Moment, format?: string): string;
  monthsShort(): string[];
  monthsShort(m: Moment, format?: string): string;
  monthsParse(monthName: string, format: string, strict: boolean): number;
  monthsRegex(strict: boolean): RegExp;
  monthsShortRegex(strict: boolean): RegExp;

  week(m: Moment): number;
  firstDayOfYear(): number;
  firstDayOfWeek(): number;

  weekdays(): string[];
  weekdays(m: Moment, format?: string): string;
  weekdaysMin(): string[];
  weekdaysMin(m: Moment): string;
  weekdaysShort(): string[];
  weekdaysShort(m: Moment): string;
  weekdaysParse(weekdayName: string, format: string, strict: boolean): number;
  weekdaysRegex(strict: boolean): RegExp;
  weekdaysShortRegex(strict: boolean): RegExp;
  weekdaysMinRegex(strict: boolean): RegExp;

  isPM(input: string): boolean;
  meridiem(hour: number, minute: number, isLower: boolean): string;
}

interface StandaloneFormatSpec {
  format: string[];
  standalone: string[];
  isFormat?: RegExp;
}

interface WeekSpec {
  dow: number;
  doy: number;
}

type CalendarSpecVal = string | ((m?: MomentInput, now?: Moment) => string);
interface CalendarSpec {
  sameDay?: CalendarSpecVal;
  nextDay?: CalendarSpecVal;
  lastDay?: CalendarSpecVal;
  nextWeek?: CalendarSpecVal;
  lastWeek?: CalendarSpecVal;
  sameElse?: CalendarSpecVal;

  // any additional properties might be used with moment.calendarFormat
  [x: string]: CalendarSpecVal | void; // undefined
}

type RelativeTimeSpecVal = (
  string |
  ((n: number, withoutSuffix: boolean,
    key: RelativeTimeKey, isFuture: boolean) => string)
);
type RelativeTimeFuturePastVal = string | ((relTime: string) => string);

interface RelativeTimeSpec {
  future: RelativeTimeFuturePastVal;
  past: RelativeTimeFuturePastVal;
  s: RelativeTimeSpecVal;
  m: RelativeTimeSpecVal;
  mm: RelativeTimeSpecVal;
  h: RelativeTimeSpecVal;
  hh: RelativeTimeSpecVal;
  d: RelativeTimeSpecVal;
  dd: RelativeTimeSpecVal;
  M: RelativeTimeSpecVal;
  MM: RelativeTimeSpecVal;
  y: RelativeTimeSpecVal;
  yy: RelativeTimeSpecVal;
}

interface LongDateFormatSpec {
  LTS: string;
  LT: string;
  L: string;
  LL: string;
  LLL: string;
  LLLL: string;

  // lets forget for a sec that any upper/lower permutation will also work
  lts?: string;
  lt?: string;
  l?: string;
  ll?: string;
  lll?: string;
  llll?: string;
}

type MonthWeekdayFn = (momentToFormat: Moment, format?: string) => string;
type WeekdaySimpleFn = (momentToFormat: Moment) => string;

interface LocaleSpecification {
  months?: string[] | StandaloneFormatSpec | MonthWeekdayFn;
  monthsShort?: string[] | StandaloneFormatSpec | MonthWeekdayFn;

  weekdays?: string[] | StandaloneFormatSpec | MonthWeekdayFn;
  weekdaysShort?: string[] | StandaloneFormatSpec | WeekdaySimpleFn;
  weekdaysMin?: string[] | StandaloneFormatSpec | WeekdaySimpleFn;

  meridiemParse?: RegExp;
  meridiem?: (hour: number, minute:number, isLower: boolean) => string;

  isPM?: (input: string) => boolean;

  longDateFormat?: LongDateFormatSpec;
  calendar?: CalendarSpec;
  relativeTime?: RelativeTimeSpec;
  invalidDate?: string;
  ordinal?: (n: number) => string;
  ordinalParse?: RegExp;

  week?: WeekSpec;

  // Allow anything: in general any property that is passed as locale spec is
  // put in the locale object so it can be used by locale functions
  [x: string]: any;
}

interface MomentObjectOutput {
  years: number;
  /* One digit */
  months: number;
  /* Day of the month */
  date: number;
  hours: number;
  minutes: number;
  seconds: number;
  milliseconds: number;
}

interface Duration {
  humanize(withSuffix?: boolean): string;

  abs(): Duration;

  as(units: unitOfTime.Base): number;
  get(units: unitOfTime.Base): number;

  milliseconds(): number;
  asMilliseconds(): number;

  seconds(): number;
  asSeconds(): number;

  minutes(): number;
  asMinutes(): number;

  hours(): number;
  asHours(): number;

  days(): number;
  asDays(): number;

  weeks(): number;
  asWeeks(): number;

  months(): number;
  asMonths(): number;

  years(): number;
  asYears(): number;

  add(inp?: DurationInputArg1, unit?: DurationInputArg2): Duration;
  subtract(inp?: DurationInputArg1, unit?: DurationInputArg2): Duration;

  locale(): string;
  locale(locale: LocaleSpecifier): Duration;
  localeData(): Locale;

  toISOString(): string;
  toJSON(): string;

  /**
   * @deprecated since version 2.8.0
   */
  lang(locale: LocaleSpecifier): Moment;
  /**
   * @deprecated since version 2.8.0
   */
  lang(): Locale;
  /**
   * @deprecated
   */
  toIsoString(): string;
}

interface MomentRelativeTime {
  future: any;
  past: any;
  s: any;
  m: any;
  mm: any;
  h: any;
  hh: any;
  d: any;
  dd: any;
  M: any;
  MM: any;
  y: any;
  yy: any;
}

interface MomentLongDateFormat {
  L: string;
  LL: string;
  LLL: string;
  LLLL: string;
  LT: string;
  LTS: string;

  l?: string;
  ll?: string;
  lll?: string;
  llll?: string;
  lt?: string;
  lts?: string;
}

interface MomentParsingFlags {
  empty: boolean;
  unusedTokens: string[];
  unusedInput: string[];
  overflow: number;
  charsLeftOver: number;
  nullInput: boolean;
  invalidMonth: string | void; // null
  invalidFormat: boolean;
  userInvalidated: boolean;
  iso: boolean;
  parsedDateParts: any[];
  meridiem: string | void; // null
}

interface MomentParsingFlagsOpt {
  empty?: boolean;
  unusedTokens?: string[];
  unusedInput?: string[];
  overflow?: number;
  charsLeftOver?: number;
  nullInput?: boolean;
  invalidMonth?: string;
  invalidFormat?: boolean;
  userInvalidated?: boolean;
  iso?: boolean;
  parsedDateParts?: any[];
  meridiem?: string;
}

interface MomentBuiltinFormat {
  __momentBuiltinFormatBrand: any;
}

type MomentFormatSpecification = string | MomentBuiltinFormat | (string | MomentBuiltinFormat)[];

namespace unitOfTime {
  type Base = (
    "year" | "years" | "y" |
    "month" | "months" | "M" |
    "week" | "weeks" | "w" |
    "day" | "days" | "d" |
    "hour" | "hours" | "h" |
    "minute" | "minutes" | "m" |
    "second" | "seconds" | "s" |
    "millisecond" | "milliseconds" | "ms"
  );

  type _quarter = "quarter" | "quarters" | "Q";
  type _isoWeek = "isoWeek" | "isoWeeks" | "W";
  type _date = "date" | "dates" | "D";
  type DurationConstructor = Base | _quarter;

  type DurationAs = Base;

  type StartOf = Base | _quarter | _isoWeek | _date;

  type Diff = Base | _quarter;

  type MomentConstructor = Base | _date;

  type All = Base | _quarter | _isoWeek | _date |
    "weekYear" | "weekYears" | "gg" |
    "isoWeekYear" | "isoWeekYears" | "GG" |
    "dayOfYear" | "dayOfYears" | "DDD" |
    "weekday" | "weekdays" | "e" |
    "isoWeekday" | "isoWeekdays" | "E";
}

interface MomentInputObject {
  years?: number;
  year?: number;
  y?: number;

  months?: number;
  month?: number;
  M?: number;

  days?: number;
  day?: number;
  d?: number;

  dates?: number;
  date?: number;
  D?: number;

  hours?: number;
  hour?: number;
  h?: number;

  minutes?: number;
  minute?: number;
  m?: number;

  seconds?: number;
  second?: number;
  s?: number;

  milliseconds?: number;
  millisecond?: number;
  ms?: number;
}

interface DurationInputObject extends MomentInputObject {
  quarters?: number;
  quarter?: number;
  Q?: number;

  weeks?: number;
  week?: number;
  w?: number;
}

interface MomentSetObject extends MomentInputObject {
  weekYears?: number;
  weekYear?: number;
  gg?: number;

  isoWeekYears?: number;
  isoWeekYear?: number;
  GG?: number;

  quarters?: number;
  quarter?: number;
  Q?: number;

  weeks?: number;
  week?: number;
  w?: number;

  isoWeeks?: number;
  isoWeek?: number;
  W?: number;

  dayOfYears?: number;
  dayOfYear?: number;
  DDD?: number;

  weekdays?: number;
  weekday?: number;
  e?: number;

  isoWeekdays?: number;
  isoWeekday?: number;
  E?: number;
}

interface FromTo {
  from: MomentInput;
  to: MomentInput;
}

type MomentInput = Moment | Date | string | number | (number | string)[] | MomentInputObject | void; // null | undefined
type DurationInputArg1 = Duration | number | string | FromTo | DurationInputObject | void; // null | undefined
type DurationInputArg2 = unitOfTime.DurationConstructor;
type LocaleSpecifier = string | Moment | Duration | string[] | boolean;

interface MomentCreationData {
  input: MomentInput;
  format?: MomentFormatSpecification;
  locale: Locale;
  isUTC: boolean;
  strict?: boolean;
}

interface Moment extends Object{
  format(format?: string): string;

  startOf(unitOfTime: unitOfTime.StartOf): Moment;
  endOf(unitOfTime: unitOfTime.StartOf): Moment;

  add(amount?: DurationInputArg1, unit?: DurationInputArg2): Moment;
  /**
   * @deprecated reverse syntax
   */
  add(unit: unitOfTime.DurationConstructor, amount: number|string): Moment;

  subtract(amount?: DurationInputArg1, unit?: DurationInputArg2): Moment;
  /**
   * @deprecated reverse syntax
   */
  subtract(unit: unitOfTime.DurationConstructor, amount: number|string): Moment;

  calendar(time?: MomentInput, formats?: CalendarSpec): string;

  clone(): Moment;

  /**
   * @return Unix timestamp in milliseconds
   */
  valueOf(): number;

  // current date/time in local mode
  local(keepLocalTime?: boolean): Moment;
  isLocal(): boolean;

  // current date/time in UTC mode
  utc(keepLocalTime?: boolean): Moment;
  isUTC(): boolean;
  /**
   * @deprecated use isUTC
   */
  isUtc(): boolean;

  parseZone(): Moment;
  isValid(): boolean;
  invalidAt(): number;

  hasAlignedHourOffset(other?: MomentInput): boolean;

  creationData(): MomentCreationData;
  parsingFlags(): MomentParsingFlags;

  year(y: number): Moment;
  year(): number;
  /**
   * @deprecated use year(y)
   */
  years(y: number): Moment;
  /**
   * @deprecated use year()
   */
  years(): number;
  quarter(): number;
  quarter(q: number): Moment;
  quarters(): number;
  quarters(q: number): Moment;
  month(M: number|string): Moment;
  month(): number;
  /**
   * @deprecated use month(M)
   */
  months(M: number|string): Moment;
  /**
   * @deprecated use month()
   */
  months(): number;
  day(d: number|string): Moment;
  day(): number;
  days(d: number|string): Moment;
  days(): number;
  date(d: number): Moment;
  date(): number;
  /**
   * @deprecated use date(d)
   */
  dates(d: number): Moment;
  /**
   * @deprecated use date()
   */
  dates(): number;
  hour(h: number): Moment;
  hour(): number;
  hours(h: number): Moment;
  hours(): number;
  minute(m: number): Moment;
  minute(): number;
  minutes(m: number): Moment;
  minutes(): number;
  second(s: number): Moment;
  second(): number;
  seconds(s: number): Moment;
  seconds(): number;
  millisecond(ms: number): Moment;
  millisecond(): number;
  milliseconds(ms: number): Moment;
  milliseconds(): number;
  weekday(): number;
  weekday(d: number): Moment;
  isoWeekday(): number;
  isoWeekday(d: number|string): Moment;
  weekYear(): number;
  weekYear(d: number): Moment;
  isoWeekYear(): number;
  isoWeekYear(d: number): Moment;
  week(): number;
  week(d: number): Moment;
  weeks(): number;
  weeks(d: number): Moment;
  isoWeek(): number;
  isoWeek(d: number): Moment;
  isoWeeks(): number;
  isoWeeks(d: number): Moment;
  weeksInYear(): number;
  isoWeeksInYear(): number;
  dayOfYear(): number;
  dayOfYear(d: number): Moment;

  from(inp: MomentInput, suffix?: boolean): string;
  to(inp: MomentInput, suffix?: boolean): string;
  fromNow(withoutSuffix?: boolean): string;
  toNow(withoutPrefix?: boolean): string;

  diff(b: MomentInput, unitOfTime?: unitOfTime.Diff, precise?: boolean): number;

  toArray(): number[];
  toDate(): Date;
  toISOString(): string;
  inspect(): string;
  toJSON(): string;
  unix(): number;

  isLeapYear(): boolean;
  /**
   * @deprecated in favor of utcOffset
   */
  zone(): number;
  zone(b: number|string): Moment;
  utcOffset(): number;
  utcOffset(b: number|string, keepLocalTime?: boolean): Moment;
  isUtcOffset(): boolean;
  daysInMonth(): number;
  isDST(): boolean;

  zoneAbbr(): string;
  zoneName(): string;

  isBefore(inp?: MomentInput, granularity?: unitOfTime.StartOf): boolean;
  isAfter(inp?: MomentInput, granularity?: unitOfTime.StartOf): boolean;
  isSame(inp?: MomentInput, granularity?: unitOfTime.StartOf): boolean;
  isSameOrAfter(inp?: MomentInput, granularity?: unitOfTime.StartOf): boolean;
  isSameOrBefore(inp?: MomentInput, granularity?: unitOfTime.StartOf): boolean;
  isBetween(a: MomentInput, b: MomentInput, granularity?: unitOfTime.StartOf, inclusivity?: "()" | "[)" | "(]" | "[]"): boolean;

  /**
   * @deprecated as of 2.8.0, use locale
   */
  lang(language: LocaleSpecifier): Moment;
  /**
   * @deprecated as of 2.8.0, use locale
   */
  lang(): Locale;

  locale(): string;
  locale(locale: LocaleSpecifier): Moment;

  localeData(): Locale;

  /**
   * @deprecated no reliable implementation
   */
  isDSTShifted(): boolean;

  // NOTE(constructor): Same as moment constructor
  /**
   * @deprecated as of 2.7.0, use moment.min/max
   */
  max(inp?: MomentInput, format?: MomentFormatSpecification, strict?: boolean): Moment;
  /**
   * @deprecated as of 2.7.0, use moment.min/max
   */
  max(inp?: MomentInput, format?: MomentFormatSpecification, language?: string, strict?: boolean): Moment;

  // NOTE(constructor): Same as moment constructor
  /**
   * @deprecated as of 2.7.0, use moment.min/max
   */
  min(inp?: MomentInput, format?: MomentFormatSpecification, strict?: boolean): Moment;
  /**
   * @deprecated as of 2.7.0, use moment.min/max
   */
  min(inp?: MomentInput, format?: MomentFormatSpecification, language?: string, strict?: boolean): Moment;

  get(unit: unitOfTime.All): number;
  set(unit: unitOfTime.All, value: number): Moment;
  set(objectLiteral: MomentSetObject): Moment;

  toObject(): MomentObjectOutput;
}

export var version: string;
export var fn: Moment;

// NOTE(constructor): Same as moment constructor
export function utc(inp?: MomentInput, format?: MomentFormatSpecification, strict?: boolean): Moment;
export function utc(inp?: MomentInput, format?: MomentFormatSpecification, language?: string, strict?: boolean): Moment;

export function unix(timestamp: number): Moment;

export function invalid(flags?: MomentParsingFlagsOpt): Moment;
export function isMoment(m: any): m is Moment;
export function isDate(m: any): m is Date;
export function isDuration(d: any): d is Duration;

/**
 * @deprecated in 2.8.0
 */
export function lang(language?: string): string;
/**
 * @deprecated in 2.8.0
 */
export function lang(language?: string, definition?: Locale): string;

export function locale(language?: string): string;
export function locale(language?: string[]): string;
export function locale(language?: string, definition?: LocaleSpecification | void): string; // null | undefined

export function localeData(key?: string | string[]): Locale;

export function duration(inp?: DurationInputArg1, unit?: DurationInputArg2): Duration;

// NOTE(constructor): Same as moment constructor
export function parseZone(inp?: MomentInput, format?: MomentFormatSpecification, strict?: boolean): Moment;
export function parseZone(inp?: MomentInput, format?: MomentFormatSpecification, language?: string, strict?: boolean): Moment;

export function months(): string[];
export function months(index: number): string;
export function months(format: string): string[];
export function months(format: string, index: number): string;
export function monthsShort(): string[];
export function monthsShort(index: number): string;
export function monthsShort(format: string): string[];
export function monthsShort(format: string, index: number): string;

export function weekdays(): string[];
export function weekdays(index: number): string;
export function weekdays(format: string): string[];
export function weekdays(format: string, index: number): string;
export function weekdays(localeSorted: boolean): string[];
export function weekdays(localeSorted: boolean, index: number): string;
export function weekdays(localeSorted: boolean, format: string): string[];
export function weekdays(localeSorted: boolean, format: string, index: number): string;
export function weekdaysShort(): string[];
export function weekdaysShort(index: number): string;
export function weekdaysShort(format: string): string[];
export function weekdaysShort(format: string, index: number): string;
export function weekdaysShort(localeSorted: boolean): string[];
export function weekdaysShort(localeSorted: boolean, index: number): string;
export function weekdaysShort(localeSorted: boolean, format: string): string[];
export function weekdaysShort(localeSorted: boolean, format: string, index: number): string;
export function weekdaysMin(): string[];
export function weekdaysMin(index: number): string;
export function weekdaysMin(format: string): string[];
export function weekdaysMin(format: string, index: number): string;
export function weekdaysMin(localeSorted: boolean): string[];
export function weekdaysMin(localeSorted: boolean, index: number): string;
export function weekdaysMin(localeSorted: boolean, format: string): string[];
export function weekdaysMin(localeSorted: boolean, format: string, index: number): string;

export function min(...moments: MomentInput[]): Moment;
export function max(...moments: MomentInput[]): Moment;

/**
 * Returns unix time in milliseconds. Overwrite for profit.
 */
export function now(): number;

export function defineLocale(language: string, localeSpec: LocaleSpecification | void): Locale; // null
export function updateLocale(language: string, localeSpec: LocaleSpecification | void): Locale; // null

export function locales(): string[];

export function normalizeUnits(unit: unitOfTime.All): string;
export function relativeTimeThreshold(threshold: string): number | boolean;
export function relativeTimeThreshold(threshold: string, limit: number): boolean;
export function relativeTimeRounding(fn: (num: number) => number): boolean;
export function relativeTimeRounding(): (num: number) => number;
export function calendarFormat(m: Moment, now: Moment): string;

/**
 * Constant used to enable explicit ISO_8601 format parsing.
 */
export var ISO_8601: MomentBuiltinFormat;

export var defaultFormat: string;
export var defaultFormatUtc: string;

}

export = moment;