Date().toDateString()

Posted on
Page
of 2
/ 2
Next
  • would something like this make sense to be a part of Espruinos Date() class ?

    Date.prototype.toDateString = function(format) {
    /*
    yyyy    Year,  long form (e.g., 2016).
    mm      Numeric month, a number from 1 to 12.
    dd      Day, a number from 1 to 31.
    HH      Hour, a number from 0 to 23.
    MM      Minutes, a number from 0 to 59.
    ss      Seconds, a number from 0 to 61 (59 plus a maximum of two leap seconds).
    */
    
      if (format) {
        // yyyy
        var YYYY = this.getFullYear().toString();
        format = format.replace("yyyy",YYYY);
        // mm
        var mm = (this.getMonth() + 1).toString();
        mm = mm.length > 1 ? mm : '0' + mm;
        format = format.replace("mm",mm);
        //dd
        var dd = this.getDate().toString();
        dd = dd.length > 1 ? dd : '0' + dd;
        format = format.replace("dd",dd);
        //HH
        var HH = this.getHours().toString();
        HH = HH.length > 1 ? HH : '0' + HH;
        format = format.replace("HH",HH);
        //MM
        var MM = this.getMinutes().toString();
        MM = MM.length > 1 ? MM : '0' + MM;
        format = format.replace("MM",MM);
        //SS
        var SS = this.getSeconds().toString();
        SS = SS.length > 1 ? SS : '0' + SS;
        format = format.replace("SS",SS);
        return format; 
      }
      else {
        format = this.toString();
      } 
    };
    
    >Date().toDateString();
    ="Tue Jul 19 2016 08:13:46 GMT+0000"
    >Date().toDateString("yyyy.mm.dd HH:MM:SS");
    ="2016.07.19 08:13:12"
    
    
  • ...such things get me going... from a private (messages) conversation:

    Some food for thought... you will also see some typical dynamic Javascript constructs in this code.

    In some contexts one likes to separate the data from the presentation... two classes are used: The first one is - obviously - the Javascript built-in Date class, and the second one is the related ...Formatter class. This allows a mix and match with different formatters and to include the formatter only when needed.

    new DateFormatter().format(); // string w/ today in default format
    var tsFrmttr = new DateFormatter("yyyyMMddHHmmssSSS");
    tsFrmttr.format(); // now timestamp in human readable format
    

    Since this may look like a bit cumbersome, short and very short form have been introduced:

    var d = new Date((new Date().getTime()) - 24*60*60*1000); // an day ago
    DateFormatter.format(); // today in default format
    DateFormatter.format(null,"yyyyMMdd"); // today in sortable format
    DateFormatter.format(d); // yesterday in default format
    DateFormatter(null,null); // today in default format
    DateFormatter(d,"D, N d, y h:mx"); // yesterday same time
    

    The code would look like something below.

    var DateFormatter = (function(){ // for in-line use
    
    // DateFormatter.js module
    //
    // allObjects 20160507
    // 
    // yMdHmsSZhxXND are date and time pattern chars, escapable by ^ (default)
    // y-year, M-month, d-day, H-hour24, m-minute, s-second, S-milliSecond, Z-zone,
    // h-hour12, x/X-am|pm/AM|PM, N-Name of month, D-Name of Day, with consecutive
    // same chars defining length (1 char: variable length, numbers never truncated)
    // var DF = require("DateFormatter"); DF.updateDefaults({pattern:"d.M.y", ...});
    // var df = new DF("optionalPattern"); log(df.format(optDate,optPattern));
    // log(DF.format(optDate,optPattern)); log(DF(dateOrFalsy,patternOrFalsy));
    // Absent|undefined|null|0|false,"" (=falsy) enforce default (date: now, pattern:
    // defaults.pattern). NOTE: DF() direct use requires ALWAYS both parms present.
    //
    // use: regular: constructor new F(optPatt) | direct: F(dateOrFalsy,pattOrFalsy)
    var F = function() { // F(pattern) | F(date,pattern) - falsy = default
      var a = arguments; if (a.length > 1) { return new F().format(a[0],a[1]); }
      this.pattern = ((a.length > 0) && a[0]) ? a[0] : this.__proto__.defaults.pattern;
    }, p = F.prototype;
    // regular use: f = new F(optPattern); f.format(optDateOrNull,optPattern);
    p.defaults = { pattern: "M/d/y", esc: "^", year: "yyyy", zone: "ESP" 
        , dayLen:3, days: "SunMonTueWedThuFriSat"
        , monLen:3, mons: "JanFebMarAprMayJunJulAugSepOctNovDec" };
    p.getDefaults = function() { return p.defaults; };
    p.updateDefaults = function(defaults) { 
      for (var d in defaults) p.defaults[d] = defaults[d]; return this; };
    p.format = function(date, pattern) { 
        var d = (date) ? date : new Date(), pt = (pattern) ? pattern : this.pattern,
        s = "", e = p.defaults.esc, pl = pt.length, i = 0, c, f, l;
        while (i < pl) { c = pt.charAt(i); i++;
          if (c === e) { if (i < pl) { s += pt.charAt(i); i++; } 
          } else if ((f = this[c])) {
            l = 1; while ((i < pl) && (pt.charAt(i) === c)) { l++; i++; }
            s += f.apply(this,[c,d,l]); 
          } else { s += c; } }
      return s;
    };
    // short format ~class|static methods: F.format(optDateOrNull,optPattern);
    F.getDefaults = function() { return p.getDefaults(); };
    F.updateDefaults = function(defaults) { p.updateDefaults(defaults); return F; };
    F.format = function(date,pattern) { return (new F(pattern)).format(date); };
    // core formatters
    p.y = function(c,d,l) { return this._fn(d.getFullYear(),c,l); };
    p.M = function(c,d,l) { return (l < 3) ? this._fn(d.getMonth() + 1,c,l) : this.N(c,d,l); };
    p.N = function(c,d,l) { return this._ft(d.getMonth(),c,l,p.defaults.mon­s,p.defaults.monLen); };
    p.d = function(c,d,l) { return this._fn(d.getDate(),c,l); };
    p.H = function(c,d,l) { return this._fn(d.getHours(),c,l); };
    p.h = function(c,d,l) { return this._fn(d.getHours() % 12,c,l); };
    p.m = function(c,d,l) { return this._fn(d.getMinutes(),c,l); };
    p.s = function(c,d,l) { return this._fn(d.getSeconds(),c,l); };
    p.S = function(c,d,l) { return this._fn(d.getMilliseconds(),c,l); };
    p.x = function(c,d,l) { return ((d.getHours() < 12) ? "am" : "pm"); };
    p.X = function(c,d,l) { return ((d.getHours() < 12) ? "AM" : "PM"); };
    p.D = function(c,d,l) { return  this._ft(d.getDay(),c,l,p.defaults.days,­p.defaults.dayLen); };
    p.Z = function(c,d,l) { return this._ft(0,c,l,p.defaults.zone,p.default­s.zone.length); };
    p._ft = function(val,c,len,t,tLen) { // log(c + ": " + val + " as " + len + " max " + tLen + " @ " + val * tLen);
      var r = t.substr(val * tLen,tLen), l; return (len === 1) 
        ? (((l = r.indexOf(" ")) < 0) ? r : r.substring(0,l) ): r.substr(0,len); };
    p._fn = function(val,c,len) { // log(c + ": " + val + " in " + len);
      var r = "" + val; return ((c === "y")
        ? (len === 1) ?  r.substr(0 - p.defaults.year.length) : r
        : (r.length < len) ? ("000" + r).substr(0 - len) : r ); };
    // function log(v) { console.log(v); }
    // exports = F; // for module use
    
    return F; // for in-line use
    })(); // for in-line use
    
    // DateFormatter = require("DateFormatter"); // for module use
    

    Attachment: DateFormatter.js and DateFormatter.min.js as modules to be placed in project sandbox modules folder.

    To be continued in next post...


    2 Attachments

  • Test code:

    function log(s) { if (typeof window !== "undefined") { var n = document.getElementById("console"); 
        n.innerHTML = n.innerHTML + "\n" + s; 
      } else { console.log(s); } }
    
    function l(pattern, date, assertValue) { // for demo convenience
      var formatter = new DateFormatter(pattern);
      var formatted = formatter.format(date);
      var ok = (assertValue) ? ((formatted === assertValue)
                   ? ": ----} ok" : ": ----> *** NOK ***") : "";
      log(formatted + "  {---- " + ((pattern) ? '"' + pattern + '"' : "default") + ok);
    }
    
    function onInit(ts) { if (ts) setTime(ts);
    log("- yMNdHhmsSxXDZ are date and time pattern chars, escapable by ^ (default)");
    log("- d is a date a day ago same time; m is 75 days, 12 hours and 30 mins ago.");
    var ts = new Date().getTime();
    var d = new Date(ts - 24*60*60*1000); // yesterday same time
    var m = new Date(ts - 75*24*60*60*1000 - 12*60*60*1000 - 30*60*1000); // 75 d 12 h 30 m ago
    log("----- normal invocation format ------");
    l(); // now in default format
    l("M/d/y"); // now (today) in / format
    l("MM/dd/yyyy"); // now (today) in fixed format for numbers
    l("MM/dd/yy"); // now (today) fixed format for numbers
    l("^Mont^h b^y na^me: NNN(3) N(all)"); // now (this month)
    l("D, N d, yyyy"); // now (today) in week day month by name and day, full year
    l("D, N d, yyyy h:mm X"); // now in week day month by name and day, full year, AM/PM time
    l("D, N d, yyyy h:mm:ss X",d); // yesterday same time in...
    l("D, N d, yyyy h:mm:ss X",m); // about 75 days ago in...
    l("ti^me: hh:mmx"); // now (hr) in am/pm
    l("d.M.y"); // now (tdday) in . format
    l("dd.MM.yyyy"); // fixed format for numbers
    l("dd.MM.yy"); // fixed format for numbers
    l("h X Z E^spruino ti^me zone ;-)"); // now in h format and time zone
    log("----- short invocation format ------");
    log(DateFormatter.format() + ' {-- DateFormatter.format()'); // today in default format
    log(DateFormatter.format(null,"yyyyMMddH­Hmmss.SSS") + ' {-- ...(null,"yyy...")'); // timestamp in sortable format
    log(DateFormatter.format(m) + ' {-- DateFormatter.format(m)'); // about 75 days ago
    log("----- very short invocation format ------");
    log(DateFormatter(null,null) + ' {-- DateFormatter(null,null)'); // today default
    log(DateFormatter(m,null) + ' {-- DateFormatter(m,null)'); // about 75 days ago
    log(DateFormatter(d,"D, N d, y h:mx") + ' {-- DateFormatter(d,"D, N d, y h:mx")');
    log("----- update defaults (default: d.M.y, esc: ~, ... some or all) ------");
    DateFormatter.updateDefaults({ pattern: "d.M.y", esc: "~", year: "yy", zone: "MEZ-1" 
        , dayLen:2, days: "SoMoDiMiDoFrSa"
        , monLen:9, mons: "Januar   Februar  Maerz    April    Mai      Juni     "
                        + "Juli     August   SeptemberOktober  November Dezember " });
    log("----- normal invocation format ------");
    l("~Monat bei~m ~Na~men: NNN N");
    l("D, d. N yyyy"); // jetzt in Wochentag Tag. Monat Jahr
    l("D, d. N yyyy H:mm"); // jetzt in Wochentag Tag. Monat Jahr und Zeit
    l("D, d. N yyyy H:mm:ss",d); // gestern um die gleiche Zeit in Wochentag...
    l("D, d. N yyyy H:mm:ss",m); // ungefaehr vor 75 Tagen in...
    l("~Zeit: HH:mm");
    l("H~h Z"); // jetzt in H format mit Zeitzone
    }
    onInit(1462603718.328); // Sat, May 7, 2016 6:48:38 AM
    // parameter derived from browser debugger: new Date().getTime() / 1000
    // rerun without parameter - in console type: onInit();
    

    Test output:

     1v85 Copyright 2016 G.Williams
    >echo(0);
    - yMNdHhmsSxXDZ are date and time pattern chars, escapable by ^ (default)
    - d is a date a day ago same time, m is 75 days, 12 hours and 30 mins ago.
    ----- normal invocation format ------
    5/7/2016  {---- default
    5/7/2016  {---- "M/d/y"
    05/07/2016  {---- "MM/dd/yyyy"
    05/07/2016  {---- "MM/dd/yy"
    Month by name: May(3) May(all)  {---- "^Mont^h b^y na^me: NNN(3) N(all)"
    Sat, May 7, 2016  {---- "D, N d, yyyy"
    Sat, May 7, 2016 6:48 AM  {---- "D, N d, yyyy h:mm X"
    Fri, May 6, 2016 6:48:38 AM  {---- "D, N d, yyyy h:mm:ss X"
    Sun, Feb 21, 2016 6:18:38 PM  {---- "D, N d, yyyy h:mm:ss X"
    time: 06:48am  {---- "ti^me: hh:mmx"
    7.5.2016  {---- "d.M.y"
    07.05.2016  {---- "dd.MM.yyyy"
    07.05.2016  {---- "dd.MM.yy"
    6 AM ESP Espruino time zone ;-)  {---- "h X Z E^spruino ti^me zone ;-)"
    ----- short invocation format ------
    5/7/2016 {-- DateFormatter.format()
    20160507064838.250 {-- ...(null,"yyy...")
    2/21/2016 {-- DateFormatter.format(m)
    ----- very short invocation format ------
    5/7/2016 {-- DateFormatter(null,null)
    2/21/2016 {-- DateFormatter(m,null)
    Fri, May 6, 2016 6:48am {-- DateFormatter(d,"D, N d, y h:mx")
    ----- update defaults (default: d.M.y, esc: ~, ... some or all) ------
    ----- normal invocation format ------
    Monat beim Namen: Mai Mai  {---- "~Monat bei~m ~Na~men: NNN N"
    Sa, 7. Mai 2016  {---- "D, d. N yyyy"
    Sa, 7. Mai 2016 6:48  {---- "D, d. N yyyy H:mm"
    Fr, 6. Mai 2016 6:48:38  {---- "D, d. N yyyy H:mm:ss"
    So, 21. Februar 2016 18:18:38  {---- "D, d. N yyyy H:mm:ss"
    Zeit: 06:48  {---- "~Zeit: HH:mm"
    6h MEZ-1  {---- "H~h Z"
    =undefined
    >
    

    Attached html can be clicked and it runs right away same code in the browser.

    Placed in a module named DateFormatter and pulled in with var DF = require("DateFormatter"); it is a bit less verbose...

    To be continued in next post...


    1 Attachment

  • Formatting pattern characters:

    // yMNdHhmsSxXDZ are date and time pattern chars, escape-able with ^ (default).

    • | y: year... "y" as defined in prototype defaults year (= "yy" or "yyyy"), "yy" or "yyyy" for short and full year format.
    • | M: Month... "M" single or two digits, "MM" two digits with leading 0 for months 1..9. "MMM".."MMM..M" is the same as "NNN" and "NNN..N". See N.
    • | N: Name of month... "N" as defined in prototype defaults mons string ("JanFebMar..." or "January__February_March____...") with fix length of monLen (3 or 9, respectively), the length of longest month name. String is extracted with mons.substr(month * monLen, monLen). "N..N" will take a substring of length equal to the number of "N"s. (Note that the underscores stand for blanks.)
    • d: day... "d" single or two digits, "dd" two digits with leading 0 for days 1..9.
    • H: Hours in 24H format... "H" single or two digits, "HH" two digits with leading 0 for hours 0..9.
    • h: hours in 12h format... "h" single or two digits, "hh" two digits with leading 0 for hours 0..9. To display related am or pm and AM or PM see x and X.
    • - ```s```: **seconds**... "s" single or two digits, "ss" two digits with leading 0 for secconds 0..9.
      - ```S```: **milliSeconds**... "S" single, two, or three digits, "SSS" three digits with two and one leading **0**(s) for seconds **1..9** and **10..99**, respectively.
      - ```x```: **am** and **pm** (lowercase)... "x" always puts **am** or **pm** in the output. See also ```X``` (uppercase) and ```h``` (lowercase).
      - ```X```: **AM** and **PM** (uppercase)... "X" always puts **AM** or **PM** in the output. See also ```x``` (lowercase) and ```h``` (lowercase).
      - ```D```: **Day name**... "D" as defined in prototype defaults ```days``` string (```"MonTueWed..."``` or ```"Monday___Tuesday__Wednesday..."``` with fix length of ```dayLen``` (```3``` or ```9```, respectively), the length of longest week day name. String is extracted with ***days.substr(day * dayLen, dayLen)***. "D..D" will take a substring of the length equal to the number of "D"s. (Note that the underscores stand for blanks.)
      - ```Z```: **Time zone**... "Z" as defined in prototype defaults ```zone``` string. "Z..Z" will take a substring of the length equal to the number of "Z"s.
      - ```any other character```... **as is**, for example: ```"in yyyy"``` for "in 1999" 
      - ```^```: **escape character**... "^" as defined in prototype defaults ```esc``` string (```"^"```). The escape character enables the use of date and time pattern characters **as is**; for example: ```"T^he ti^me i^s now: h:mm X"``` for "The time is now: 3:45 PM". 
      
      Formatting pattern examples:
      
      1. ```M/d/y``` ---> **2/29/1956 or 2/29/56**, depending on prototype defaults ```year: "yyyyy"``` or ```"yy"```
      2. ```yyyy/MM/dd``` ---> **1956/02/29**, date as sortable string
      3. ```D, N d, y``` ---> **Wednesday, February 29, 1956** (with prototype defaults ```days: "Sunday___Monday___Tuesday__Wednesday...­", dayLen: 9``` and equally for ```mons: "January__Feburary_...", monLen: 9```).  
      4. ```DDD, NNN d, y``` ---> **Wed, Feb 29, 1956**  (with prototype defaults ```days: "Sunday___Monday___Tuesday__Wednesday...­", dayLen: 9```; or ```"SunMonTueWed..."``` and ```3```;  and analog for ```mons``` and ```monLen```).
      5. ```DDD, NNN d, y``` ---> **Wed, Feb 29, 1956**  (with prototype defaults ```days: "SunMonTueWed...", dayLen: 3``` and ```mons: "JanFebMar...", monLen: 3```).
      6. ```D, N d, y``` ---> **Wed, Feb 29, 1956** (with prototype defaults ```days: "SunMonTueWed...", dayLen: 3``` and ```mons: "JanFebMar...", monLen: 3```).
      7. ```DD, NNNNNN d, yy``` ---> **We, Feb 29, 56** (with prototype defaults ```days: "SunMonTueWed...", dayLen: 3``` and ```mons: "JanFebMar...", monLen: 3```).
      8. ```Begin: yyyy-mm-dd_HH:mm:ss.SSS``` ---> **Begin: 1956-02-29_00:00:00.000**
      9. ```En^d: yyyy-mm-dd_HH:mm:ss.SSS``` ---> **End: 1956-02-29_23:59:59.999**
      
      ***Usage Notes:***
      
      1. Consecutive same date and time pattern characters define the length (as reasonable)
      2. Characters in the template other than date and time pattern characters are just pushed to the output
      3. To use pattern characters as plain template characters, they have to be ***escaped*** with **"^**" as defined in prototype defaults ```esc```.
      4. Prototype defaults can be updated with a defaults object that includes the property names and values to update. A mixin / merge into the existing properties allows the selective / partial update.
      5. Prototype defaults are shared and a change affects all formatters... with one exception see next item).
      6. Common to all usages is pulling the module or placing the code in-line and assigning result to a variable, for example ```var DF = ....;"``` (DateFormatter class, hence uppercase). ```var DF = require("DateFormatter");```` when code placed as module in local sandbox modules folder.
      7. The usual or regular form of usage is creating a formatter with optionally passing the template / pattern: ```var df = new DF();```.  If no template or pattern is passed, the prototype default is taken and stored in the formatter instance.  Therefore, a once created formatter holds the template / pattern that was actual at the time of formatter creation. To use the formatter, its methods ```df.format()```, ```df.getDefaults()``` and ```df.updateDefaults()``` are invoked. ```.format()``` accepts ***none***, ***one*** and ***two parameter*** parameters: *first is date*, and *second is pattern*. For *missing* or falsy (**undefined*, *null*, *0*, "", false) parameters the respective default is taken: *now* for *date*, and *prototpe.defaults.pattern* for *pattern*. To show, for example, now in ***non-default*** pattern, invocation is with ***null and non-default pattern***: ```.format(null,"yyyy-MM-dd")``` shows something like 2016-05-07.
      8. A shorter form of usage is invocation of formatter class (function name) methods ```DF.format()```, ```DF.getDefaults()``` and ```DF.updateDefaults()```. Parameter handling is the same as for regular usage. 
      9. The shortest - bit messy - version is the use of the constructor function (class definition function) ```DF(...)```. This version requires always two parameters - both ***date*** and ***pattern*** - to be supplied, of which one or both can be specified as "**false**" (undefined, null, 0, "", false) to force use of respective default. For example - shortest form: ```DF(0,0)``` - returns now in the format as defined in the prototype defaults property "**pattern**".
      
      Common to below usage examples is this snippet:
      
      

    // assuming now/today is: Wed, May 17, 2016, 1:45:59 PM, 704 milliseconds
    // assuming module's defaults = {pattern:"M/d/y",year:"yyyy",zone:"ESP"}­
    function log(v) { console.log(v); }
    var myDate = new Date(1955,1,29,13,45,59,123); // 1955-02-29_13:45:59.123
    var DF = require("DateFormatter"); // hold on to module as class and function

    
    ***Examples related to Usage Note 7. (with snippet a runnable sequence):***
    
    

    var DF = require("DateFormatter"); // hold on to module as class (and function)
    var df = new DF(); // create instance using class' default formatting pattern
    log(df.pattern); // M/d/y - logs instance's default formatting pattern
    log(df.format()); // 5/17/2016 - logs now in instance's default pattern
    log(df.format(myDate)); // 2/29/1955 - logs myDate in instance's default pattern
    log(df.format(null,"d.M.y")); // 17.5.2016 - logs now in specified format
    log(df.format(myDate,"d.M.y")); // 29.2.1955 - logs myDate in specified pattern
    df.pattern = "d.M.y"; // change instance's formatting pattern
    log(df.pattern); // d.M.y - logs instance's default formatting pattern
    df.updateDefaults({pattern:"yyyy-MM-dd",­ year:"yy", Z:"PSD"}); // update shared defaults, but not the default formatting pattern of existing instances.
    var df2 = new DF(); // create 2nd df picks up new default formatting pattern
    log(df.pattern); // d.M.y - instance's default pattern - still the 'old' one
    log(df.format()); // 17.5.16 - now in instance's still 'old' default, but...
    log(df.format(myDate)); // 29.2.55 - ...in inst.'s default but y from new defaults
    log(df.format(null,"d.M.yyyy")); // 17.5.2016 - logs now in specified format
    log(df.format(myDate,"d.M.y")); // 29.2.55 - myDate as spec'd, y as new defaults
    log(df.format(null,"d.M.yyyy")); // 17.5.2016 - logs now in specified format
    log(df.format(myDate,"dd.MM.yyyy")); // 29.02.1955 - myDate in spec'd pattern
    log(df2.format()); // 2016-05-17 - logs now in instance's default pattern
    log(df2.format(y-m-d)); // 16-5-17 - now in inst.'s default but y from defaults
    log(df2.format(null,"M/d/yyyy")); // 5/17/2016 - logs now in specified format

    
    ***Examples related to Usage Note 8. (with snippet a runnable sequence):***
    
    

    var DF = require("DateFormatter"); // hold on to module as class (and function)
    log(DF.getDefaults().pattern); // M/d/y - logs default formatting pattern
    log(DF.format()); // 5/17/2016 - logs now in shared default pattern
    log(DF.format(myDate)); // 2/29/1955 - logs myDate in shared default pattern
    log(DF.format(null,"d.M.y")); // 17.5.2016 - logs now in specified format
    log(DF.format(myDate,"d.M.y")); // 29.2.1955 - logs myDate in specified pattern
    DF.getDefaults().pattern = "d.M.y"; // change shared default pattern, BUT IT IS NOT the recommended way
    log(DF.getDefaults().pattern); // d.M.y - logs shared default formatting pattern
    DF.updateDefaults({pattern:"yyyy-MM-dd",­ year:"yy", Z:"PSD"}); // update shared defaults the RECOMMENDED way
    log(DF.getDefaults().pattern); // yyyy-MM-dd - shared default pattern
    log(DF.format()); // 2016-05-17 - logs now in shared default pattern
    log(DF.format(myDate)); // 1955-02-29 - myDate in shared default pattern
    log(DF.format(null,"d.M.yyyy")); // 17.5.2016 - logs now in specified pattern
    log(DF.format(myDate,"d.M.y")); // 29.2.55 - myDate in spec'd, y from new defaults
    log(DF.format(null,"d.M.yyyy")); // 17.5.2016 - logs now in specified pattern
    log(DF.format(myDate,"dd.MM.yyyy")); // 29.02.1955 - logs myDate in spec'd pattern

    
    ***Examples related to Usage Note 9. (with snippet a runnable sequence):***
    
    

    var fD = require("DateFormatter"); // hold on to module as formatDate() function
    log(fD.getDefaults().pattern); // M/d/y - logs default formatting pattern
    log(fD(0,0)); // 5/17/2016 - logs now in default pattern
    log(fD(myDate,null)); // 2/29/1955 - logs myDate in shared default pattern
    log(fD(null,"d.M.y")); // 17.5.2016 - logs now in specified format
    log(fD(myDate,"d.M.y")); // 29.2.1955 - logs myDate in specified pattern
    fD.getDefaults().pattern = "d.M.y"; // change shared default pattern, BUT IT IS NOT the recommended way
    log(fD.getDefaults().pattern); // d.M.y - logs shared default formatting pattern
    fD.updateDefaults({pattern:"yyyy-MM-dd",­ year:"yy", Z:"PSD"}); // update shared defaults the RECOMMENDED way
    log(fD.getDefaults().pattern); // yyyy-MM-dd - shared default pattern
    log(fD(false,undefined)); // 2016-05-17 - logs now in shared default pattern
    log(fD(myDate,0)); // 1955-02-29 - myDate in shared default pattern
    log(fD(null,"d.M.yyyy")); // 17.5.2016 - logs now in specified pattern
    log(fD(myDate,"d.M.y")); // 29.2.55 - myDate in spec'd, y from new defaults
    log(fD(null,"d.M.yyyy")); // 17.5.2016 - logs now in specified pattern
    log(fD(myDate,"dd.MM.yyyy")); // 29.02.1955 - logs myDate in spec'd pattern
    ```

    At a later time, I may put this up as a module. Until then, it still needs some hardening.

    Closing comments in next post...

  • Some closing comments:

    Parameters are unfortunately positional... the first for .format() method - instance or class - is always the date (or falsy for the default, which is now), and the second is the formatting pattern (or falsy for the default, which is the default in the module or the formatting pattern passed on construction). falsy values - values that evaluate to false (see MDN Glossary) - will 'provoke' the default. Therefore, for the last example: xxy.format(0,'MM/dd/yy'); is the shortest form with an instance.

    Adding... some more examples based on the description of items 7. through 9. of Notes of previous post.

    Since I know that not all users are inclined to use objects (constructed from classes), I provided also the class (or static) method usage, and a plain function usage.

    For the class (or static) method usage, you assign the module to a variable: var DF = require("DateFormatter"); and do formatting with DF.format();, just the same way as with objects. With the class (or static) method usage you can have only one default format: the class default, which of course, you can change with DF.updateDefaults({pattern:"yyyy-MM-dd"}­); (the formatting pattern is just an example).

    For the functional usage, you assign the module to a variable: var fd = require("DateFormatter"); and do formatting with fd(parm1,parm2);. Note that it is formatdate() versus DF for DateFormatter class and df for instance of DateFormatter class. With the functional usage, you always have to provide two (2) parameters, or in other words, always two (2) parameters have to be present. The javascript functions knows based on the number of parms whether to behave as plain function or as constructor. parm1 one is either a date or falsy, and parm2 is a formatting pattern or falsy.

    Btw, if you always provide the format, you need only one instance of formatter. If you always use the same format or have a prevailing format, create a formatter with that format: var df = new (require("DateFormatter"))("MM/dd/yy");, and use it then anywhere in the code.

  • @allObjects: wow - great stuff - looks like you coded your own date.js and more ;-)

    my intention is to code a small and simple helper to get a human readable date/time

    a little change for a shorter the length checker:

    //var HH = this.getHours().toString();
    //HH = HH.length > 1 ? HH : '0' + HH;
    var HH = ("0"+this.getHours().toString()).slice(-­2);
    
  • @MaBe, the whole came out of a personal conversation that had less in mind to get a formatter going but more so to show the separation of concerns: model / data separate from presentation. Therefore, I would not not extend the Date 'class'... last but not least to prevent future conflicts, when one day Date will extend...

    The DateFormatter then just became the icing on the cake - a Kürlauf! ...and freebee... ;)

  • @allObjects - thanks for sharing !

  • The 'complete' (or almost perfect separation) would go one step further: extract the internationalization (i18n) out of the formatter itself and have it as a separate modules, one for each language... loaded automatically correctly by the locale information by the system or overridable (with a parm in require module and/or constructor. Since the argument in require(argument) as a literal ("xxx...") works on build time (upload time), for runtime - when it is a variable - Espruino firmware tries to read from the memory card (as present in original Espruino board).

  • @Gordon are you thinking of an implementing of DateFormatter in Espruino as native code ?

  • As far as I can tell, toDateString shouldn't take any format arguments? https://developer.mozilla.org/en-US/docs­/Web/JavaScript/Reference/Global_Objects­/Date/toDateString

    If there is a date formatting that's part of the JS standard then I'd be interested in implementing it - but memory is too scarce to add non-standard functionality, especially if it can be done easily with JS.

  • Ok so the solution is clear to me - do it in JS and use the code allObjects share.

  • @allObjects btw - now reading Cross-cutting_concern to understand what you pointed out:

    to show the separation of concerns: model / data separate from presentation.

  • Subject domains are like silos and in silos separation of concerns is applied.

    For example, the date subject has subject specific data (and calculation/behavior) and subject specific formatting. Other subjects are: Numbers, bank accounts, address, etc. Subjects are also composition of other subjects.

    So far, we just talked about subject specific separation of concerns.

    Cross-cutting concerns are concerns that are across all subjects / across the silos / horizontal - not subject specific - and this is for example logging, security/access control/... etc. Most of the time, these cross-cutting things are services/servers and clients to domain specific clients.

    In other words, 'the world' is diced and sliced in two dimensions, and some of the pieces are logically and physically 'shared': The logging has a domain specific component and a cross domains shared generic component: invocation of the logging - what to log (and often also some of the configuration info of the logging)- is a domain specific and the actual writing/appending to the log is generic and common to all domains.

    There are programming paradigms to generalize and separate also the specific components to keep the domain code as concise as possible. A good example for that is aspect oriented (ao) programming.

    Javascript is especially nice to this idea, because it can be dynamically / at runtime changed... (in java, it is a bit more difficult, but still doable... therefore annotations are used to 'inject' the code at build / compile time). In JavaScript, ao can be applied even when only programming with functions.

    For an example, let's assume we have a add(summand1, summand2){ ... }function:

    function add(summand1, summand2) {
      return summand1 + summand2;
    }
    

    Now, we want to have (configurable, conditional) logging for before and after - in other words: log the parameters (input) and log the returned result (output).

    Easy, p(l)easy! you say... but it may not really be pleasant...

    function add(summand1, summand2) {
      console.log("add(summand1:" + summand1 + ", " + summand2 +"){...");
      var result = summand1 + summand2;
      console.log("add(summand1:" + summand1 + ", " + summand2 +"){...}:" + result);
      return result;
    }
    

    Ooooookaaaaay... you got your (1st round of) logging (the configurable/conditional we put off for later... and ...never), but you also change the code not just by adding but also in structure: you had to temp store the result for the logging the way the it was.

    function add(summand1, summand2) {
      return summand1 + summand2;
    }
    
    // function to add before and after logging to add function while keeping add's implementation
    function addLogging() {
      var add_f = add;
      add = function(summand1, summand2) {
        console.log("add(summand1:" + summand1 + ", " + summand2 +"){...");
        var result = add_f(summand1, summand2);
        console.log("add(summand1:" + summand1 + ", " + summand2 +"){...}: " + result);
        return result;
      };
      add._logging = add_f;
    }
    
    // remove logging
    function removeLogging() {
      add = add._logging;
    }
    
    // using the last result and add() function in a watch on BTN1
    // even last (result) turns green LED on and red LED off
    // odd last (result) turns red LED on and green LED off
    var last = 0;
    setWatch(function(){
        last = add(last,1);
        digitalWrite(LED1, last % 2);
        digitalWrite(LED2, (last + 1) % 2);
      }, BTN1, {repeat: true, edge: "falling", debounce:20});
    

    Go through these steps after uploading of above code snippet:

    • Enter add(1,2) in console: Console shows =3.

    • Enter addLogging(); to add logging to the add() function.

    • Enter add(1,2) in console again: console shows the logging and =3:

      >add(1,2)
      add(summand1:1, 2){...
      add(summand1:1, 2){...}: 3
      =3
      >
      
    • Entering removeLogging(); will remove logging from the add() function.

    • Enter add(1,2) in console again: console shows just =3 again.

    Now, we want to use the button BTN1.

    Pressing button BTN1 will alternately lite red and green (Pico and Original) onboard LED. But how do we know if odd result shows red LED on, and even green LED?

    We add logging and compare log output in console with lit LED.

    Conclusion: This brief exercise shows conceptually how logging can be added without changing the implementation.

    NOTE: Real world is much more sophisticated and does not have issues like this example (after upload enter in console bad = add, then addLogging(), then add(1,2), and finally bad(1,2). What do you observe and what is the explanation?)

  • Possible applications use for Bangle.js clocks with date.

    Like to continue this discussion , because there will be requests for something like

    Looking at the MDN docs there's a standard for Intl (https://developer.mozilla.org/en-US/docs­/Web/JavaScript/Reference/Global_Objects­/DateTimeFormat) that I reckon could be implemented as a JS library reasonably easily?

    named in https://github.com/espruino/Espruino/iss­ues/1703

    What about extend "DateFormatter" to pass names for your local days and month so it’s not blowing up the module.

    eg days and mons as long and/or short format

    days: "SundayMondayTuesdayWednesday...", dayLen: 9 and equally for mons: "JanuaryFeburary_...", monLen: 9)

    To make a clock app very flexible, same code for all locales.
    it could be injected via app customized page eg Route Viewer

    What do you think about this idea?

  • As much as I (still) enjoy reading thru the solution, I feel it is "too fat" for casually formatting a date (and time) on BangleJS... BUT: If an application has the subject of date(s) and time(s), then it is most likely already the smallest 'module' for it's comprehensiveness, flexibility and efficiency (w/ @MaBe 's shorter length checker/stretcher).

    Here is another - cheap - Date Formatter:

    // DF: BangleJS Date Formatter
    let DF = // cheap Date Formatter
    { l: null    // last used; for reuse pass -1 for d as date
    , f: function(d,p) { // format(optDateOrMillis,optPattern) - 02/29/2020
        var d=this.i(d),s=this.s(p=p||this.p),b;
        return (p.split(s).map(v=>( ((b=((b=v.length)<2)?0:b)?"000":"")
          + this.x[v.charAt(0)](d) ).substr(-b)).join(s) ); }
    , d: function(d,ds) { return this.t(d,"n",ds||this.ds); } // d name
    , m: function(d,ms) { return this.t(d,"m",ms||this.ms); } // m name
    , c: function(d) { return [DF.d(d),DF.m(-1),DF.f(-1,"d y")].join(" "); }
    , t: function(d,x,ts){ return ts.split(",")[this.x[x](this.i(d))]; }
    , ds: "Sun,Mon,Tue,Wed,Thu,Fri,Sat" // Monday,Tuesday ..."
    , ms: ",Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oc­t,Nov,Dec" // January,...
    , p: "mm/dd/yy" // pattern
    , x: {m:d=>d.getMonth()+1,d:d=>d.getDate(),y:­d=>d.getFullYear()
         ,n:d=>d.getDay()}
    , s: function(p) { var s,i=-1,l=p.length; while(++i<l) { // 1st sep
        if ("mdy".indexOf(s=p.charAt(i))<0) return s; } return"/"; }
    , i: function(d) { // ret Date as is, today, from millis or last used
        return this.l = (isNaN(d)||d=="") ? d||new Date()
          : (d<0) ? this.l : new Date(d); }
    };
    

    Usage:

    02/15/20 - DF.f() today
    2/15 - DF.f(-1,"m/d") from used
    15.2. - DF.f("","m.d")+"." today
    2020-02-15 - DF.f(null,"yyyy-mm-dd") today
    Sat Feb 15, 2020 - DF.d(),DF.m(-1),DF.f(-1,"d")+",",DF.f(-1­,"y")
    Sat Feb 15 2020 - DF.d(),DF.m(-1),DF.f(-1,"d y")
    Sat Feb 15 2020 - DF.c() - today, combined, like Date.toString()
    Sat Feb 15 2020 - DF.l.toString().substr(0,15) - today from used
    Sat Feb 15 2020 08:49:34 GMT-0800 (Pacific Standard Time) - DF.i(-1) - today from last used
    Sat Feb 15 2020 08:49:34 GMT-0800 (Pacific Standard Time) - DF.l - today from used
    

    Attached .html file can be directly executed to run formatter and example in browser. It can also be downloaded, modified, and played with.

    The formatter can be modularized and then composed with a base module and extended with - for example - the week day and month names and combined formatting functions - and last but not lest - with the time formatting functions.

    The base module with just the number formatter is very compact and looks then like this:

    let DF = // cheap Date Formatter
    { l: null    // last used; for reuse pass -1 for d as date
    , f: function(d,p) { // format(optDateOrMillis,optPattern) - 02/29/2020
        var d=this.i(d),s=this.s(p=p||this.p),b;
        return (p.split(s).map(v=>( ((b=((b=v.length)<2)?0:b)?"000":"")
          + this.x[v.charAt(0)](d) ).substr(-b)).join(s) ); }
    , p: "mm/dd/yy" // pattern
    , x: {m:d=>d.getMonth()+1,d:d=>d.getDate(),y:­d=>d.getFullYear() }
    , s: function(p) { var s,i=-1,l=p.length; while(++i<l) { // 1st sep
        if ("mdy".indexOf(s=p.charAt(i))<0) return s; } return"/"; }
    , i: function(d) { // ret Date as is, today, from millis or last used
        return this.l=(isNaN(d)||d=="")?d||new Date():(d<0)?this.l:new Date(d);}
    };
    

    1 Attachment

  • Great, thanks for sharing.

    I will use this to build a class with using ds and ms from @locale.

    store the county specific locale for day and month, eg de-DE

        var loc = { 
            "ds" : "So,Mo,Di,Mi,Do,Fr,Sa",
            "ms":"Jan,Feb,Maerz,Apr,Mai,Jun,Jul,Aug,­Sept,Okt,Nov,Dez" 
        };
        require("Storage").write("@locale",JSON.­stringify(loc));
    

    and than use it to init the DF class

    class DF {
             constructor(ds, ms) {
                 this.ds = ds;
                 this.ms = ms;
             }
             f() { ......}
             ....
             i()  {.....}
    }
    
    loc = require("Storage").read("@locale");
    df = new DF( loc.ds, loc.ms);
    
    

    or something similar ;-)

  • I intentionally stayed away from instance / constructor idea (to save space). I would rather look for a i18n method that puts not just ms and ds but also p in place (month and day names and pattern) and return DF

    , i18n: function(l) { // optional locale
        var p,j=require("Storage").read(l||"@locale"­); for(p in j)this[p]=j[p]; return this; }
    

    and

    let DF = require("DF").i18n();
    

    I'm though not sure how this becomes part of Bangle.js code... I can see that Bangle.js is somehow a bit different in handling uploaded code... applications... and I could see like modules being shared between multiple apps... so setup looks different.

    PS: @MaBe, did you intentionally use Maerz vs März? Do we have a 7/8 bit issue w/ Espruino? ...I guess we have, since font(s) (intended to be used) would need i18n as well (most fonts cover only ASCII 32..126 (decimal)). In other words: half-baked i18n. :/<

  • I agree, have you checked Intl.DateTimeFormat - post #15

    did you intentionally use Maerz vs März?

    Yes, just 7 bit, that’s the next issue to handle.

    Edit: We can use the font tested here ;-)

  • https://lh.2xlibre.net/locale/de_DE/

    LC_TIME entries can be used and find via de_DE or any other language

    Edit: We can use the glibc export as base to create a subset.

  • Using glibc export for an example how locale could look

    So this peace of code is not a app but a locale saver

    • lives in Bangle.js App config page
    • User select his locale from a pull down
    • only store selected local in @locale
    • app can use it via reading @locale

    What do you think?

    var locales = {
        "en_US" : {
            mon   : "January,February,March,April,May,June,J­uly,August,September,October,November,De­cember",
            abmon : "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct­,Nov,Dec",
            day   : "Sunday,Monday,Tuesday,Wednesday,Thursda­y,Friday,Saturday",
            abday : "Sun,Mon,Tue,Wed,Thu,Fri,Sat"
        },
    
        "de_DE" : {
            mon   : "Januar,Februar,März,April,Mai,Juni,Juli­,August,September,Oktober,November,Dezem­ber",
            abmon : "Jan,Feb,Mär,Apr,Mai,Jun,Jul,Aug,Sep,Okt­,Nov,Dez",
            day   : "Sonntag,Montag,Dienstag,Mittwoch,Donner­stag,Freitag,Samstag",
            abday : "So,Mo,Di,Mi,Do,Fr,Sa"
        },
        "fr_FR": {
            mon   : "janvier,février,mars,avril,mai,juin,jui­llet,août,septembre,octobre,novembre,déc­embre",
            abmon : "anv.,févr.,mars,avril,mai,juin,juil.,ao­ût,sept.,oct.,nov.,déc.",
            day   : "dimanche,lundi,mardi,mercredi,jeudi,ven­dredi,samedi",
            abday : "dim.,lun.,mar.,mer.,jeu.,ven.sam."
        },
        "it_IT": {
            mon   : "gennaio,febbraio,marzo,aprile,maggio,gi­ugno,luglio,agosto,settembre,ottobre,nov­embre,dicembre",
            abmon : "gen,feb,mar,apr,mag,giu,lug,ago,set,ott­,nov,dic",
            day   : "domenica,lunedì,martedì,mercoledì,giove­dì,venerdì, sabato",
            abday : "dom,lun,mar,mer,gio,ven,sab"
        }
    };
    
    
    // save my relevant locale setting for for day, abday, mon, abmon
    var locale = "en_US";
    require("Storage").write("@locale",JSON.­stringify(locales[locale]));
    
    // read my locale setting for day, abday, mon, abmon
    myLocale = require("Storage").readJSON("@locale");
    //
    console.log(myLocale.day.split(','));
    console.log(myLocale.abday.split(','));
    console.log(myLocale.mon.split(','));
    console.log(myLocale.abmon.split(','));
    
    // output
    
    >[
      "Sunday",
      "Monday",
      "Tuesday",
      "Wednesday",
      "Thursday",
      "Friday",
      "Saturday"
     ]
    [
      "Sun",
      "Mon",
      "Tue",
      "Wed",
      "Thu",
      "Fri",
      "Sat"
     ]
    [
      "January",
      "February",
      "March",
      "April",
      "May",
      "June",
      "July",
      "August",
      "September",
      "October",
      "November",
      "December"
     ]
    [
      "Jan",
      "Feb",
      "Mar",
      "Apr",
      "May",
      "Jun",
      "Jul",
      "Aug",
      "Sep",
      "Oct",
      "Nov",
      "Dec"
     ]
    > 
    
    
  • Tue 2020.02.18

    'What do you think?'

    While I have only been taking sneak peaks at this Bangle.js development, I must say @MaBe that the concept and presentation are succinct, tidy and easy to understand. Nicely Done. Kudos!

    As I understand this then, for uncommon Locales, would a developer then use the Local Helper Library (link in #20 post) to create their own var locales assignment, thus reducing the code bloat and avoidance of unnecessary memory usage then?   or,  is the intent to make this part of the core, and somehow override the set of Locales?

    EDIT:   Wed 2020.03.04
    Answered in post #34

    Following the usage of that now defined Locale, that it's form would then be enhanced by the use of @allObjects Date Formatter code in post #2 or post #16?

  • This looks really promising. Definitely something that could be pulled into Bangle.js.

    I guess one question is we have similar issues with time (12/24 hr), distance, speed, and maybe even numbers (,/. for separators). Perhaps we should have a 'Locale' library that handles all of these in one go. It might make sense (at least initially) to have it as a JS module/app to allow quick iteration.

    My suggestion would be:

    • Add something like Bangle.getLocaleString(value, inputUnits) - eg Bangle.getLocaleString(new Date(),'time') which executed the JS in a file called locale if it existed, or just output a 'standard' format.
    • Add a new 'Locale' app which had a settings app which contained all @MaBe's strings, as well as the magic locale JS file that did all the magic

    A second option is to still have the app and locale file, but to do something like this for each app that uses it:

    var locale = {}
    try {locale = require("locale");} catch(e){}
    if (!locale.getTime) locale.getTime = d=>d.toISOString().match(/T(.....)/)[1];­
    // ...
    console.log(locale.getTime(new Date()));
    

    That'd then be compatible with all devices including the emulator (which doesn't support apps yet)

  • glad you like it :)

    Yes it would be nice to have some more locale entries like this, or even more.

    ...
        decimal_point : ",",
        thousands_sep :  ".",
        currency_symbol : "€",
        int_curr_symbol : "EUR",
        yesstr : "ja",
        nostr : "nein",
        am_pm : ""
    ....
    
  • @MaBe, right now I could use de_CH ... hahaha... don't know if this is a official thing, but you know what it means...

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

Date().toDateString()

Posted by Avatar for MaBe @MaBe

Actions