How to get the meridian (am / pm) with momentsjs and angularjs
Solution 1
A few things:
The word you're looking for is "meridiem", not "meridian". It's a Latin word meaning "mid-day". A.M. is "ante meridiem" (before mid-day) and P.M. is "post meridiem" (after mid-day).
The
.hours()
function and the input array passed to themoment
constructor both expect hours of a 24-hour clock, from 0 to 23. There is no built-in function to accept or output a 12-hour clock value, or a meridiem (am/pm).The parsing and formatting functions in moment do have options for 12-hour clock hours (
h
orhh
) and meridiem (A
ora
), but you're not parsing or formatting in your use case, so they would be a bit clumsy to use. In particular, they are based on moment's current locale settings, so testing a string would be problematic if the locale wasn't fixed to a specific language.-
You can use these simple functions to convert from 12-hour + meridiem to 24-hour and vice-versa. They are not specific to moment, so you can also use them with the
Date
object or elsewhere.function hours12to24(h, pm) { return h == 12 ? pm ? 12 : 0 : pm ? h + 12 : h; } function hours24to12(h) { return { hour : (h + 11) % 12 + 1, pm : h >= 12 } }
To test these functions:
function test() { for (var i = 0; i <= 23; i++) { var x = hours24to12(i); var h = hours12to24(x.hour, x.pm); console.log(i + " == " + x.hour + (x.pm ? " pm" : " am") + " == " + h); } }
Also, be careful when calling
.month()
to get the month number, the results are 0-11, not 1-12. The same is true when building the input array. Your dropdown either needs to use 0-11, or you need to add or subtract 1 accordingly.
Solution 2
You could also do something like m.format('a')
Solution 3
The other answer is very good. It helped me to create my demo because with-out it I would have missed the point with the month (0 to 11).
But you don't need to create the meridiem function because it's already implemented in moment.js
. The point is that the usage is not very clear from the docs.
You can use the meridiem
function like this:
var curDate = moment().hour(yourHour); // create a moment date with the hour you'd like to test
var meridiem = curDate
.localeData().meridiem(hour); // will return AM/PM String
If you need a boolean instead of AM/PM
string you can pass the AM/PM string to curDate.localeData().isPM(meridiem)
. That will return true or false.
The point with the month that you don't want to display 0 to 11 in your select can be fixed by a displayLabel
function that will increment every value of the month array. Then you'll have it displayed 1 to 12 but stored as 0 to 11. If you're writing a directive that could be better handled by a $parser / $formatter
function of ngModel
.
Please have a look at the demo below or at this jsfiddle.
angular.module('demoApp', [])
.filter('momentDate', MomentDateFilter)
.filter('momentUTC', MomentUTCFilter)
.controller('MainController', MainController);
function MomentDateFilter() {
return function(input, format) {
return moment(input).format(format);
};
}
function MomentUTCFilter() {
return function(input, format) {
return moment.utc(input).format(format);
};
}
function MainController($scope, $log) {
var vm = this,
now = moment();
vm.checkHour = checkHour;
vm.dateSelect = dateSelection();
vm.displayLabel = displayLabel;
vm.now = now;
vm.selected = {
"day": 27,
"month": 8,
"year": 2015,
"hour": 18,
"meridiem": "PM",
"minutes": 6,
"seconds": 20
};
function dateSelection() {
return { // generated on every page load for demo
// better store the generate object as json and load it
day: createRange(1,31),
month: createRange(0,11),
year: createRange(1900, 2100),
hour: createRange(0,24),
minutes: createRange(0,59),
seconds: createRange(0,59),
meridiem: 'AM_PM'.split('_')
};
}
function displayLabel(key, value) {
if (key === 'month') {
value++; // increment month for correct month value 1 to 12
}
return value;
}
function checkHour(key, hour) { // updates meridiem (AM/PM)
if (key === 'hour') {
var curDate = moment().hour(hour);
//console.log('check hour', hour, curDate.hour(), curDate.localeData());
vm.selected.meridiem = curDate
.localeData().meridiem(hour);
//console.log(curDate
// .localeData().isPM(vm.selected.meridiem));
}
//console.log('changed', key);
}
//console.log(this.dateSelect);
function createRange(from, to) {
var arr = [];
for(i=from; i<=to; i++) {
arr.push(i);
}
//console.log(arr);
return arr;
}
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0-beta.0/angular.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.10.6/moment.js"></script>
<div ng-app="demoApp" ng-controller="MainController as mainCtrl" class="container-fluid">
<form class="form-inline">
<div ng-repeat="(key, array) in mainCtrl.dateSelect" class="form-group">
<label>{{key}}</label><select class="form-control" ng-change="mainCtrl.checkHour(key, array[mainCtrl.selected[key]])" ng-model="mainCtrl.selected[key]" ng-options="value as mainCtrl.displayLabel(key, value) for value in mainCtrl.dateSelect[key]" ng-disabled="key === 'meridiem'">
</select>
</div>
</form>
selected: <pre>{{mainCtrl.selected|json}}</pre>
raw date (unformatted, UTC): {{mainCtrl.selected | momentUTC}}<br/>
date formatted (meridiem locale): {{mainCtrl.selected | momentUTC : 'LLLL' }}<br/>
now (momentjs) {{mainCtrl.now}}
</div>
ojedawinder
Updated on July 10, 2022Comments
-
ojedawinder almost 2 years
I have a question about the library moments.js, I have an application in angularjs where I have six select elements for the year, month, day, hour, minutes, and am / pm format. I am using the following format moment to build m.format date ('YYYY-MM-DD hh: mm: ss a).
The code is as follows:
var m = moment([scope.val.year, scope.val.month, scope.val.date, scope.val.hour, scope.val.minute]); //build date model.$setViewValue(m.format('YYYY-MM-DD hh:mm:ss a'))
I get the data
m.hour ()
,m.minute ()
, etc but there is a way to get the am / pm format, I have not found anything about it, something perhaps asm.meridian () => "am "or" pm "
.I would build the array passing as parameter if am or pm, and then also would get from any date if am or pm date.
-
ojedawinder over 8 yearsThanks, it was very useful to me your solution.
-
ojedawinder over 8 yearsThanks for your help, the solution of Matt and your solution worked for me.