├── .editorconfig ├── .github └── ISSUE_TEMPLATE.md ├── .gitignore ├── .husky └── pre-commit ├── .travis.yml ├── .vscode └── settings.json ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── angular.json ├── jest.config.js ├── ng-package.json ├── ng-package.prod.json ├── package-lock.json ├── package.json ├── prettier.config.js ├── setupJest.ts ├── src ├── add.pipe.spec.ts ├── add.pipe.ts ├── calendar.pipe.spec.ts ├── calendar.pipe.ts ├── date-format.pipe.spec.ts ├── date-format.pipe.ts ├── difference.pipe.spec.ts ├── difference.pipe.ts ├── duration.pipe.spec.ts ├── duration.pipe.ts ├── from-unix.pipe.spec.ts ├── from-unix.pipe.ts ├── from-utc.pipe.spec.ts ├── from-utc.pipe.ts ├── index.ts ├── is-after.pipe.spec.ts ├── is-after.pipe.ts ├── is-before.pipe.spec.ts ├── is-before.pipe.ts ├── local.pipe.spec.ts ├── local.pipe.ts ├── locale.pipe.spec.ts ├── locale.pipe.ts ├── moment-options.ts ├── moment.module.ts ├── parse-zone.pipe.spec.ts ├── parse-zone.pipe.ts ├── parse.pipe.spec.ts ├── parse.pipe.ts ├── subtract.pipe.spec.ts ├── subtract.pipe.ts ├── time-ago.pipe.spec.ts ├── time-ago.pipe.ts ├── utc.pipe.spec.ts └── utc.pipe.ts ├── tsconfig.json ├── tsconfig.lint.json ├── tsconfig.spec.json └── tslint.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | 9 | # Change these settings to your own preference 10 | indent_style = space 11 | indent_size = 2 12 | end_of_line = lf 13 | charset = utf-8 14 | trim_trailing_whitespace = true 15 | insert_final_newline = true 16 | 17 | [*.md] 18 | trim_trailing_whitespace = false 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | **Description of the Issue and Steps to Reproduce:** 2 | 3 | Did you search for duplicate issue? [Yes / No] 4 | 5 | Please describe the issue and steps to reproduce, preferably with a code sample / plunker: 6 | 7 | 8 | 9 | 10 | 11 | 12 | *Ensure your issue is isolated to ngx-moment. Issues involving third party tools will be closed unless submitted by the tool's author/maintainer.* 13 | 14 | **Environment:** 15 | 16 | Please answer the following questions: 17 | 18 | * Angular version? 19 | * TypeScript version? 20 | * `moment` version? 21 | * Are you using `moment-timezone`? 22 | * Are you using the angular-cli? 23 | * Using Rollup/Webpack/System.js/Ionic/similar? 24 | 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .history 2 | .idea 3 | node_modules 4 | .angular 5 | *.log 6 | dist 7 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npm test 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: false 3 | node_js: 4 | - "10" 5 | - "12" 6 | - "14" 7 | env: 8 | - TZ=utc 9 | - TZ=Etc/GMT-14 10 | - TZ=Etc/GMT+12 11 | - TZ=Asia/Katmandu 12 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "**/*.js": { 5 | "when": "$(basename).ts" 6 | }, 7 | "**/*.js.map": true, 8 | "**/*.d.ts": { 9 | "when": "$(basename).ts" 10 | } 11 | } 12 | , 13 | "typescript.tsdk": "./node_modules/typescript/lib" 14 | } 15 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 6.0.2 - 2021-12-22 4 | - Fix moment import via jsnext entry point (use default import interop) (see [#245](https://github.com/urish/ngx-moment/issues/245)) 5 | 6 | ## 6.0.1 - 2021-12-20 7 | - Fix build issue (see [#273](https://github.com/urish/ngx-moment/issues/273))_ 8 | 9 | ## 6.0.0 - 2021-11-30 10 | - Support for Angular 13 + Ivy 11 | 12 | ## 5.0.0 - 2020-07-03 13 | - fix: support for Angular 10 (see [#240](https://github.com/urish/ngx-moment/issues/240)) 14 | - breaking: drop support for Angular < 7 15 | 16 | ## 4.0.1 - 2020-06-12 17 | - republish to npm without ngcc backup files 18 | 19 | ## 4.0.0 - 2020-06-12 20 | - fix: correct input types for pipes 906e40c 21 | - chore(deps): angular 9, typescript 3.8 9966a9a 22 | - chore: reformat all code with prettier 10fe5c51 23 | - docs: remove system.js instructions from README 24 | 25 | ## 3.5.0 - 2019-11-08 26 | - feat: add `formatFn` argument to `amTimeAgo` ([#213](https://github.com/urish/ngx-moment/pull/213), contributed by [chaoyangnz](https://github.com/chaoyangnz)) 27 | - feat: add `format|formats` to from-utc pipe and parse-pipe ([#215](https://github.com/urish/ngx-moment/pull/215), contributed by [gigadie](https://github.com/gigadie)) 28 | - fix: improve `amLocale` compability with Angular Ivy ([#226](https://github.com/urish/ngx-moment/pull/226), contributed by [sobanieca](https://github.com/sobanieca)) 29 | 30 | ## 3.4.0 - 2019-03-07 31 | - feat: add amIsBefore, amIsAfter pipes ([#208](https://github.com/urish/ngx-moment/pull/208), contributed by [StickNitro](https://github.com/StickNitro)) 32 | - Ability to provide options to the MomentModule ([#209](https://github.com/urish/ngx-moment/pull/209), contributed by [StickNitro](https://github.com/StickNitro)) 33 | - fix: Improved Text Update on locale change when not using MomentInput ([#210](https://github.com/urish/ngx-moment/pull/210), contributed by [jensweigele](https://github.com/jensweigele)) 34 | 35 | ## 3.3.0 - 2018-12-09 36 | - fix: Allow using all supported Moment inputs with TimeAgoPipe ([#206](https://github.com/urish/ngx-moment/pull/206), contributed by [theodorejb](https://github.com/theodorejb)) 37 | 38 | ## 3.2.0 - 2018-10-30 39 | - Angular 7 support ([#203](https://github.com/urish/ngx-moment/issues/203)) 40 | 41 | ## 3.1.0 - 2018-07-01 42 | - Add 'amParseZone' pipe ([#198](https://github.com/urish/ngx-moment/pull/198), contributed by [davidballester](https://github.com/davidballester)) 43 | 44 | ## 3.0.1 - 2018-06-07 45 | - Update installation instruction in readme (see [#194](https://github.com/urish/ngx-moment/issues/194)) 46 | 47 | ## 3.0.0 - 2018-06-02 48 | - Make `moment` a peerDependency instead of dependency (see [#149](https://github.com/urish/ngx-moment/issues/149) for discussion) 49 | 50 | When upgrading to this version, make sure to `npm install --save moment`. 51 | 52 | ## 2.0.0 - 2018-04-25 53 | - Rename package to ngx-moment 54 | - Migrate project to the Angular CLI 55 | - Switch to [Angular Package Format](https://docs.google.com/document/d/1CZC2rcpxffTDfRDs6p1cfbmKNLA6x5O-NtkJglDaBVs/edit), (using [ngPackagr](http://spektrakel.de/ng-packagr/)) 56 | 57 | 2.0.0-beta.0 and 2.0.0-rc.0 are aliases for this version. 58 | 59 | ## 1.9.0 - 2018-05-03 60 | - Fix: update momentjs version ([#190](https://github.com/urish/ngx-moment/issues/190)) 61 | - Fix: amTimeAgo pipe updates it's output when locale changes ([#188](https://github.com/urish/ngx-moment/pull/188), contributed by [lukasz-kusnierz](https://github.com/lukasz-kusnierz)) 62 | 63 | ## 1.8.0 - 2018-01-23 64 | - Fix: Remove `node_modules` from compiled version ([#187](https://github.com/urish/ngx-moment/pull/187), contributed by [wachri](https://github.com/wachri)) 65 | 66 | ## 1.7.1 - 2017-12-22 67 | - Fix bug where FromUtcPipe was being imported instead of exported ([#180](https://github.com/urish/ngx-moment/pull/180), contributed by [fshin123](https://github.com/fshin123)) 68 | - test: fix amLocal tests to pass regardless of timezone 69 | 70 | ## 1.7.0 - 2017-08-19 71 | - Add `amFromUtc` pipe ([#163](https://github.com/urish/ngx-moment/pull/163), contributed by [connormlewis](https://github.com/connormlewis)) 72 | 73 | ## 1.6.0 - 2017-07-18 74 | - Add `amLocal` pipe ([#153](https://github.com/urish/ngx-moment/pull/153), contributed by [benwilkins](https://github.com/benwilkins)) 75 | 76 | ## 1.5.0 - 2017-07-14 77 | - Add `amLocale` pipe ([#155](https://github.com/urish/ngx-moment/pull/155), contributed by [FallenRiteMonk](https://github.com/FallenRiteMonk)) 78 | - Migrate testing framework to jest 79 | 80 | ## 1.4.0 - 2017-06-18 81 | - Add `amParse` pipe to enable parsing of custom-formatted date string ([#148](https://github.com/urish/ngx-moment/pull/148), contributed by [vin-car](https://github.com/vin-car)) 82 | 83 | ## 1.3.3 - 2017-03-18 84 | - Fix: `amCalendar` causes protractor to timeout on waiting async Angular ([#135](https://github.com/urish/ngx-moment/pull/135), contributed by [romanovma](https://github.com/romanovma)) 85 | 86 | ## 1.3.2 - 2017-03-17 87 | - Fix: Add missing `amAdd` and `amSubtract` pipes to the NgModule ([#134](https://github.com/urish/ngx-moment/pull/134), contributed by [datencia](https://github.com/datencia)) 88 | 89 | ## 1.3.1 - 2017-03-16 90 | - Add missing `amAdd` and `amSubtract` pipes (fixes [#130](https://github.com/urish/ngx-moment/issues/130)) 91 | 92 | ## 1.3.0 - 2017-03-10 93 | - Enable Angular 4 as peer dependency 94 | 95 | ## 1.2.0 - 2017-02-09 96 | - Add `amUtc` pipe ([#121](https://github.com/urish/ngx-moment/pull/121), contributed by [bodnarbm](https://github.com/bodnarbm)) 97 | 98 | ## 1.1.0 - 2017-01-09 99 | Happy new year! 100 | 101 | - Add `referenceTime` and `format` args to `amCalendar` ([#64](https://github.com/urish/ngx-moment/pull/64), contributed by [irsick](https://github.com/irsick)) 102 | - Add `amAdd` and `amSubtract` pipes ([#113](https://github.com/urish/ngx-moment/pull/113), contributed by [dustin486](https://github.com/dustin486)) 103 | - Fix: Do not import whole Rx.js library ([#117](https://github.com/urish/ngx-moment/pull/117), contributed by [FabienDehopre](https://github.com/FabienDehopre)) 104 | 105 | ## 1.0.0 - 2016-12-01 106 | Promoted 1.0.0-rc.1 to final release 107 | 108 | ## 1.0.0-rc.1 - 2016-11-11 109 | *** Breaking change: Requires moment 2.16.0 or newer 110 | 111 | - Fix “Expression has changed after it was checked” ([#111](https://github.com/urish/ngx-moment/pull/111), contributed by [nithril](https://github.com/nithril)) 112 | - Fix "Module 'moment' has no exported member 'UnitOfTime'" ([#112](https://github.com/urish/ngx-moment/issues/112)) 113 | 114 | ## 1.0.0-beta.6 - 2016-10-24 115 | *** Breaking change: typescript sources are no longer published in the npm package 116 | 117 | - Inline sources in the source map file, should fix [#96](https://github.com/urish/ngx-moment/issues/96). 118 | - Handle undefined dates in `amDateFormat` pipe ([#105](https://github.com/urish/ngx-moment/pull/105/files), contributed by [amcdnl](https://github.com/amcdnl)) 119 | 120 | ## 1.0.0-beta.5 - 2016-10-13 121 | 122 | *** Breaking change: source files renamed, which could affect your imports: 123 | 124 | import { TimeAgoPipe } from 'angular-moment/TimeAgoPipe'; 125 | 126 | now becomes: 127 | 128 | import { TimeAgoPipe } from 'angular-moment/time-ago.pipe'; 129 | 130 | All changes: 131 | 132 | - Rename source files to follow [Angular 2 Style Guide conventions](https://angular.io/styleguide#!#02-02) 133 | - Require `moment` >= 2.13.0, and remove `@types/moment` from our dependencies (as it is already included in `moment`) 134 | 135 | ## 1.0.0-beta.4 - 2016-10-06 136 | - Add support for server side pre-rendering ([#89](https://github.com/urish/ngx-moment/pull/89), contributed by [https://github.com/jmezach](https://github.com/jmezach)) 137 | - Fix a bug caused TimeAgo and Calendar pipes not to update automatically ([#94](https://github.com/urish/ngx-moment/pull/94)) 138 | - Add `@types/moment` to package dependencies (see [#91](https://github.com/urish/ngx-moment/issues/91)) 139 | 140 | ## 1.0.0-beta.3 - 2016-10-04 141 | - Fix exports for Rollup / Ionic 2 users ([#86](https://github.com/urish/ngx-moment/pull/86), contributed by [TheMadBug](https://github.com/TheMadBug)) 142 | - Protractor fix: run long standing timeouts outside of angular zones ([#74](https://github.com/urish/ngx-moment/pull/74), contributed by [tiagoroldao](https://github.com/tiagoroldao)) 143 | 144 | ## 1.0.0-beta.2 - 2016-10-01 145 | - Switch to Typescript 2.0 146 | - Angular 2 AoT (Ahead of Time) template compilation support ([#68](https://github.com/urish/ngx-moment/issues/68)) 147 | - Removed impure flags from pure Pipes: `amDateFormat` and `amDifference` ([#75](https://github.com/urish/ngx-moment/pull/75), contributed by [tiagoroldao](https://github.com/tiagoroldao)) 148 | 149 | ## 1.0.0-beta.1 - 2016-08-16 150 | - Support angular-2.0.0-rc.5 NgModules, see [README](README.md) for details. 151 | 152 | ## 0.8.2 - 2016-08-01 153 | - Add `amDifference` pipe ([#54](https://github.com/urish/ngx-moment/pull/54), contributed by [josx](https://github.com/josx)) 154 | 155 | ## 0.8.1 - 2016-07-03 156 | - Add `omitSuffix` parameter to `amTimeAgo` pipe ([#47](https://github.com/urish/ngx-moment/pull/47), contributed by [bzums](https://github.com/bzums)) 157 | 158 | ## 0.8.0 - 2016-05-22 159 | - Publish typescript sources under `src` folder, should fix Ionic 2 issues such as [#28](https://github.com/urish/ngx-moment/issues/28) and [#33](https://github.com/urish/ngx-moment/issues/33). 160 | 161 | ## 0.7.0 - 2016-05-03 162 | - Align with the angular 2.0.0-rc.0 and the new angular packaging system 163 | 164 | ## 0.6.0 - 2016-04-28 165 | - Align with angular 2.0.0-beta.16 ([#32](https://github.com/urish/ngx-moment/pull/32), contributed by [fknop](https://github.com/fknop)) 166 | 167 | ## 0.5.0 - 2016-04-08 168 | - Move `angular2` from npm `dependencies` to `peerDependencies` (see [#24](https://github.com/urish/ngx-moment/pull/24)) 169 | - Add `amDuration` pipe ([#29](https://github.com/urish/ngx-moment/pull/29), contributed by [xenolinguist](https://github.com/xenolinguist)) 170 | 171 | ## 0.4.3 - 2016-03-06 172 | - include `amFromUnix` pipe in the package's exports 173 | - publish our `typings.json` to npm 174 | 175 | ## 0.4.2 - 2016-02-24 176 | - add `amFromUnix` pipe ([#16](https://github.com/urish/ngx-moment/pull/16), contributed by [lanocturne](https://github.com/lanocturne)) 177 | 178 | ## 0.4.1 - 2016-02-21 179 | - Don't run `typings install` on postinstall (fixes [#13](https://github.com/urish/ngx-moment/issues/13)) 180 | 181 | ## 0.4.0 - 2016-02-16 182 | - Switch from `tsd` to `typings`, stop publishing the `moment.js` typings to npm. 183 | - Additional unit-tests 184 | 185 | Note: You may need to manually install moment.js typings, by running `typings install --save moment` in your project directory. 186 | 187 | ## 0.3.0 - 2016-01-27 188 | - add `amDateFormat` pipe ([#9](https://github.com/urish/ngx-moment/pull/9), contributed by [andreialecu](https://github.com/andreialecu)) 189 | - refactor: remove the `supports()` from all the pipes (it is no longer used as of angular2-beta) 190 | 191 | ## 0.2.1 - 2016-01-16 192 | - bugfix: wrong method name for cleanup, caused resource leak ([#8](https://github.com/urish/ngx-moment/pull/8), contributed by [andreialecu](https://github.com/andreialecu)) 193 | 194 | ## 0.2.0 - 2016-01-12 195 | - add `amCalendar` pipe ([#6](https://github.com/urish/ngx-moment/pull/6), contributed by [andreialecu](https://github.com/andreialecu)) 196 | 197 | ## 0.1.1 - 2015-12-18 198 | - Fix 'Cannot use in app due to triple-slash references' typescript error ([#2](https://github.com/urish/ngx-moment/issues/2)) 199 | 200 | ## 0.1.0 - 2015-12-15 201 | - Align with angular 2.0.0-beta.0 202 | 203 | ## 0.0.5 - 2015-11-12 204 | - Align with angular-2.0.0-alpha.46 205 | 206 | ## 0.0.4 - 2015-10-25 207 | - Add ES5 transpiled version and typescript definitions (.d.ts) file to the published npm package 208 | 209 | ## 0.0.3 - 2015-10-22 210 | - Align with angular-2.0.0-alpha.44 211 | 212 | ## 0.0.2 - 2015-09-18 213 | - Align with angular-2.0.0-alpha.37 214 | 215 | ## 0.0.1 - 2015-08-25 216 | 217 | - Initial release 218 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guide 2 | 3 | Contributing to `ngx-moment` is fairly easy. This document shows you how to 4 | get the project, run all provided tests and generate a production ready build. 5 | 6 | It also covers provided npm scripts, that help you developing on `ngx-moment`. 7 | 8 | ## Dependencies 9 | 10 | To make sure, that the following instructions work, please install the following dependencies 11 | on you machine: 12 | 13 | - Node.js 14 | - npm 15 | - Git 16 | 17 | ## Installation 18 | 19 | To get the source of `ngx-moment` clone the git repository via: 20 | 21 | `git clone https://github.com/urish/ngx-moment` 22 | 23 | This will clone the complete source to your local machine. Navigate to the project folder 24 | and install all needed dependencies via **npm**: 25 | 26 | `npm install` 27 | 28 | Well done! ngx-moment is now installed and ready to be built. 29 | 30 | ## Building 31 | 32 | `ngx-moment` comes with a few **npm scripts** which help you to automate 33 | the development process. The following npm scripts are provided: 34 | 35 | #### npm test 36 | 37 | `npm test` compiles the typescript code into javascript, and then runs the unit 38 | tests, which are located in `src/*.spec.ts`. The task uses the [jest test runner](https://facebook.github.io/jest/). 39 | 40 | #### npm run prepublish 41 | 42 | `npm run prepublish` compiles the typescript code into javascript. 43 | 44 | ## Contributing/Submitting changes 45 | 46 | - Checkout a new branch based on `master` and name it to what you intend to do: 47 | - Example: 48 | ```` 49 | $ git checkout -b BRANCH_NAME 50 | ```` 51 | - Use one branch per fix/feature 52 | - Make your changes 53 | - Make sure to provide a spec for unit tests (see [time-ago.pipe.spec.ts](src/time-ago.pipe.spec.ts) for example) 54 | - Run your tests with `npm test` 55 | - When all tests pass, everything's fine 56 | - Commit your changes 57 | - Please provide a git message which explains what you've done 58 | - Commit to the forked repository 59 | - Make a pull request 60 | 61 | If you follow these instructions, your PR will land pretty safely in the main repo! 62 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013-2020 Uri Shaked and contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ngx-moment 2 | 3 | moment.js pipes for Angular 4 | 5 | [![Build Status](https://travis-ci.org/urish/ngx-moment.png?branch=master)](https://travis-ci.org/urish/ngx-moment) 6 | [![npm version](https://img.shields.io/npm/v/ngx-moment.svg)](https://www.npmjs.com/package/ngx-moment) 7 | 8 | This module works with Angular 7.0.0 and newer. 9 | 10 | For older Angular versions, please install [angular2-moment](https://npmjs.org/package/angular2-moment). For the AngularJS, please check out [angular-moment](https://github.com/urish/angular-moment). 11 | 12 | Installation 13 | ------------ 14 | 15 | ```bash 16 | npm install --save moment ngx-moment 17 | ``` 18 | 19 | or if you use yarn: 20 | 21 | ```bash 22 | yarn add moment ngx-moment 23 | ``` 24 | 25 | Usage 26 | ----- 27 | 28 | Import `MomentModule` into your app's modules: 29 | 30 | ``` typescript 31 | import { MomentModule } from 'ngx-moment'; 32 | 33 | @NgModule({ 34 | imports: [ 35 | MomentModule 36 | ] 37 | }) 38 | ``` 39 | 40 | If you would like to supply any `NgxMomentOptions` that will be made available to the pipes you can also use: 41 | 42 | ```typescript 43 | import { MomentModule } from 'ngx-moment'; 44 | 45 | @NgModule({ 46 | imports: [ 47 | MomentModule.forRoot({ 48 | relativeTimeThresholdOptions: { 49 | 'm': 59 50 | } 51 | }) 52 | ] 53 | }) 54 | ``` 55 | 56 | This makes all the `ngx-moment` pipes available for use in your app components. 57 | 58 | Available pipes 59 | --------------- 60 | 61 | ## amTimeAgo pipe 62 | Takes an optional `omitSuffix` argument that defaults to `false` and another optional `formatFn` function which can be used to customise the format of the time ago text. 63 | 64 | ``` typescript 65 | @Component({ 66 | selector: 'app', 67 | template: ` 68 | Last updated: {{myDate | amTimeAgo}} 69 | ` 70 | }) 71 | ``` 72 | 73 | Prints `Last updated: a few seconds ago` 74 | 75 | ``` typescript 76 | @Component({ 77 | selector: 'app', 78 | template: ` 79 | Last updated: {{myDate | amTimeAgo:true}} 80 | ` 81 | }) 82 | ``` 83 | 84 | Prints `Last updated: a few seconds` 85 | 86 | ## amCalendar pipe 87 | Takes optional `referenceTime` argument (defaults to now) 88 | and `formats` argument that could be output formats object or callback function. 89 | See [momentjs docs](http://momentjs.com/docs/#/displaying/calendar-time/) for details. 90 | 91 | ``` typescript 92 | @Component({ 93 | selector: 'app', 94 | template: ` 95 | Last updated: {{myDate | amCalendar}} 96 | ` 97 | }) 98 | ``` 99 | 100 | Prints `Last updated: Today at 14:00` (default referenceTime is today by default) 101 | 102 | ``` typescript 103 | @Component({ 104 | selector: 'app', 105 | template: ` 106 | Last updated: 107 | ` 108 | }) 109 | export class AppComponent { 110 | nextDay: Date; 111 | 112 | constructor() { 113 | this.nextDay = new Date(); 114 | nextDay.setDate(nextDay.getDate() + 1); 115 | } 116 | } 117 | ``` 118 | 119 | Prints `Last updated: Yesterday at 14:00` (referenceTime is tomorrow) 120 | 121 | ``` typescript 122 | @Component({ 123 | selector: 'app', 124 | template: ` 125 | Last updated: 126 | ` 127 | }) 128 | ``` 129 | 130 | Prints `Last updated: Same Day at 2:00 PM` 131 | 132 | ## amDateFormat pipe 133 | 134 | ``` typescript 135 | @Component({ 136 | selector: 'app', 137 | template: ` 138 | Last updated: {{myDate | amDateFormat:'LL'}} 139 | ` 140 | }) 141 | ``` 142 | 143 | Prints `Last updated: January 24, 2016` 144 | 145 | ## amParse pipe 146 | 147 | Parses a custom-formatted date into a moment object that can be used with the other pipes. 148 | 149 | ``` typescript 150 | @Component({ 151 | selector: 'app', 152 | template: ` 153 | Last updated: {{'24/01/2014' | amParse:'DD/MM/YYYY' | amDateFormat:'LL'}} 154 | ` 155 | }) 156 | ``` 157 | 158 | Prints `Last updated: January 24, 2016` 159 | 160 | The pipe can also accept an array of formats as parameter. 161 | 162 | ``` typescript 163 | @Component({ 164 | selector: 'app', 165 | template: ` 166 | Last updated: {{'24/01/2014 22:00' | amParse: formats | amDateFormat:'LL'}} 167 | ` 168 | }) 169 | export class App { 170 | 171 | formats: string[] = ['DD/MM/YYYY HH:mm:ss', 'DD/MM/YYYY HH:mm']; 172 | 173 | constructor() { } 174 | 175 | } 176 | ``` 177 | 178 | Prints `Last updated: January 24, 2016` 179 | 180 | ## amLocal pipe 181 | 182 | Converts UTC time to local time. 183 | 184 | ``` typescript 185 | @Component({ 186 | selector: 'app', 187 | template: ` 188 | Last updated: {{mydate | amLocal | amDateFormat: 'YYYY-MM-DD HH:mm'}} 189 | ` 190 | }) 191 | ``` 192 | 193 | Prints `Last updated 2016-01-24 12:34` 194 | 195 | ## amLocale pipe 196 | 197 | To be used with amDateFormat pipe in order to change locale. 198 | 199 | ``` typescript 200 | @Component({ 201 | selector: 'app', 202 | template: ` 203 | Last updated: {{'2016-01-24 14:23:45' | amLocale:'en' | amDateFormat:'MMMM Do YYYY, h:mm:ss a'}} 204 | ` 205 | }) 206 | ``` 207 | 208 | Prints `Last updated: January 24th 2016, 2:23:45 pm` 209 | 210 | Note: The locale might have to be imported (e.g. in the app module). 211 | 212 | ``` typescript 213 | import 'moment/locale/de'; 214 | ``` 215 | 216 | ## amFromUnix pipe 217 | 218 | ``` typescript 219 | @Component({ 220 | selector: 'app', 221 | template: ` 222 | Last updated: {{ (1456263980 | amFromUnix) | amDateFormat:'hh:mmA'}} 223 | ` 224 | }) 225 | ``` 226 | 227 | Prints `Last updated: 01:46PM` 228 | 229 | ## amDuration pipe 230 | 231 | ``` typescript 232 | @Component({ 233 | selector: 'app', 234 | template: ` 235 | Uptime: {{ 365 | amDuration:'seconds' }} 236 | ` 237 | }) 238 | ``` 239 | 240 | Prints `Uptime: 6 minutes` 241 | 242 | ## amDifference pipe 243 | 244 | ``` typescript 245 | @Component({ 246 | selector: 'app', 247 | template: ` 248 | Expiration: {{nextDay | amDifference: today :'days' : true}} days 249 | ` 250 | }) 251 | ``` 252 | Prints `Expiration: 1 days` 253 | 254 | ## amAdd and amSubtract pipes 255 | 256 | Use these pipes to perform date arithmetics. See [momentjs docs](http://momentjs.com/docs/#/manipulating/add/) for details. 257 | 258 | ``` typescript 259 | @Component({ 260 | selector: 'app', 261 | template: ` 262 | Expiration: {{'2017-03-17T16:55:00.000+01:00' | amAdd: 2 : 'hours' | amDateFormat: 'YYYY-MM-DD HH:mm'}} 263 | ` 264 | }) 265 | ``` 266 | Prints `Expiration: 2017-03-17 18:55` 267 | 268 | ``` typescript 269 | @Component({ 270 | selector: 'app', 271 | template: ` 272 | Last updated: {{'2017-03-17T16:55:00.000+01:00' | amSubtract: 5 : 'years' | amDateFormat: 'YYYY-MM-DD HH:mm'}} 273 | ` 274 | }) 275 | ``` 276 | Prints `Last updated: 2012-03-17 16:55` 277 | 278 | ## amFromUtc pipe 279 | 280 | Parses the date as UTC and enables mode for subsequent moment operations (such as displaying the time in UTC). This can be combined with `amLocal` to display a UTC date in local time. 281 | 282 | ``` typescript 283 | @Component({ 284 | selector: 'app', 285 | template: ` 286 | Last updated: {{ '2016-12-31T23:00:00.000-01:00' | amFromUtc | amDateFormat: 'YYYY-MM-DD' }} 287 | ` 288 | }) 289 | ``` 290 | 291 | Prints `Last updated: 2017-01-01` 292 | 293 | It's also possible to specify a different format than the standard ISO8601. 294 | 295 | ``` typescript 296 | @Component({ 297 | selector: 'app', 298 | template: ` 299 | Last updated: {{ '31/12/2016 23:00-01:00' | amFromUtc: 'DD/MM/YYYY HH:mmZZ' | amDateFormat: 'YYYY-MM-DD' }} 300 | ` 301 | }) 302 | ``` 303 | 304 | Or even an array of formats: 305 | 306 | ``` typescript 307 | @Component({ 308 | selector: 'app', 309 | template: ` 310 | Last updated: {{ '31/12/2016 23:00-01:00' | amFromUtc: formats | amDateFormat: 'YYYY-MM-DD' }} 311 | ` 312 | }) 313 | export class App { 314 | 315 | formats: string[] = ['DD/MM/YYYY HH:mm:ss', 'DD/MM/YYYY HH:mmZZ']; 316 | 317 | constructor() { } 318 | 319 | } 320 | ``` 321 | 322 | Both examples above will print `Last updated: 2017-01-01` 323 | 324 | ## amUtc pipe 325 | 326 | Enables UTC mode for subsequent moment operations (such as displaying the time in UTC). 327 | 328 | ``` typescript 329 | @Component({ 330 | selector: 'app', 331 | template: ` 332 | Last updated: {{ '2016-12-31T23:00:00.000-01:00' | amUtc | amDateFormat: 'YYYY-MM-DD' }} 333 | ` 334 | }) 335 | ``` 336 | 337 | Prints `Last updated: 2017-01-01` 338 | 339 | ## amParseZone pipe 340 | 341 | Parses a string but keeps the resulting Moment object in a fixed-offset timezone with the provided offset in the string. 342 | 343 | ``` typescript 344 | @Component({ 345 | selector: 'app', 346 | template: ` 347 | Last updated: {{ '2016-12-31T23:00:00.000-03:00' | amParseZone | amDateFormat: 'LLLL (Z)' }} 348 | ` 349 | }) 350 | ``` 351 | 352 | Prints `Last updated: Saturday, December 31, 2016 11:00 PM (-03:00)` 353 | 354 | ## amIsBefore and amIsAfter pipe 355 | 356 | Check if a moment is before another moment. Supports limiting granularity to a unit other than milliseconds, pass the units as second parameter 357 | 358 | ```typescript 359 | @Component({ 360 | selector: 'app', 361 | template: ` 362 | Today is before tomorrow: {{ today | amIsBefore:tomorrow:'day' }} 363 | ` 364 | }) 365 | ``` 366 | 367 | Prints `Today is before tomorrow: true` 368 | 369 | ```typescript 370 | @Component({ 371 | selector: 'app', 372 | template: ` 373 | Tomorrow is after today: {{ tomorrow | amIsAfter:today:'day' }} 374 | ` 375 | }) 376 | ``` 377 | 378 | Prints `Tomorrow is after today: true` 379 | 380 | NgxMomentOptions 381 | ---------------- 382 | An `NgxMomentOptions` object can be provided to the module using the `forRoot` convention and will provide options for the pipes to use with the `moment` instance, these options are detailed in the table below: 383 | 384 | | prop | type | description | 385 | | --- |:---:| --- | 386 | | relativeTimeThresholdOptions | Dictionary
key: string
value: number | Provides the `relativeTimeThreshold` units allowing a pipe to set the `moment.relativeTimeThreshold` values.

The `key` is a unit defined as one of `ss`, `s`, `m`, `h`, `d`, `M`.

See [Relative Time Thresholds](https://momentjs.com/docs/#/customization/relative-time-threshold/) documentation for more details. | 387 | 388 | Complete Example 389 | ---------------- 390 | 391 | ``` typescript 392 | import { NgModule, Component } from '@angular/core'; 393 | import { BrowserModule } from '@angular/platform-browser'; 394 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 395 | import { MomentModule } from 'ngx-moment'; 396 | 397 | @Component({ 398 | selector: 'app', 399 | template: ` 400 | Last updated: {{myDate | amTimeAgo}}, {{myDate | amCalendar}}, {{myDate | amDateFormat:'LL'}} 401 | ` 402 | }) 403 | export class AppComponent { 404 | myDate: Date; 405 | 406 | constructor() { 407 | this.myDate = new Date(); 408 | } 409 | } 410 | 411 | @NgModule({ 412 | imports: [ 413 | BrowserModule, 414 | MomentModule 415 | ], 416 | declarations: [ AppComponent ] 417 | bootstrap: [ AppComponent ] 418 | }) 419 | class AppModule {} 420 | 421 | platformBrowserDynamic().bootstrapModule(AppModule); 422 | ``` 423 | 424 | Demo 425 | ---- 426 | 427 | [See online demo on StackBlitz](https://stackblitz.com/edit/ngx-moment-demo?file=src%2Fapp%2Fapp.component.html) 428 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "ngx-moment": { 7 | "projectType": "library", 8 | "root": "", 9 | "sourceRoot": "src", 10 | "prefix": "am", 11 | "architect": { 12 | "build": { 13 | "builder": "@angular-devkit/build-angular:ng-packagr", 14 | "options": { 15 | "project": "ng-package.json" 16 | }, 17 | "configurations": { 18 | "production": { 19 | "tsConfig": "./tsconfig.json" 20 | } 21 | }, 22 | "defaultConfiguration": "production" 23 | } 24 | } 25 | } 26 | }, 27 | "defaultProject": "ngx-moment" 28 | } 29 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'jest-preset-angular', 3 | setupFilesAfterEnv: ['/setupJest.ts'], 4 | }; 5 | -------------------------------------------------------------------------------- /ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "dist/ngx-moment", 4 | "lib": { 5 | "entryFile": "src/index.ts" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ng-package.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "dist/lib", 4 | "lib": { 5 | "entryFile": "src/index.ts" 6 | } 7 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ngx-moment", 3 | "version": "6.0.2", 4 | "description": "Moment.JS pipes for Angular (timeago and more)", 5 | "scripts": { 6 | "build": "ng build", 7 | "test": "tslint --project tsconfig.lint.json && tsc -p tsconfig.spec.json && jest", 8 | "test:watch": "jest --watch", 9 | "test:coverage": "jest --coverage", 10 | "test:debug": "node --inspect-brk --inspect ./node_modules/jest/bin/jest.js --runInBand", 11 | "prepublish": "ng build", 12 | "prepare": "husky install", 13 | "postinstall": "husky install" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/urish/ngx-moment.git" 18 | }, 19 | "keywords": [ 20 | "angular", 21 | "timeago", 22 | "momentjs" 23 | ], 24 | "author": "Uri Shaked", 25 | "license": "MIT", 26 | "bugs": { 27 | "url": "https://github.com/urish/ngx-moment/issues" 28 | }, 29 | "homepage": "https://github.com/urish/ngx-moment#readme", 30 | "peerDependencies": { 31 | "moment": "^2.19.3" 32 | }, 33 | "devDependencies": { 34 | "@angular-devkit/build-angular": "^13.1.1", 35 | "@angular/cli": "^13.1.1", 36 | "@angular/common": "^13.1.0", 37 | "@angular/compiler": "^13.1.0", 38 | "@angular/compiler-cli": "^13.1.0", 39 | "@angular/core": "^13.1.0", 40 | "@angular/language-service": "^13.1.0", 41 | "@angular/platform-browser": "^13.1.0", 42 | "@angular/platform-browser-dynamic": "^13.1.0", 43 | "@types/eslint": "^6.8.0", 44 | "@types/jest": "^27.0.3", 45 | "@types/node": "^16.11.13", 46 | "core-js": "^3.19.3", 47 | "husky": "^7.0.0", 48 | "jest": "^27.4.5", 49 | "jest-preset-angular": "^11.0.1", 50 | "lint-staged": "^12.1.2", 51 | "moment": "^2.29.1", 52 | "ng-packagr": "^13.1.1", 53 | "prettier": "^2.5.1", 54 | "rxjs": "^7.4.0", 55 | "tslib": "^2.3.1", 56 | "tslint": "^6.1.3", 57 | "typescript": "~4.4.4", 58 | "zone.js": "~0.11.4" 59 | }, 60 | "husky": { 61 | "hooks": { 62 | "pre-commit": "lint-staged" 63 | } 64 | }, 65 | "lint-staged": { 66 | "src/**/*.ts": [ 67 | "tslint --project tsconfig.lint.json -c tslint.json --fix", 68 | "prettier --write", 69 | "git add" 70 | ] 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | printWidth: 100, 3 | singleQuote: true, 4 | tabWidth: 2, 5 | trailingComma: 'all', 6 | }; 7 | -------------------------------------------------------------------------------- /setupJest.ts: -------------------------------------------------------------------------------- 1 | import 'jest-preset-angular'; 2 | -------------------------------------------------------------------------------- /src/add.pipe.spec.ts: -------------------------------------------------------------------------------- 1 | import 'reflect-metadata'; 2 | import moment from 'moment'; 3 | import { AddPipe } from './add.pipe'; 4 | 5 | describe('AddPipe', () => { 6 | let pipe: AddPipe; 7 | 8 | beforeEach(() => (pipe = new AddPipe())); 9 | 10 | describe('#transform', () => { 11 | it('should throw when provided no arguments', () => { 12 | expect(() => (pipe.transform as any)(128)).toThrow('AddPipe: missing required arguments'); 13 | }); 14 | 15 | it('should add two hours', () => { 16 | const result = pipe.transform(moment('2016-01-24 15:00:00'), '2', 'hours'); 17 | expect(moment(result).format('YYYY-MM-DD HH:mm:ss')).toBe('2016-01-24 17:00:00'); 18 | }); 19 | 20 | it('should add two days', () => { 21 | const result = pipe.transform(moment('2016-01-24 15:00:00'), '2', 'days'); 22 | expect(moment(result).format('YYYY-MM-DD HH:mm:ss')).toBe('2016-01-26 15:00:00'); 23 | }); 24 | 25 | it('should add two years', () => { 26 | const result = pipe.transform(moment('2016-01-24 15:00:00'), '2', 'years'); 27 | expect(moment(result).format('YYYY-MM-DD HH:mm:ss')).toBe('2018-01-24 15:00:00'); 28 | }); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /src/add.pipe.ts: -------------------------------------------------------------------------------- 1 | /* ngx-moment (c) 2015, 2016 Uri Shaked / MIT Licence */ 2 | 3 | import { Pipe, PipeTransform } from '@angular/core'; 4 | import moment from 'moment'; 5 | 6 | @Pipe({ name: 'amAdd' }) 7 | export class AddPipe implements PipeTransform { 8 | transform( 9 | value: moment.MomentInput, 10 | amount: moment.DurationInputArg1, 11 | unit?: moment.DurationInputArg2, 12 | ): any { 13 | if ( 14 | typeof amount === 'undefined' || 15 | (typeof amount === 'number' && typeof unit === 'undefined') 16 | ) { 17 | throw new Error('AddPipe: missing required arguments'); 18 | } 19 | return moment(value).add(amount, unit); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/calendar.pipe.spec.ts: -------------------------------------------------------------------------------- 1 | import 'reflect-metadata'; 2 | import moment from 'moment'; 3 | import { NgZone } from '@angular/core'; 4 | import { CalendarPipe } from './calendar.pipe'; 5 | 6 | class NgZoneMock { 7 | runOutsideAngular(fn: Function) { 8 | return fn(); 9 | } 10 | run(fn: Function) { 11 | return fn(); 12 | } 13 | } 14 | 15 | describe('CalendarPipe', () => { 16 | describe('#transform', () => { 17 | const pipe = new CalendarPipe(null, new NgZoneMock() as NgZone); 18 | it('should transform the start of the current day to "Today at 12:00 AM"', () => { 19 | expect(pipe.transform(moment().startOf('day'))).toBe('Today at 12:00 AM'); 20 | }); 21 | 22 | it('should transform the start of the current day to "Yesterday at 12:00 AM"', () => { 23 | const testDate = moment().startOf('day'); 24 | const referenceTime = moment().clone().add(1, 'day'); 25 | expect(pipe.transform(testDate, referenceTime)).toBe('Yesterday at 12:00 AM'); 26 | }); 27 | 28 | it('should transform date to "January 13th 2016, 1:23:45 AM"', () => { 29 | const testDate = new Date(2016, 0, 13, 1, 23, 45); 30 | const formats = { sameElse: 'MMMM Do YYYY, h:mm:ss A' }; 31 | expect(pipe.transform(testDate, formats)).toBe('January 13th 2016, 1:23:45 AM'); 32 | }); 33 | 34 | it('should support any order of arguments', () => { 35 | const testDate = moment(); 36 | const referenceTime = moment().clone().add(1, 'day'); 37 | const formats = { lastDay: '[Last day at] h:mm A' }; 38 | expect(pipe.transform(testDate, formats, referenceTime)).toBe( 39 | pipe.transform(testDate, referenceTime, formats), 40 | ); 41 | }); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /src/calendar.pipe.ts: -------------------------------------------------------------------------------- 1 | /* ngx-moment (c) 2015, 2016 Uri Shaked / MIT Licence */ 2 | 3 | import { 4 | Pipe, 5 | ChangeDetectorRef, 6 | PipeTransform, 7 | EventEmitter, 8 | OnDestroy, 9 | NgZone, 10 | } from '@angular/core'; 11 | import moment from 'moment'; 12 | import { Subscription } from 'rxjs'; 13 | 14 | @Pipe({ name: 'amCalendar', pure: false }) 15 | export class CalendarPipe implements PipeTransform, OnDestroy { 16 | /** 17 | * Internal reference counter, so we can clean up when no instances are in use 18 | */ 19 | private static refs = 0; 20 | 21 | private static timer: number | null = null; 22 | private static midnight: EventEmitter | null = null; 23 | 24 | private midnightSub: Subscription; 25 | 26 | constructor(private cdRef: ChangeDetectorRef, private ngZone: NgZone) { 27 | // using a single static timer for all instances of this pipe for performance reasons 28 | CalendarPipe.initTimer(ngZone); 29 | 30 | CalendarPipe.refs++; 31 | 32 | // values such as Today will need to be replaced with Yesterday after midnight, 33 | // so make sure we subscribe to an EventEmitter that we set up to emit at midnight 34 | this.midnightSub = CalendarPipe.midnight.subscribe(() => { 35 | this.ngZone.run(() => this.cdRef.markForCheck()); 36 | }); 37 | } 38 | 39 | transform(value: moment.MomentInput, ...args: any[]): any { 40 | let formats: any = null; 41 | let referenceTime: any = null; 42 | 43 | for (let i = 0, len = args.length; i < len; i++) { 44 | if (args[i] !== null) { 45 | if (typeof args[i] === 'object' && !moment.isMoment(args[i])) { 46 | formats = args[i]; 47 | } else { 48 | referenceTime = moment(args[i]); 49 | } 50 | } 51 | } 52 | 53 | return moment(value).calendar(referenceTime, formats); 54 | } 55 | 56 | ngOnDestroy(): void { 57 | if (CalendarPipe.refs > 0) { 58 | CalendarPipe.refs--; 59 | } 60 | 61 | if (CalendarPipe.refs === 0) { 62 | CalendarPipe.removeTimer(); 63 | } 64 | 65 | this.midnightSub.unsubscribe(); 66 | } 67 | 68 | private static initTimer(ngZone: NgZone) { 69 | // initialize the timer 70 | if (!CalendarPipe.midnight) { 71 | CalendarPipe.midnight = new EventEmitter(); 72 | if (typeof window !== 'undefined') { 73 | const timeToUpdate = CalendarPipe._getMillisecondsUntilUpdate(); 74 | CalendarPipe.timer = ngZone.runOutsideAngular(() => { 75 | return window.setTimeout(() => { 76 | // emit the current date 77 | CalendarPipe.midnight.emit(new Date()); 78 | 79 | // refresh the timer 80 | CalendarPipe.removeTimer(); 81 | CalendarPipe.initTimer(ngZone); 82 | }, timeToUpdate); 83 | }); 84 | } 85 | } 86 | } 87 | 88 | private static removeTimer() { 89 | if (CalendarPipe.timer) { 90 | window.clearTimeout(CalendarPipe.timer); 91 | CalendarPipe.timer = null; 92 | CalendarPipe.midnight = null; 93 | } 94 | } 95 | 96 | private static _getMillisecondsUntilUpdate() { 97 | const now = moment(); 98 | const tomorrow = moment().startOf('day').add(1, 'days'); 99 | const timeToMidnight = tomorrow.valueOf() - now.valueOf(); 100 | return timeToMidnight + 1000; // 1 second after midnight 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/date-format.pipe.spec.ts: -------------------------------------------------------------------------------- 1 | import moment from 'moment'; 2 | import { DateFormatPipe } from './date-format.pipe'; 3 | 4 | describe('DateFormatPipe', () => { 5 | describe('#transform', () => { 6 | it('should properly format a date', () => { 7 | const pipe = new DateFormatPipe(); 8 | const result = pipe.transform(moment('2016-01-24 01:23:45'), 'MMMM Do YYYY, h:mm:ss a'); 9 | expect(result).toBe('January 24th 2016, 1:23:45 am'); 10 | }); 11 | 12 | it('should not format empty dates', () => { 13 | const pipe = new DateFormatPipe(); 14 | 15 | const result1 = pipe.transform('', 'MMMM Do YYYY, h:mm:ss a'); 16 | expect(result1).toBe(''); 17 | 18 | const result2 = pipe.transform(null); 19 | expect(result2).toBe(''); 20 | 21 | const result3 = pipe.transform(undefined); 22 | expect(result3).toBe(''); 23 | }); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/date-format.pipe.ts: -------------------------------------------------------------------------------- 1 | /* ngx-moment (c) 2015, 2016 Uri Shaked / MIT Licence */ 2 | 3 | import { Pipe, PipeTransform } from '@angular/core'; 4 | import moment from 'moment'; 5 | 6 | @Pipe({ name: 'amDateFormat' }) 7 | export class DateFormatPipe implements PipeTransform { 8 | transform(value: moment.MomentInput, ...args: any[]): string { 9 | if (!value) { 10 | return ''; 11 | } 12 | return moment(value).format(args[0]); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/difference.pipe.spec.ts: -------------------------------------------------------------------------------- 1 | import { DifferencePipe } from './difference.pipe'; 2 | 3 | describe('DifferencePipe', () => { 4 | let pipe: DifferencePipe; 5 | 6 | beforeEach(() => (pipe = new DifferencePipe())); 7 | 8 | describe('#transform', () => { 9 | it('should take the difference of two dates in milliseconds', () => { 10 | const today = new Date(2012, 0, 22, 0, 0, 0); 11 | const testDate = new Date(today.getFullYear(), today.getMonth(), today.getDate(), 13, 33, 33); 12 | expect(pipe.transform(testDate, today)).toBe(48813000); 13 | }); 14 | 15 | it('should support passing "years", "months", "days", etc as a units parameter', () => { 16 | const test = new Date(2012, 0, 22, 4, 46, 54); 17 | const testDate1 = new Date(2013, 0, 22, 4, 46, 54); 18 | expect(pipe.transform(testDate1, test, 'years')).toBe(1); 19 | const testDate2 = new Date(2012, 1, 22, 4, 46, 54); 20 | expect(pipe.transform(testDate2, test, 'months')).toBe(1); 21 | const testDate3 = new Date(2012, 0, 23, 4, 46, 54); 22 | expect(pipe.transform(testDate3, test, 'days')).toBe(1); 23 | }); 24 | 25 | it('should allow rounding to be disabled via parameter', () => { 26 | const test = new Date(2012, 0, 22, 4, 46, 54); 27 | const testDate1 = new Date(test.getFullYear() + 1, test.getMonth() + 6, test.getDate()); 28 | expect(pipe.transform(testDate1, test, 'years')).toBe(1); 29 | expect(pipe.transform(testDate1, test, 'years', true)).toBeCloseTo(1.5, 1); 30 | }); 31 | 32 | it('dates from the future should return negative values', () => { 33 | const today = new Date(2012, 0, 22, 4, 46, 54); 34 | const testDate = new Date(2013, 0, 22, 4, 46, 54); 35 | expect(String(pipe.transform(today, testDate))).toContain('-'); 36 | }); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /src/difference.pipe.ts: -------------------------------------------------------------------------------- 1 | /* ngx-moment (c) 2015, 2016 Uri Shaked / MIT Licence */ 2 | 3 | import { Pipe, PipeTransform } from '@angular/core'; 4 | import moment from 'moment'; 5 | 6 | @Pipe({ name: 'amDifference' }) 7 | export class DifferencePipe implements PipeTransform { 8 | transform( 9 | value: moment.MomentInput, 10 | otherValue: moment.MomentInput, 11 | unit?: moment.unitOfTime.Diff, 12 | precision?: boolean, 13 | ): number { 14 | const date = moment(value); 15 | const date2 = otherValue !== null ? moment(otherValue) : moment(); 16 | 17 | return date.diff(date2, unit, precision); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/duration.pipe.spec.ts: -------------------------------------------------------------------------------- 1 | import { DurationPipe } from './duration.pipe'; 2 | import { NgxMomentOptions } from './moment-options'; 3 | 4 | describe('DurationPipe', () => { 5 | let pipe: DurationPipe; 6 | 7 | beforeEach(() => (pipe = new DurationPipe())); 8 | 9 | describe('#transform', () => { 10 | it('should throw when provided no arguments', () => { 11 | expect(() => pipe.transform(128)).toThrow( 12 | 'DurationPipe: missing required time unit argument', 13 | ); 14 | }); 15 | 16 | it('should convert a duration to a human-readable string', () => { 17 | expect(pipe.transform(24, 'hours')).toEqual('a day'); 18 | expect(pipe.transform(27, 'days')).toEqual('a month'); 19 | expect(pipe.transform(365, 'seconds')).toEqual('6 minutes'); 20 | expect(pipe.transform(365, 'days')).toEqual('a year'); 21 | expect(pipe.transform(86400, 'seconds')).toEqual('a day'); 22 | }); 23 | 24 | it(`should convert '50 minutes' to 'an hour' with default 'relativeTimeThreshold'`, () => { 25 | expect(pipe.transform(50, 'minutes')).toEqual('an hour'); 26 | }); 27 | }); 28 | 29 | describe('ctor with NgxMomentOptions', () => { 30 | const momentOptions: NgxMomentOptions = { 31 | relativeTimeThresholdOptions: { 32 | m: 59, 33 | }, 34 | }; 35 | 36 | beforeEach(() => (pipe = new DurationPipe(momentOptions))); 37 | 38 | it(`should convert '50 minutes' to '50 minutes' when relativeTimeThreshold for 'm' unit is set to 59`, () => { 39 | expect(pipe.transform(50, 'minutes')).toEqual('50 minutes'); 40 | }); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /src/duration.pipe.ts: -------------------------------------------------------------------------------- 1 | import moment from 'moment'; 2 | 3 | import { Inject, Optional, Pipe, PipeTransform } from '@angular/core'; 4 | import { NGX_MOMENT_OPTIONS, NgxMomentOptions } from './moment-options'; 5 | 6 | @Pipe({ name: 'amDuration' }) 7 | export class DurationPipe implements PipeTransform { 8 | allowedUnits: Array = ['ss', 's', 'm', 'h', 'd', 'M']; 9 | 10 | constructor(@Optional() @Inject(NGX_MOMENT_OPTIONS) momentOptions?: NgxMomentOptions) { 11 | this._applyOptions(momentOptions); 12 | } 13 | 14 | transform(value: moment.DurationInputArg1, ...args: string[]): string { 15 | if (typeof args === 'undefined' || args.length !== 1) { 16 | throw new Error('DurationPipe: missing required time unit argument'); 17 | } 18 | return moment.duration(value, args[0] as moment.unitOfTime.DurationConstructor).humanize(); 19 | } 20 | 21 | private _applyOptions(momentOptions: NgxMomentOptions): void { 22 | if (!momentOptions) { 23 | return; 24 | } 25 | 26 | if (!!momentOptions.relativeTimeThresholdOptions) { 27 | const units: Array = Object.keys(momentOptions.relativeTimeThresholdOptions); 28 | const filteredUnits: Array = units.filter( 29 | (unit) => this.allowedUnits.indexOf(unit) !== -1, 30 | ); 31 | filteredUnits.forEach((unit) => { 32 | moment.relativeTimeThreshold(unit, momentOptions.relativeTimeThresholdOptions[unit]); 33 | }); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/from-unix.pipe.spec.ts: -------------------------------------------------------------------------------- 1 | import moment from 'moment'; 2 | import { FromUnixPipe } from './from-unix.pipe'; 3 | 4 | describe('FromUnixPipe', () => { 5 | describe('#transform', () => { 6 | it('should parse a unix timestamp number to moment', () => { 7 | const pipe = new FromUnixPipe(); 8 | const result = pipe.transform(1456263980); 9 | expect(result).toEqual(moment.unix(1456263980)); 10 | }); 11 | 12 | it('should parse a unix timestamp string to moment', () => { 13 | const pipe = new FromUnixPipe(); 14 | const result = pipe.transform('1456263980'); 15 | expect(result).toEqual(moment.unix(1456263980)); 16 | }); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /src/from-unix.pipe.ts: -------------------------------------------------------------------------------- 1 | /* ngx-moment (c) 2015, 2016 Uri Shaked / MIT Licence */ 2 | 3 | import { Pipe, PipeTransform } from '@angular/core'; 4 | import moment from 'moment'; 5 | 6 | @Pipe({ name: 'amFromUnix' }) 7 | export class FromUnixPipe implements PipeTransform { 8 | transform(value: number | string, ...args: string[]): any { 9 | return typeof value === 'string' ? moment.unix(parseInt(value, 10)) : moment.unix(value); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/from-utc.pipe.spec.ts: -------------------------------------------------------------------------------- 1 | import moment from 'moment'; 2 | import { DateFormatPipe } from './date-format.pipe'; 3 | import { FromUtcPipe } from './from-utc.pipe'; 4 | 5 | describe('UtcPipe', () => { 6 | describe('#transform', () => { 7 | let utcDatePipe: FromUtcPipe; 8 | 9 | beforeEach(() => { 10 | utcDatePipe = new FromUtcPipe(); 11 | }); 12 | 13 | it('should output an invalid momemt object for a null input', () => { 14 | const utcDate = utcDatePipe.transform(null); 15 | expect(utcDate).toEqual(expect.any(moment)); 16 | expect(utcDate.isValid()).toBe(false); 17 | }); 18 | 19 | it('should output a moment object for a moment input', () => { 20 | const momentDate = moment(); 21 | const utcDate = utcDatePipe.transform(momentDate); 22 | expect(utcDate).toEqual(expect.any(moment)); 23 | expect(utcDate.isValid()).toBe(true); 24 | }); 25 | 26 | it('should output a moment object for a date input', () => { 27 | const date = new Date(); 28 | const utcDate = utcDatePipe.transform(date); 29 | expect(utcDate).toEqual(expect.any(moment)); 30 | expect(utcDate.isValid()).toBe(true); 31 | }); 32 | 33 | it('should output a moment object for a string date', () => { 34 | const dateString = '2016-01-01'; 35 | const utcDate = utcDatePipe.transform(dateString); 36 | expect(utcDate).toEqual(expect.any(moment)); 37 | expect(utcDate.isValid()).toBe(true); 38 | }); 39 | 40 | it('should output a moment object for a timestamp', () => { 41 | const timestamp: number = Date.now(); 42 | const utcDate = utcDatePipe.transform(timestamp); 43 | expect(utcDate).toEqual(expect.any(moment)); 44 | expect(utcDate.isValid()).toBe(true); 45 | }); 46 | 47 | it('should parse with provided timezone and format to UTC', () => { 48 | const amDateFormat = new DateFormatPipe(); 49 | const datetimeString = '2016-12-31T23:00:00.000-01:00'; 50 | const momentFormatString = 'YYYY-MM-DD'; 51 | const utcOutput = utcDatePipe.transform(datetimeString); 52 | expect(amDateFormat.transform(utcOutput, momentFormatString)).toEqual('2017-01-01'); 53 | }); 54 | 55 | it('should parse as UTC without provided timezone', () => { 56 | const amDateFormat = new DateFormatPipe(); 57 | const datetimeString = '2016-12-31T23:00:00.000'; 58 | const momentFormatString = 'YYYY-MM-DD'; 59 | const utcOutput = utcDatePipe.transform(datetimeString); 60 | expect(amDateFormat.transform(utcOutput, momentFormatString)).toEqual('2016-12-31'); 61 | }); 62 | 63 | it('should parse as UTC with a provided format', () => { 64 | const datetimeString = '31/12/2016, 23:02:00'; 65 | const momentFormatString = 'DD/MM/YYYY, HH:mm:ss'; 66 | const utcOutput = utcDatePipe.transform(datetimeString, momentFormatString); 67 | expect(utcOutput).toEqual(expect.any(moment)); 68 | expect(utcOutput.isValid()).toBe(true); 69 | 70 | expect(utcOutput.year()).toBe(2016); 71 | expect(utcOutput.month()).toBe(11); 72 | expect(utcOutput.date()).toBe(31); 73 | }); 74 | 75 | it('should parse as UTC with an array of provided formats', () => { 76 | const datetimeString = '31st 12/2016'; 77 | const momentFormatStrings = ['DD/MM/YYYY, HH:mm:ss', 'Do MM/YYYY']; 78 | const utcOutput = utcDatePipe.transform(datetimeString, momentFormatStrings); 79 | expect(utcOutput).toEqual(expect.any(moment)); 80 | expect(utcOutput.isValid()).toBe(true); 81 | 82 | expect(utcOutput.year()).toBe(2016); 83 | expect(utcOutput.month()).toBe(11); 84 | expect(utcOutput.date()).toBe(31); 85 | }); 86 | 87 | it('should output an invalid moment object for a different formatted input', () => { 88 | const datetimeString = '31/12/2016, 23:02:00'; 89 | const utcDate = utcDatePipe.transform(datetimeString); 90 | expect(utcDate).toEqual(expect.any(moment)); 91 | expect(utcDate.isValid()).toBe(false); 92 | }); 93 | }); 94 | }); 95 | -------------------------------------------------------------------------------- /src/from-utc.pipe.ts: -------------------------------------------------------------------------------- 1 | /* ngx-moment (c) 2015, 2016 Uri Shaked / MIT Licence */ 2 | 3 | import { Pipe, PipeTransform } from '@angular/core'; 4 | import moment from 'moment'; 5 | 6 | @Pipe({ name: 'amFromUtc' }) 7 | export class FromUtcPipe implements PipeTransform { 8 | transform(value: moment.MomentInput, formats?: string | string[], ...args: string[]): any { 9 | return formats ? moment.utc(value, formats) : moment.utc(value); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export { AddPipe } from './add.pipe'; 2 | export { CalendarPipe } from './calendar.pipe'; 3 | export { DateFormatPipe } from './date-format.pipe'; 4 | export { DifferencePipe } from './difference.pipe'; 5 | export { DurationPipe } from './duration.pipe'; 6 | export { FromUnixPipe } from './from-unix.pipe'; 7 | export { ParsePipe } from './parse.pipe'; 8 | export { MomentModule } from './moment.module'; 9 | export { SubtractPipe } from './subtract.pipe'; 10 | export { TimeAgoPipe } from './time-ago.pipe'; 11 | export { UtcPipe } from './utc.pipe'; 12 | export { FromUtcPipe } from './from-utc.pipe'; 13 | export { LocalTimePipe } from './local.pipe'; 14 | export { LocalePipe } from './locale.pipe'; 15 | export { ParseZonePipe } from './parse-zone.pipe'; 16 | export { IsBeforePipe } from './is-before.pipe'; 17 | export { IsAfterPipe } from './is-after.pipe'; 18 | 19 | export { NgxMomentOptions, NGX_MOMENT_OPTIONS } from './moment-options'; 20 | -------------------------------------------------------------------------------- /src/is-after.pipe.spec.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable:no-unused-variable */ 2 | import { IsAfterPipe } from './is-after.pipe'; 3 | 4 | describe('IsAfterPipe', () => { 5 | let pipe: IsAfterPipe; 6 | 7 | beforeEach(() => (pipe = new IsAfterPipe())); 8 | 9 | describe('#transform', () => { 10 | it('should return true if value is after otherValue', () => { 11 | const test = new Date(2018, 11, 15, 0, 0, 0); 12 | const testDate1 = new Date(2018, 11, 13, 0, 0, 0); 13 | expect(pipe.transform(test, testDate1)).toBeTruthy(); 14 | }); 15 | 16 | it('should support passing "year", "month", "week", "day", etc as a unit parameter', () => { 17 | const test = new Date(2019, 11, 13, 12, 45, 45); 18 | const testDate1 = new Date(2018, 0, 13, 12, 45, 45); 19 | expect(pipe.transform(test, testDate1, 'year')).toBe(true); 20 | const testDate2 = new Date(2018, 1, 13, 12, 45, 45); 21 | expect(pipe.transform(test, testDate2, 'month')).toBe(true); 22 | const testDate3 = new Date(2018, 1, 10, 12, 45, 45); 23 | expect(pipe.transform(test, testDate3, 'day')).toBe(true); 24 | }); 25 | 26 | it('should return false if value is before otherValue', () => { 27 | const test = new Date(2018, 11, 13, 0, 0, 0); 28 | const testDate1 = new Date(2018, 11, 15, 0, 0, 0); 29 | expect(pipe.transform(test, testDate1)).toBeFalsy(); 30 | }); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /src/is-after.pipe.ts: -------------------------------------------------------------------------------- 1 | import moment from 'moment'; 2 | 3 | import { Pipe, PipeTransform } from '@angular/core'; 4 | 5 | @Pipe({ 6 | name: 'amIsAfter', 7 | }) 8 | export class IsAfterPipe implements PipeTransform { 9 | transform( 10 | value: moment.MomentInput, 11 | otherValue: moment.MomentInput, 12 | unit?: moment.unitOfTime.StartOf, 13 | ): boolean { 14 | return moment(value).isAfter(moment(otherValue), unit); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/is-before.pipe.spec.ts: -------------------------------------------------------------------------------- 1 | import { IsBeforePipe } from './is-before.pipe'; 2 | 3 | describe('IsBeforePipe', () => { 4 | let pipe: IsBeforePipe; 5 | 6 | beforeEach(() => (pipe = new IsBeforePipe())); 7 | 8 | describe('#transform', () => { 9 | it('should return true if value is before otherValue', () => { 10 | const test = new Date(2018, 11, 13, 0, 0, 0); 11 | const testDate1 = new Date(2018, 11, 15, 0, 0, 0); 12 | expect(pipe.transform(test, testDate1)).toBeTruthy(); 13 | }); 14 | 15 | it('should support passing "year", "month", "week", "day", etc as a unit parameter', () => { 16 | const test = new Date(2018, 0, 13, 12, 45, 45); 17 | const testDate1 = new Date(2019, 0, 13, 12, 45, 45); 18 | expect(pipe.transform(test, testDate1, 'year')).toBe(true); 19 | const testDate2 = new Date(2018, 1, 13, 12, 45, 45); 20 | expect(pipe.transform(test, testDate2, 'month')).toBe(true); 21 | const testDate3 = new Date(2018, 1, 13, 12, 45, 45); 22 | expect(pipe.transform(test, testDate3, 'day')).toBe(true); 23 | }); 24 | 25 | it('should return false if value is after otherValue', () => { 26 | const test = new Date(2018, 11, 15, 0, 0, 0); 27 | const testDate1 = new Date(2018, 11, 13, 0, 0, 0); 28 | expect(pipe.transform(test, testDate1)).toBeFalsy(); 29 | }); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /src/is-before.pipe.ts: -------------------------------------------------------------------------------- 1 | import moment from 'moment'; 2 | 3 | import { Pipe, PipeTransform } from '@angular/core'; 4 | 5 | @Pipe({ 6 | name: 'amIsBefore', 7 | }) 8 | export class IsBeforePipe implements PipeTransform { 9 | transform( 10 | value: moment.MomentInput, 11 | otherValue: moment.MomentInput, 12 | unit?: moment.unitOfTime.StartOf, 13 | ): boolean { 14 | return moment(value).isBefore(moment(otherValue), unit); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/local.pipe.spec.ts: -------------------------------------------------------------------------------- 1 | import moment from 'moment'; 2 | import { DateFormatPipe } from './date-format.pipe'; 3 | import { LocalTimePipe } from './local.pipe'; 4 | 5 | describe('LocalPipe', () => { 6 | describe('#transform', () => { 7 | let localPipe: LocalTimePipe; 8 | 9 | beforeEach(() => { 10 | localPipe = new LocalTimePipe(); 11 | }); 12 | 13 | it('should output an invalid momemt object for a null input', () => { 14 | const localDate = localPipe.transform(null); 15 | expect(localDate).toEqual(expect.any(moment)); 16 | expect(localDate.isValid()).toBe(false); 17 | }); 18 | 19 | it('should output a moment object for a moment input', () => { 20 | const momentDate = moment(); 21 | const localDate = localPipe.transform(momentDate); 22 | expect(localDate).toEqual(expect.any(moment)); 23 | expect(localDate.isValid()).toBe(true); 24 | }); 25 | 26 | it('should output a moment object for a date input', () => { 27 | const date = new Date(); 28 | const localDate = localPipe.transform(date); 29 | expect(localDate).toEqual(expect.any(moment)); 30 | expect(localDate.isValid()).toBe(true); 31 | }); 32 | 33 | it('should output a moment object for a string date', () => { 34 | const dateString = '2016-01-01'; 35 | const localDate = localPipe.transform(dateString); 36 | expect(localDate).toEqual(expect.any(moment)); 37 | expect(localDate.isValid()).toBe(true); 38 | }); 39 | 40 | it('should output a moment object for a timestamp', () => { 41 | const timestamp: number = Date.now(); 42 | const localDate = localPipe.transform(timestamp); 43 | expect(localDate).toEqual(expect.any(moment)); 44 | expect(localDate.isValid()).toBe(true); 45 | }); 46 | 47 | it('should be pipeable to amDateFormat', () => { 48 | const amDateFormat = new DateFormatPipe(); 49 | const datetimeString = '2015-12-31T23:00:00.000-15:00'; 50 | const momentFormatString = 'YYYY-MM'; 51 | const localOutput = localPipe.transform(datetimeString); 52 | expect(amDateFormat.transform(localOutput, momentFormatString)).toEqual('2016-01'); 53 | }); 54 | }); 55 | }); 56 | -------------------------------------------------------------------------------- /src/local.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | import moment from 'moment'; 3 | 4 | @Pipe({ name: 'amLocal' }) 5 | export class LocalTimePipe implements PipeTransform { 6 | transform(value: moment.MomentInput): moment.Moment { 7 | return moment(value).local(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/locale.pipe.spec.ts: -------------------------------------------------------------------------------- 1 | import moment from 'moment'; 2 | import { DateFormatPipe } from './date-format.pipe'; 3 | import { LocalePipe } from './locale.pipe'; 4 | 5 | describe('LocalePipe', () => { 6 | describe('#transform', () => { 7 | let localePipe: LocalePipe; 8 | 9 | beforeEach(() => { 10 | localePipe = new LocalePipe(); 11 | }); 12 | 13 | it('should output a moment object for a string date', () => { 14 | const datetimeString = '2016-01-24 01:23:45'; 15 | const langKeyString1 = 'en'; 16 | const langKeyString2 = 'de'; 17 | const parsedMoment1 = localePipe.transform(datetimeString, langKeyString1); 18 | const parsedMoment2 = localePipe.transform(datetimeString, langKeyString2); 19 | expect(parsedMoment1).toEqual(expect.any(moment)); 20 | expect(parsedMoment2).toEqual(expect.any(moment)); 21 | expect(parsedMoment1.isValid()).toBe(true); 22 | expect(parsedMoment2.isValid()).toBe(true); 23 | }); 24 | 25 | it('should be pipeable to amDateFormat', () => { 26 | const amDateFormat = new DateFormatPipe(); 27 | const datetimeString = '2016-01-24 14:23:45'; 28 | const langKeyString1 = 'en'; 29 | const langKeyString2 = 'de'; 30 | const momentFormatString1 = 'MMMM Do YYYY, h:mm:ss a'; 31 | const momentFormatString2 = 'MMMM Do YYYY, HH:mm:ss'; 32 | const parseOutput1 = localePipe.transform(datetimeString, langKeyString1); 33 | const parseOutput2 = localePipe.transform(datetimeString, langKeyString2); 34 | expect(amDateFormat.transform(parseOutput1, momentFormatString1)).toEqual( 35 | 'January 24th 2016, 2:23:45 pm', 36 | ); 37 | expect(amDateFormat.transform(parseOutput2, momentFormatString2)).toEqual( 38 | 'Januar 24. 2016, 14:23:45', 39 | ); 40 | }); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /src/locale.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | import moment from 'moment'; 3 | 4 | @Pipe({ name: 'amLocale' }) 5 | export class LocalePipe implements PipeTransform { 6 | transform(value: moment.MomentInput, locale: string): moment.Moment { 7 | return moment(value).locale(locale); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/moment-options.ts: -------------------------------------------------------------------------------- 1 | import { InjectionToken } from '@angular/core'; 2 | 3 | export const NGX_MOMENT_OPTIONS: InjectionToken = new InjectionToken< 4 | NgxMomentOptions 5 | >('NGX_MOMENT_OPTIONS'); 6 | 7 | export interface NgxMomentOptions { 8 | /** 9 | * relativeTimeThresholdOptions 10 | * @description Provides the `relativeTimeThreshold` units allowing a pipe to set the `moment.relativeTimeThreshold` values. 11 | * The `key` is a unit defined as one of `ss`, `s`, `m`, `h`, `d`, `M`. 12 | * @see https://momentjs.com/docs/#/customization/relative-time-threshold/ 13 | * @example by default more than 45 seconds is considered a minute, more than 22 hours is considered a day and so on. 14 | * So settings the unit 'm' to `59` will adjust the `relativeTimeThreshold` and consider more than 59 minutes 15 | * to be an hour (default is `45 minutes`) 16 | */ 17 | relativeTimeThresholdOptions: { [key: string]: number }; 18 | } 19 | -------------------------------------------------------------------------------- /src/moment.module.ts: -------------------------------------------------------------------------------- 1 | import { ModuleWithProviders, NgModule } from '@angular/core'; 2 | import { NGX_MOMENT_OPTIONS, NgxMomentOptions } from './moment-options'; 3 | 4 | import { AddPipe } from './add.pipe'; 5 | import { CalendarPipe } from './calendar.pipe'; 6 | import { DateFormatPipe } from './date-format.pipe'; 7 | import { DifferencePipe } from './difference.pipe'; 8 | import { DurationPipe } from './duration.pipe'; 9 | import { FromUnixPipe } from './from-unix.pipe'; 10 | import { FromUtcPipe } from './from-utc.pipe'; 11 | import { IsAfterPipe } from './is-after.pipe'; 12 | import { IsBeforePipe } from './is-before.pipe'; 13 | import { LocalTimePipe } from './local.pipe'; 14 | import { LocalePipe } from './locale.pipe'; 15 | import { ParsePipe } from './parse.pipe'; 16 | import { ParseZonePipe } from './parse-zone.pipe'; 17 | import { SubtractPipe } from './subtract.pipe'; 18 | import { TimeAgoPipe } from './time-ago.pipe'; 19 | import { UtcPipe } from './utc.pipe'; 20 | 21 | const ANGULAR_MOMENT_PIPES = [ 22 | AddPipe, 23 | CalendarPipe, 24 | DateFormatPipe, 25 | DifferencePipe, 26 | DurationPipe, 27 | FromUnixPipe, 28 | ParsePipe, 29 | SubtractPipe, 30 | TimeAgoPipe, 31 | UtcPipe, 32 | FromUtcPipe, 33 | LocalTimePipe, 34 | LocalePipe, 35 | ParseZonePipe, 36 | IsBeforePipe, 37 | IsAfterPipe, 38 | ]; 39 | 40 | @NgModule({ 41 | declarations: ANGULAR_MOMENT_PIPES, 42 | exports: ANGULAR_MOMENT_PIPES, 43 | }) 44 | export class MomentModule { 45 | static forRoot(options?: NgxMomentOptions): ModuleWithProviders { 46 | return { 47 | ngModule: MomentModule, 48 | providers: [ 49 | { 50 | provide: NGX_MOMENT_OPTIONS, 51 | useValue: { 52 | ...options, 53 | }, 54 | }, 55 | ], 56 | }; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/parse-zone.pipe.spec.ts: -------------------------------------------------------------------------------- 1 | import moment from 'moment'; 2 | import { ParseZonePipe } from './parse-zone.pipe'; 3 | 4 | describe('ParseZonePipe', () => { 5 | describe('#transform', () => { 6 | let parseZonePipe: ParseZonePipe; 7 | 8 | beforeEach(() => { 9 | parseZonePipe = new ParseZonePipe(); 10 | }); 11 | 12 | it('should output an invalid momemt object for a null input', () => { 13 | const parsedDate = parseZonePipe.transform(null); 14 | expect(parsedDate).toEqual(expect.any(moment)); 15 | expect(parsedDate.isValid()).toBe(false); 16 | }); 17 | 18 | it('should parse a date in ISO_8601 format', () => { 19 | const date: string = moment().toISOString(); 20 | const parsedDate = parseZonePipe.transform(date); 21 | expect(parsedDate).toEqual(expect.any(moment)); 22 | expect(parsedDate.isValid()).toBe(true); 23 | }); 24 | 25 | it('should preserve a positive UTC offset', () => { 26 | const offset = 720; 27 | const date = moment().utcOffset(offset).format(); 28 | const parsedDate = parseZonePipe.transform(date); 29 | expect(parsedDate.utcOffset()).toEqual(offset); 30 | }); 31 | 32 | it('should preserve a negative UTC offset', () => { 33 | const offset = -720; 34 | const date = moment().utcOffset(offset).format(); 35 | const parsedDate = parseZonePipe.transform(date); 36 | expect(parsedDate.utcOffset()).toEqual(offset); 37 | }); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /src/parse-zone.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | import moment from 'moment'; 3 | 4 | @Pipe({ name: 'amParseZone' }) 5 | export class ParseZonePipe implements PipeTransform { 6 | transform(value: moment.MomentInput): moment.Moment { 7 | return moment.parseZone(value); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/parse.pipe.spec.ts: -------------------------------------------------------------------------------- 1 | import moment from 'moment'; 2 | import { DateFormatPipe } from './date-format.pipe'; 3 | import { ParsePipe } from './parse.pipe'; 4 | 5 | describe('ParsePipe', () => { 6 | describe('#transform', () => { 7 | let parsePipe: ParsePipe; 8 | 9 | beforeEach(() => { 10 | parsePipe = new ParsePipe(); 11 | }); 12 | 13 | it('should output a moment object for a string date', () => { 14 | const dateString = '2015#09#13'; 15 | const formatInputString = 'YYYY#MM#DD'; 16 | const parsedMoment = parsePipe.transform(dateString, formatInputString); 17 | expect(parsedMoment).toEqual(expect.any(moment)); 18 | expect(parsedMoment.isValid()).toBe(true); 19 | 20 | expect(parsedMoment.year()).toBe(2015); 21 | expect(parsedMoment.month()).toBe(8); 22 | expect(parsedMoment.date()).toBe(13); 23 | }); 24 | 25 | it('should be pipeable to amDateFormat', () => { 26 | const amDateFormat = new DateFormatPipe(); 27 | const datetimeString = '01/02/2016'; 28 | const formatInputString = 'DD/MM/YYYY'; 29 | const momentFormatString = 'YYYY-MM-DD'; 30 | const parseOutput = parsePipe.transform(datetimeString, formatInputString); 31 | expect(amDateFormat.transform(parseOutput, momentFormatString)).toEqual('2016-02-01'); 32 | }); 33 | 34 | it('should output a moment object for a string date with array of formats', () => { 35 | const dateString = '15--09//13'; 36 | const formatInputStrings = ['YYYY#MM#DD', 'YY--MM//DD']; 37 | const parsedMoment = parsePipe.transform(dateString, formatInputStrings); 38 | expect(parsedMoment).toEqual(expect.any(moment)); 39 | expect(parsedMoment.isValid()).toBe(true); 40 | 41 | expect(parsedMoment.year()).toBe(2015); 42 | expect(parsedMoment.month()).toBe(8); 43 | expect(parsedMoment.date()).toBe(13); 44 | }); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /src/parse.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | import moment from 'moment'; 3 | 4 | @Pipe({ name: 'amParse' }) 5 | export class ParsePipe implements PipeTransform { 6 | transform(value: moment.MomentInput, formats: string | string[]): moment.Moment { 7 | return moment(value, formats); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/subtract.pipe.spec.ts: -------------------------------------------------------------------------------- 1 | import moment from 'moment'; 2 | import { SubtractPipe } from './subtract.pipe'; 3 | 4 | describe('SubtractPipe', () => { 5 | let pipe: SubtractPipe; 6 | 7 | beforeEach(() => (pipe = new SubtractPipe())); 8 | 9 | describe('#transform', () => { 10 | it('should throw when provided no arguments', () => { 11 | expect(() => (pipe.transform as any)(128)).toThrow( 12 | 'SubtractPipe: missing required arguments', 13 | ); 14 | }); 15 | 16 | it('should subtract two hours', () => { 17 | const result = pipe.transform(moment('2016-01-24 15:00:00'), '2', 'hours'); 18 | expect(moment(result).format('YYYY-MM-DD HH:mm:ss')).toBe('2016-01-24 13:00:00'); 19 | }); 20 | 21 | it('should subtract two days', () => { 22 | const result = pipe.transform(moment('2016-01-24 15:00:00'), '2', 'days'); 23 | expect(moment(result).format('YYYY-MM-DD HH:mm:ss')).toBe('2016-01-22 15:00:00'); 24 | }); 25 | 26 | it('should subtract two years', () => { 27 | const result = pipe.transform(moment('2016-01-24 15:00:00'), '2', 'years'); 28 | expect(moment(result).format('YYYY-MM-DD HH:mm:ss')).toBe('2014-01-24 15:00:00'); 29 | }); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /src/subtract.pipe.ts: -------------------------------------------------------------------------------- 1 | /* ngx-moment (c) 2015, 2016 Uri Shaked / MIT Licence */ 2 | 3 | import { Pipe, PipeTransform } from '@angular/core'; 4 | import moment from 'moment'; 5 | 6 | @Pipe({ name: 'amSubtract' }) 7 | export class SubtractPipe implements PipeTransform { 8 | transform( 9 | value: moment.MomentInput, 10 | amount: moment.DurationInputArg1, 11 | unit?: moment.DurationInputArg2, 12 | ): any { 13 | if ( 14 | typeof amount === 'undefined' || 15 | (typeof amount === 'number' && typeof unit === 'undefined') 16 | ) { 17 | throw new Error('SubtractPipe: missing required arguments'); 18 | } 19 | return moment(value).subtract(amount, unit); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/time-ago.pipe.spec.ts: -------------------------------------------------------------------------------- 1 | import { NgZone } from '@angular/core'; 2 | import { TimeAgoPipe } from './time-ago.pipe'; 3 | import moment from 'moment'; 4 | import 'moment/min/locales'; 5 | 6 | declare var global: any; 7 | 8 | jest.useFakeTimers(); 9 | 10 | class NgZoneMock { 11 | runOutsideAngular(fn: Function) { 12 | return fn(); 13 | } 14 | run(fn: Function) { 15 | return fn(); 16 | } 17 | } 18 | 19 | const _Date = Date; 20 | 21 | function fakeDate(defaultDate: string | number) { 22 | global.Date = function (arg: any) { 23 | return new _Date(typeof arg !== 'undefined' ? arg : defaultDate); 24 | }; 25 | global.Date.UTC = _Date.UTC; 26 | } 27 | 28 | describe('TimeAgoPipe', () => { 29 | describe('#transform', () => { 30 | beforeEach(() => { 31 | moment.locale('en-gb'); 32 | }); 33 | 34 | afterEach(() => { 35 | global.Date = _Date; 36 | jest.clearAllTimers(); 37 | }); 38 | 39 | it('should transform the current date to "a few seconds ago"', () => { 40 | const pipe = new TimeAgoPipe(null, new NgZoneMock() as NgZone); 41 | expect(pipe.transform(new Date())).toBe('a few seconds ago'); 42 | }); 43 | 44 | it('should support string dates', () => { 45 | const pipe = new TimeAgoPipe(null, new NgZoneMock() as NgZone); 46 | const dateStr = new Date().toISOString(); 47 | expect(pipe.transform(dateStr)).toBe('a few seconds ago'); 48 | }); 49 | 50 | it('should omit the suffix if second parameter is truthy', () => { 51 | const pipe = new TimeAgoPipe(null, new NgZoneMock() as NgZone); 52 | expect(pipe.transform(new Date(new Date().getTime() + 60000), true)).toBe('a minute'); 53 | }); 54 | 55 | it('should automatically update the text as time passes', () => { 56 | const changeDetectorMock = { markForCheck: jest.fn() }; 57 | const pipe = new TimeAgoPipe(changeDetectorMock as any, new NgZoneMock() as NgZone); 58 | expect(pipe.transform(new Date())).toBe('a few seconds ago'); 59 | expect(changeDetectorMock.markForCheck).not.toHaveBeenCalled(); 60 | jest.advanceTimersByTime(60000); 61 | expect(changeDetectorMock.markForCheck).toHaveBeenCalled(); 62 | }); 63 | 64 | it('should update the text with a new date instance different from the previous one', () => { 65 | const changeDetectorMock = { markForCheck: jest.fn() }; 66 | const pipe = new TimeAgoPipe(changeDetectorMock as any, new NgZoneMock() as NgZone); 67 | fakeDate('2016-05-01'); 68 | expect(pipe.transform(new Date())).toBe('a few seconds ago'); 69 | expect(pipe.transform(new Date(0))).toBe('46 years ago'); 70 | expect(pipe.transform(moment())).toBe('a few seconds ago'); 71 | expect(pipe.transform(moment(0))).toBe('46 years ago'); 72 | }); 73 | 74 | it('should update the text when moment locale changes', () => { 75 | const changeDetectorMock = { markForCheck: jest.fn() }; 76 | const pipe = new TimeAgoPipe(changeDetectorMock as any, new NgZoneMock() as NgZone); 77 | fakeDate('2016-05-01'); 78 | expect(pipe.transform(moment(0))).toBe('46 years ago'); 79 | expect(pipe.transform(moment(0).locale('pl'))).toBe('46 lat temu'); 80 | }); 81 | 82 | it('should reset language when localized moment changes to Date', () => { 83 | const changeDetectorMock = { markForCheck: jest.fn() }; 84 | const pipe = new TimeAgoPipe(changeDetectorMock as any, new NgZoneMock() as NgZone); 85 | fakeDate('2016-05-01'); 86 | expect(pipe.transform(moment(0).locale('pl'))).toBe('46 lat temu'); 87 | expect(pipe.transform(new Date(0))).toBe('46 years ago'); 88 | }); 89 | 90 | it('should update the text when using Date Objects and locale changes', () => { 91 | const changeDetectorMock = { markForCheck: jest.fn() }; 92 | const pipe = new TimeAgoPipe(changeDetectorMock as any, new NgZoneMock() as NgZone); 93 | fakeDate('2016-05-01'); 94 | expect(pipe.transform(new Date(0))).toBe('46 years ago'); 95 | moment.locale('de'); 96 | expect(pipe.transform(new Date(0))).toBe('vor 46 Jahren'); 97 | }); 98 | 99 | it('should update the text when the date instance time is updated', () => { 100 | const changeDetectorMock = { markForCheck: jest.fn() }; 101 | const pipe = new TimeAgoPipe(changeDetectorMock as any, new NgZoneMock() as NgZone); 102 | fakeDate('2016-05-01'); 103 | const date = new Date(); 104 | expect(pipe.transform(date)).toBe('a few seconds ago'); 105 | date.setFullYear(2000); 106 | expect(pipe.transform(date)).toBe('16 years ago'); 107 | 108 | const dateAsMoment = moment(); 109 | expect(pipe.transform(dateAsMoment)).toBe('a few seconds ago'); 110 | dateAsMoment.year(2000); 111 | expect(pipe.transform(dateAsMoment)).toBe('16 years ago'); 112 | }); 113 | 114 | it('should remove all timers when destroyed', () => { 115 | const changeDetectorMock = { markForCheck: jest.fn() }; 116 | const pipe = new TimeAgoPipe(changeDetectorMock as any, new NgZoneMock() as NgZone); 117 | expect(pipe.transform(new Date())).toBe('a few seconds ago'); 118 | pipe.ngOnDestroy(); 119 | jest.advanceTimersByTime(60000); 120 | expect(changeDetectorMock.markForCheck).not.toHaveBeenCalled(); 121 | }); 122 | 123 | it('should transform a date to a different format when a format function is provided', () => { 124 | const changeDetectorMock = { markForCheck: jest.fn() }; 125 | const formatFnMock = (m: moment.Moment) => { 126 | const seconds = m.diff(moment()) / 1000; 127 | return seconds >= 3600 ? '' : `${Math.floor(seconds / 60)} min ago`; 128 | }; 129 | const pipe = new TimeAgoPipe(changeDetectorMock as any, new NgZoneMock() as NgZone); 130 | fakeDate('2016-05-01'); 131 | expect(pipe.transform(moment().add(20, 'm'), false, formatFnMock)).toBe('20 min ago'); 132 | expect(pipe.transform(moment().add(1, 'h'), false, formatFnMock)).toBe(''); 133 | expect(pipe.transform(moment().add(3, 'h'), false, formatFnMock)).toBe(''); 134 | }); 135 | }); 136 | }); 137 | -------------------------------------------------------------------------------- /src/time-ago.pipe.ts: -------------------------------------------------------------------------------- 1 | /* ngx-moment (c) 2015, 2016 Uri Shaked / MIT Licence */ 2 | 3 | import { Pipe, ChangeDetectorRef, PipeTransform, OnDestroy, NgZone } from '@angular/core'; 4 | import moment from 'moment'; 5 | 6 | @Pipe({ name: 'amTimeAgo', pure: false }) 7 | export class TimeAgoPipe implements PipeTransform, OnDestroy { 8 | private currentTimer: number | null; 9 | 10 | private lastTime: Number; 11 | private lastValue: moment.MomentInput; 12 | private lastOmitSuffix: boolean; 13 | private lastLocale?: string; 14 | private lastText: string; 15 | private formatFn: (m: moment.Moment) => string; 16 | 17 | constructor(private cdRef: ChangeDetectorRef, private ngZone: NgZone) {} 18 | 19 | format(m: moment.Moment) { 20 | return m.from(moment(), this.lastOmitSuffix); 21 | } 22 | 23 | transform( 24 | value: moment.MomentInput, 25 | omitSuffix?: boolean, 26 | formatFn?: (m: moment.Moment) => string, 27 | ): string { 28 | if (this.hasChanged(value, omitSuffix)) { 29 | this.lastTime = this.getTime(value); 30 | this.lastValue = value; 31 | this.lastOmitSuffix = omitSuffix; 32 | this.lastLocale = this.getLocale(value); 33 | this.formatFn = formatFn || this.format.bind(this); 34 | this.removeTimer(); 35 | this.createTimer(); 36 | this.lastText = this.formatFn(moment(value)); 37 | } else { 38 | this.createTimer(); 39 | } 40 | 41 | return this.lastText; 42 | } 43 | 44 | ngOnDestroy(): void { 45 | this.removeTimer(); 46 | } 47 | 48 | private createTimer() { 49 | if (this.currentTimer) { 50 | return; 51 | } 52 | 53 | const momentInstance = moment(this.lastValue); 54 | const timeToUpdate = this.getSecondsUntilUpdate(momentInstance) * 1000; 55 | 56 | this.currentTimer = this.ngZone.runOutsideAngular(() => { 57 | if (typeof window !== 'undefined') { 58 | return window.setTimeout(() => { 59 | this.lastText = this.formatFn(moment(this.lastValue)); 60 | 61 | this.currentTimer = null; 62 | this.ngZone.run(() => this.cdRef.markForCheck()); 63 | }, timeToUpdate); 64 | } else { 65 | return null; 66 | } 67 | }); 68 | } 69 | 70 | private removeTimer() { 71 | if (this.currentTimer) { 72 | window.clearTimeout(this.currentTimer); 73 | this.currentTimer = null; 74 | } 75 | } 76 | 77 | private getSecondsUntilUpdate(momentInstance: moment.Moment) { 78 | const howOld = Math.abs(moment().diff(momentInstance, 'minute')); 79 | if (howOld < 1) { 80 | return 1; 81 | } else if (howOld < 60) { 82 | return 30; 83 | } else if (howOld < 180) { 84 | return 300; 85 | } else { 86 | return 3600; 87 | } 88 | } 89 | 90 | private hasChanged(value: moment.MomentInput, omitSuffix?: boolean): boolean { 91 | return ( 92 | this.getTime(value) !== this.lastTime || 93 | this.getLocale(value) !== this.lastLocale || 94 | omitSuffix !== this.lastOmitSuffix 95 | ); 96 | } 97 | 98 | private getTime(value: moment.MomentInput): number { 99 | if (moment.isDate(value)) { 100 | return value.getTime(); 101 | } else if (moment.isMoment(value)) { 102 | return value.valueOf(); 103 | } else { 104 | return moment(value).valueOf(); 105 | } 106 | } 107 | 108 | private getLocale(value: moment.MomentInput): string | null { 109 | return moment.isMoment(value) ? value.locale() : moment.locale(); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/utc.pipe.spec.ts: -------------------------------------------------------------------------------- 1 | import moment from 'moment'; 2 | import { DateFormatPipe } from './date-format.pipe'; 3 | import { UtcPipe } from './utc.pipe'; 4 | 5 | describe('UtcPipe', () => { 6 | describe('#transform', () => { 7 | let utcDatePipe: UtcPipe; 8 | 9 | beforeEach(() => { 10 | utcDatePipe = new UtcPipe(); 11 | }); 12 | 13 | it('should output an invalid momemt object for a null input', () => { 14 | const utcDate = utcDatePipe.transform(null); 15 | expect(utcDate).toEqual(expect.any(moment)); 16 | expect(utcDate.isValid()).toBe(false); 17 | }); 18 | 19 | it('should output a moment object for a moment input', () => { 20 | const momentDate = moment(); 21 | const utcDate = utcDatePipe.transform(momentDate); 22 | expect(utcDate).toEqual(expect.any(moment)); 23 | expect(utcDate.isValid()).toBe(true); 24 | }); 25 | 26 | it('should output a moment object for a date input', () => { 27 | const date = new Date(); 28 | const utcDate = utcDatePipe.transform(date); 29 | expect(utcDate).toEqual(expect.any(moment)); 30 | expect(utcDate.isValid()).toBe(true); 31 | }); 32 | 33 | it('should output a moment object for a string date', () => { 34 | const dateString = '2016-01-01'; 35 | const utcDate = utcDatePipe.transform(dateString); 36 | expect(utcDate).toEqual(expect.any(moment)); 37 | expect(utcDate.isValid()).toBe(true); 38 | }); 39 | 40 | it('should output a moment object for a timestamp', () => { 41 | const timestamp: number = Date.now(); 42 | const utcDate = utcDatePipe.transform(timestamp); 43 | expect(utcDate).toEqual(expect.any(moment)); 44 | expect(utcDate.isValid()).toBe(true); 45 | }); 46 | 47 | it('should be pipeable to amDateFormat', () => { 48 | const amDateFormat = new DateFormatPipe(); 49 | const datetimeString = '2015-12-31T23:00:00.000-01:00'; 50 | const momentFormatString = 'YYYY-MM-DD'; 51 | const utcOutput = utcDatePipe.transform(datetimeString); 52 | expect(amDateFormat.transform(utcOutput, momentFormatString)).toEqual('2016-01-01'); 53 | }); 54 | }); 55 | }); 56 | -------------------------------------------------------------------------------- /src/utc.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core'; 2 | import moment from 'moment'; 3 | 4 | @Pipe({ name: 'amUtc' }) 5 | export class UtcPipe implements PipeTransform { 6 | transform(value: moment.MomentInput): moment.Moment { 7 | return moment(value).utc(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "compileOnSave": false, 4 | "compilerOptions": { 5 | "baseUrl": "./", 6 | "outDir": "./dist/out-tsc", 7 | "forceConsistentCasingInFileNames": true, 8 | "strict": true, 9 | "strictNullChecks": false, 10 | "noImplicitOverride": true, 11 | "noPropertyAccessFromIndexSignature": true, 12 | "noImplicitReturns": true, 13 | "noFallthroughCasesInSwitch": true, 14 | "sourceMap": true, 15 | "declaration": true, 16 | "declarationMap": false, 17 | "inlineSources": true, 18 | "downlevelIteration": true, 19 | "experimentalDecorators": true, 20 | "esModuleInterop": true, 21 | "allowSyntheticDefaultImports": true, 22 | "moduleResolution": "node", 23 | "importHelpers": true, 24 | "target": "es2017", 25 | "module": "es2020", 26 | "lib": [ 27 | "es2020", 28 | "dom" 29 | ] 30 | }, 31 | "angularCompilerOptions": { 32 | "compilationMode": "partial", 33 | "enableI18nLegacyMessageIdFormat": false, 34 | "strictInjectionParameters": true, 35 | "strictInputAccessModifiers": true, 36 | "strictTemplates": true 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /tsconfig.lint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/spec", 5 | "module": "es2015", 6 | "types": [] 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "noEmit": true, 5 | "strictNullChecks": false, 6 | "types": [ 7 | "jest", 8 | "node" 9 | ] 10 | }, 11 | "include": [ 12 | "src/*.spec.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "arrow-return-shorthand": true, 4 | "callable-types": true, 5 | "class-name": true, 6 | "comment-format": [ 7 | true, 8 | "check-space" 9 | ], 10 | "curly": true, 11 | "deprecation": { 12 | "severity": "warn" 13 | }, 14 | "eofline": true, 15 | "forin": true, 16 | "import-blacklist": [ 17 | true, 18 | "rxjs/Rx" 19 | ], 20 | "import-spacing": true, 21 | "indent": [ 22 | true, 23 | "spaces" 24 | ], 25 | "interface-over-type-literal": true, 26 | "label-position": true, 27 | "max-line-length": [ 28 | true, 29 | 140 30 | ], 31 | "member-access": false, 32 | "member-ordering": [ 33 | true, 34 | { 35 | "order": [ 36 | "static-field", 37 | "instance-field", 38 | "instance-method", 39 | "static-method" 40 | ] 41 | } 42 | ], 43 | "no-arg": true, 44 | "no-bitwise": true, 45 | "no-console": [ 46 | true, 47 | "debug", 48 | "info", 49 | "time", 50 | "timeEnd", 51 | "trace" 52 | ], 53 | "no-construct": true, 54 | "no-debugger": true, 55 | "no-duplicate-super": true, 56 | "no-empty": false, 57 | "no-empty-interface": true, 58 | "no-eval": true, 59 | "no-inferrable-types": [ 60 | true, 61 | "ignore-params" 62 | ], 63 | "no-misused-new": true, 64 | "no-non-null-assertion": true, 65 | "no-shadowed-variable": true, 66 | "no-string-literal": false, 67 | "no-string-throw": true, 68 | "no-switch-case-fall-through": true, 69 | "no-trailing-whitespace": true, 70 | "no-unnecessary-initializer": true, 71 | "no-unused-expression": true, 72 | "no-use-before-declare": true, 73 | "no-var-keyword": true, 74 | "object-literal-sort-keys": false, 75 | "one-line": [ 76 | true, 77 | "check-open-brace", 78 | "check-catch", 79 | "check-else", 80 | "check-whitespace" 81 | ], 82 | "prefer-const": true, 83 | "quotemark": [ 84 | true, 85 | "single" 86 | ], 87 | "radix": true, 88 | "semicolon": [ 89 | true, 90 | "always" 91 | ], 92 | "triple-equals": [ 93 | true, 94 | "allow-null-check" 95 | ], 96 | "typedef-whitespace": [ 97 | true, 98 | { 99 | "call-signature": "nospace", 100 | "index-signature": "nospace", 101 | "parameter": "nospace", 102 | "property-declaration": "nospace", 103 | "variable-declaration": "nospace" 104 | } 105 | ], 106 | "unified-signatures": true, 107 | "variable-name": false, 108 | "whitespace": [ 109 | true, 110 | "check-branch", 111 | "check-decl", 112 | "check-operator", 113 | "check-separator", 114 | "check-type" 115 | ] 116 | } 117 | } 118 | --------------------------------------------------------------------------------