├── README.md ├── diff.go ├── moment.go ├── moment_parser.go ├── moment_parser_test.go ├── strftime_parser.go └── strftime_parser_test.go /README.md: -------------------------------------------------------------------------------- 1 | moment 2 | ====== 3 | 4 | Port of moment.js to Go (Golang) with some additional features for working with dates and times. Also some major inspiration from Carbon as well as some custom functions. 5 | 6 | Supports custom formats (i.e. YYYY-MM-DD instead of Go's 2006-01-02) - currently the moment format and strftime formats are supported. 7 | 8 | Note this is still a work in progress and not anywhere near production ready. Please feel free to fork and contribute missing methods, timezones, strtotime(), locale/languages functionality, or just provide more idiomatic Go if you see potential for improvement. I'm currently working on unit tests and eventually documentation. 9 | 10 | Links: 11 | * http://momentjs.com/ 12 | * https://github.com/briannesbitt/Carbon 13 | * http://golang.org/pkg/time/ 14 | * https://github.com/fightbulc/moment.php (Credits for replacement keys and regex in moment_parser.go) -------------------------------------------------------------------------------- /diff.go: -------------------------------------------------------------------------------- 1 | package moment 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | "time" 7 | ) 8 | 9 | // @todo In months/years requires the old and new to calculate correctly, right? 10 | // @todo decide how to handle rounding (i.e. always floor?) 11 | type Diff struct { 12 | duration time.Duration 13 | } 14 | 15 | func (d *Diff) InSeconds() int { 16 | return int(d.duration.Seconds()) 17 | } 18 | 19 | func (d *Diff) InMinutes() int { 20 | return int(d.duration.Minutes()) 21 | } 22 | 23 | func (d *Diff) InHours() int { 24 | return int(d.duration.Hours()) 25 | } 26 | 27 | func (d *Diff) InDays() int { 28 | return int(math.Floor(float64(d.InSeconds()) / 86400)) 29 | } 30 | 31 | // This depends on where the weeks fall? 32 | func (d *Diff) InWeeks() int { 33 | return int(math.Floor(float64(d.InDays() / 7))) 34 | } 35 | 36 | func (d *Diff) InMonths() int { 37 | return 0 38 | } 39 | 40 | func (d *Diff) InYears() int { 41 | return 0 42 | } 43 | 44 | // http://momentjs.com/docs/#/durations/humanize/ 45 | func (d *Diff) Humanize() string { 46 | diffInSeconds := d.InSeconds() 47 | 48 | if diffInSeconds <= 45 { 49 | return fmt.Sprintf("%d seconds ago", diffInSeconds) 50 | } else if diffInSeconds <= 90 { 51 | return "a minute ago" 52 | } 53 | 54 | diffInMinutes := d.InMinutes() 55 | 56 | if diffInMinutes <= 45 { 57 | return fmt.Sprintf("%d minutes ago", diffInMinutes) 58 | } else if diffInMinutes <= 90 { 59 | return "an hour ago" 60 | } 61 | 62 | diffInHours := d.InHours() 63 | 64 | if diffInHours <= 22 { 65 | return fmt.Sprintf("%d hours ago", diffInHours) 66 | } else if diffInHours <= 36 { 67 | return "a day ago" 68 | } 69 | 70 | return "diff is in days" 71 | } 72 | 73 | // In Months 74 | 75 | // In years 76 | -------------------------------------------------------------------------------- /moment.go: -------------------------------------------------------------------------------- 1 | package moment 2 | 3 | import ( 4 | "fmt" 5 | "regexp" 6 | "strconv" 7 | "strings" 8 | "time" 9 | ) 10 | 11 | // links 12 | // http://en.wikipedia.org/wiki/ISO_week_date 13 | // http://golang.org/src/pkg/time/format.go 14 | // http://www.php.net/manual/en/class.datetime.php#datetime.constants.rfc822 15 | // http://php.net/manual/en/function.date.php 16 | // http://www.php.net/manual/en/datetime.formats.relative.php 17 | 18 | // @todo are these constants needed if they are in the time package? 19 | // There are a lot of extras here, and RFC822 doesn't match up. Why? 20 | // Also, is timezone usage wrong? Double-check 21 | const ( 22 | ATOM = "2006-01-02T15:04:05Z07:00" 23 | COOKIE = "Monday, 02-Jan-06 15:04:05 MST" 24 | ISO8601 = "2006-01-02T15:04:05Z0700" 25 | RFC822 = "Mon, 02 Jan 06 15:04:05 Z0700" 26 | RFC850 = "Monday, 02-Jan-06 15:04:05 MST" 27 | RFC1036 = "Mon, 02 Jan 06 15:04:05 Z0700" 28 | RFC1123 = "Mon, 02 Jan 2006 15:04:05 Z0700" 29 | RFC2822 = "Mon, 02 Jan 2006 15:04:05 Z0700" 30 | RFC3339 = "2006-01-02T15:04:05Z07:00" 31 | RSS = "Mon, 02 Jan 2006 15:04:05 Z0700" 32 | W3C = "2006-01-02T15:04:05Z07:00" 33 | ) 34 | 35 | // http://golang.org/src/pkg/time/format.go?s=12686:12728#L404 36 | 37 | // Timezone implementation 38 | // https://groups.google.com/forum/#!topic/golang-nuts/XEVN4QwTvHw 39 | // http://en.wikipedia.org/wiki/Zone.tab 40 | 41 | // Support ISO8601 Duration Parsing? 42 | // http://en.wikipedia.org/wiki/ISO_8601 43 | 44 | // Differences 45 | // Months are NOT zero-index, MOmentJS they are 46 | // Weeks are 0 indexed 47 | // -- Sunday being the last day of the week ISO-8601 - is that diff from Moment? 48 | // From/FromNow Return a Diff object rather than strings 49 | 50 | // Support for locale and languages with English as default 51 | 52 | // Support for strftime 53 | // https://github.com/benjaminoakes/moment-strftime 54 | // Format: https://php.net/strftime 55 | 56 | type Moment struct { 57 | time time.Time 58 | 59 | Parser 60 | } 61 | 62 | type Parser interface { 63 | Convert(string) string 64 | } 65 | 66 | func New() *Moment { 67 | m := &Moment{time.Now(), new(MomentParser)} 68 | 69 | return m 70 | } 71 | 72 | func (m *Moment) GetTime() time.Time { 73 | return m.time 74 | } 75 | 76 | func (m *Moment) Now() *Moment { 77 | m.time = time.Now() 78 | 79 | return m 80 | } 81 | 82 | func (m *Moment) Moment(layout string, datetime string) *Moment { 83 | return m.MomentGo(m.Convert(layout), datetime) 84 | } 85 | 86 | func (m *Moment) MomentGo(layout string, datetime string) *Moment { 87 | time, _ := time.Parse(layout, datetime) 88 | 89 | m.time = time 90 | 91 | return m 92 | } 93 | 94 | // This method is nowhere near done - requires lots of work. 95 | func (m *Moment) Strtotime(str string) *Moment { 96 | str = strings.ToLower(strings.TrimSpace(str)) 97 | compiled := regexp.MustCompile(`\s{2,}`) 98 | str = compiled.ReplaceAllString(str, " ") 99 | 100 | // Assume everything is relative from now 101 | m.Now() 102 | 103 | // Replace written numbers (i.e. nine, ten) with actual numbers (9, 10) 104 | written := regexp.MustCompile("one|two|three|four|five|six|seven|eight|nine|ten") 105 | str = written.ReplaceAllStringFunc(str, func(n string) string { 106 | switch n { 107 | case "one": 108 | return "1" 109 | case "two": 110 | return "2" 111 | case "three": 112 | return "3" 113 | case "four": 114 | return "4" 115 | case "five": 116 | return "5" 117 | case "six": 118 | return "6" 119 | case "seven": 120 | return "7" 121 | case "eight": 122 | return "8" 123 | case "nine": 124 | return "9" 125 | case "ten": 126 | return "10" 127 | } 128 | 129 | return "" 130 | }) 131 | 132 | // Remove ordinal suffixes st, nd, rd, th 133 | ordinal := regexp.MustCompile("([0-9]+)st|nd|rd|th") 134 | str = ordinal.ReplaceAllString(str, "$1") 135 | 136 | // Replace n second|minute|hour... ago to -n second|minute|hour... to consolidate parsing 137 | ago := regexp.MustCompile("^([0-9]+) (second|minute|hour|day|week|month|year)s? ago$") 138 | str = ago.ReplaceAllString(str, "-$1 $2") 139 | 140 | // Look for relative +1day, +3 days 5 hours 15 minutes 141 | relative := regexp.MustCompile(`([\+\-])?([0-9]+),? ?(second|minute|hour|day|week|month|year)s?`) 142 | if match := relative.FindAllStringSubmatch(str, -1); match != nil { 143 | for i := range match { 144 | switch match[i][1] { 145 | case "-": 146 | number, _ := strconv.Atoi(match[i][2]) 147 | m.Subtract(match[i][3], number) 148 | default: 149 | number, _ := strconv.Atoi(match[i][2]) 150 | m.Add(match[i][3], number) 151 | } 152 | } 153 | 154 | return m 155 | } 156 | 157 | // Remove any words that aren't needed for consistency 158 | str = strings.Replace(str, " at ", " ", -1) 159 | str = strings.Replace(str, " on ", " ", -1) 160 | 161 | // Support for interchangeable previous/last 162 | str = strings.Replace(str, "previous", "last", -1) 163 | 164 | // Try to parse out time from the string 165 | // @todo seconds need a colon before 166 | time := `(?P\d\d?):(?P\d\d?)(?P\d\d?)?\s?(?Pam|pm)?\s?(?P[a-z]{3,3})?|(?Pnoon|midnight)` 167 | var timeDefaults = map[string]int{ 168 | "hour": 0, 169 | "minutes": 0, 170 | "seconds": 0, 171 | } 172 | 173 | relative = regexp.MustCompile(time) 174 | 175 | timeMatches := timeDefaults 176 | var zone string 177 | if match := relative.FindStringSubmatch(str); match != nil { 178 | for i, name := range relative.SubexpNames() { 179 | if i == 0 { 180 | str = strings.Replace(str, match[i], "", 1) 181 | continue 182 | } 183 | 184 | if match[i] == "" { 185 | continue 186 | } 187 | 188 | // Midnight is all zero's so nothing to do 189 | if name == "relativetime" && match[i] == "noon" { 190 | timeDefaults["hour"] = 12 191 | } 192 | 193 | if name == "zone" { 194 | zone = match[i] 195 | } 196 | 197 | if name == "meridiem" && match[i] == "pm" && timeMatches["hour"] < 12 { 198 | timeMatches["hour"] += 12 199 | } 200 | 201 | if name == "hour" || name == "minutes" || name == "seconds" { 202 | timeMatches[name], _ = strconv.Atoi(match[i]) 203 | } 204 | } 205 | 206 | // Processing time is always last 207 | defer m.strtotimeSetTime(timeMatches, zone) 208 | 209 | if str == "" { 210 | // Nothing left to parse 211 | return m 212 | } 213 | 214 | str = strings.TrimSpace(str) 215 | } 216 | 217 | // m.StartOf("month", "January").GoTo(time.Sunday) 218 | 219 | months := "jan|january|feb|february|mar|march|apr|april|may|jun|june|jul|july|aug|august|sep|september|oct|october|nov|november|dec|december" 220 | days := "mon|monday|tues|tuesday|wed|wednesday|thurs|thursday|fri|friday|sat|saturday|sun|sunday" 221 | 222 | relative = regexp.MustCompile(`of (?Pthis|next|last) (week|month|year)`) 223 | 224 | if match := relative.FindStringSubmatch(str); match != nil { 225 | period := match[1] 226 | unit := match[2] 227 | 228 | str = strings.Replace(str, match[0], "", 1) 229 | 230 | switch period { 231 | case "next": 232 | if unit == "year" { 233 | m.AddYears(1) 234 | } 235 | if unit == "month" { 236 | m.AddMonths(1) 237 | } 238 | if unit == "week" { 239 | m.AddWeeks(1) 240 | } 241 | case "last": 242 | if unit == "year" { 243 | m.SubYears(1) 244 | } 245 | if unit == "month" { 246 | m.SubMonths(1) 247 | } 248 | if unit == "week" { 249 | m.SubWeeks(1) 250 | } 251 | } 252 | 253 | str = strings.TrimSpace(str) 254 | 255 | // first := regexp.MustCompile("(?Pfirst|last)?") 256 | } 257 | 258 | /* 259 | 260 | relativeday: first day of 261 | relativeperiod: this, last, next 262 | relativeperiodunit week, month, year 263 | day: monday, tues, wednesday 264 | month: january, feb 265 | 266 | 267 | YYYY-MM-DD (HH:MM:SS MST)? 268 | MM-DD-YYYY (HH:MM:SS MST) 269 | 10 September 2015 (HH:MM:SS MST)? 270 | September, 10 2015 (HH:MM:SS MST)? 271 | September 10 2015 (HH:MM:SS M 272 | 273 | this year 2014 274 | next year 2015 275 | last year 2013 276 | 277 | this month April 278 | next month May 279 | last month Mar 280 | 281 | first day of April 282 | last day of April 283 | 284 | 285 | DONE 3PM 286 | DONE 3:00 PM 287 | DONE 3:00:05 MST 288 | 3PM on January 5th 289 | January 5th at 3:00PM 290 | first saturday _of_ next month 291 | first saturday _of_ next month _at_ 3:00PM 292 | saturday of next week 293 | saturday of last week 294 | saturday next week 295 | monday next week 296 | saturday of this week 297 | saturday at 3:00pm 298 | saturday at 4:00PM 299 | saturday at midn 300 | first of january 301 | last of january 302 | january of next year 303 | first day of january 304 | last day of january 305 | first day of February 306 | 307 | DONE midnight 308 | DONE noon 309 | DONE 3 days ago 310 | DONE ten days 311 | DONE 9 weeks ago // Convert to -9 weeks 312 | DONE -9 weeks 313 | 314 | */ 315 | 316 | relative = regexp.MustCompile(`^(yesterday|today|tomorrow) ?` + time + `$`) 317 | 318 | relative = regexp.MustCompile(`(first|last) day of (this|next|last|previous) (week|month|year)`) 319 | 320 | relative = regexp.MustCompile(`(first|last) day of (` + months + `)(?:\s(\d{4,4}))?`) 321 | 322 | relative = regexp.MustCompile(`(this|next|last|previous) (` + days + `)`) 323 | 324 | relative = regexp.MustCompile(`([0-9]+) (day|week|month|year)s? ago`) 325 | 326 | /* 327 | 328 | 329 | yesterday 11:00 330 | today 11:00 331 | tomorrow 11:00 332 | midnight 333 | noon 334 | DONE +n (second|day|week|month|year)s? 335 | DONE -n (second|day|week|month|year)s? 336 | next (monday|tuesday|wednesday|thursday|friday|saturday|sunday) 11:00 337 | last (monday|tuesday|wednesday|thursday|friday|saturday|sunday) 11:00 338 | next (month|year) 339 | last (month|year) 340 | first day of (january|february|march...|december) 2014 341 | last day of (january|february|march...|december) 2014 342 | first day of (this|next|last) (week|month|year) 343 | last day of (this|next|last) (week|month|year) 344 | first (monday|tuesday|wednesday) of July 2014 345 | last (monday|tuesday|wednesday) of July 2014 346 | n (day|week|month|year)s? ago 347 | Monday|Tuesday|Wednesday|Thursday|Friday 348 | Monday (last|this|next) week 349 | 350 | DONE +1 week 2 days 3 hours 4 minutes 5 seconds 351 | */ 352 | 353 | return m 354 | } 355 | 356 | // @todo deal with timezone 357 | func (m *Moment) strtotimeSetTime(time map[string]int, zone string) { 358 | m.SetHour(time["hour"]).SetMinute(time["minutes"]).SetSecond(time["seconds"]) 359 | } 360 | 361 | func (m Moment) Clone() *Moment { 362 | copy := New() 363 | copy.time = m.GetTime() 364 | 365 | return copy 366 | } 367 | 368 | /** 369 | * Getters 370 | * 371 | */ 372 | // https://groups.google.com/forum/#!topic/golang-nuts/pret7hjDc70 373 | func (m *Moment) Millisecond() { 374 | 375 | } 376 | 377 | func (m *Moment) Second() int { 378 | return m.GetTime().Second() 379 | } 380 | 381 | func (m *Moment) Minute() int { 382 | return m.GetTime().Minute() 383 | } 384 | 385 | func (m *Moment) Hour() int { 386 | return m.GetTime().Hour() 387 | } 388 | 389 | // Day of month 390 | func (m *Moment) Date() int { 391 | return m.DayOfMonth() 392 | } 393 | 394 | // Carbon convenience method 395 | func (m *Moment) DayOfMonth() int { 396 | return m.GetTime().Day() 397 | } 398 | 399 | // Day of week (int or string) 400 | func (m *Moment) Day() time.Weekday { 401 | return m.DayOfWeek() 402 | } 403 | 404 | // Carbon convenience method 405 | func (m *Moment) DayOfWeek() time.Weekday { 406 | return m.GetTime().Weekday() 407 | } 408 | 409 | func (m *Moment) DayOfWeekISO() int { 410 | day := m.GetTime().Weekday() 411 | 412 | if day == time.Sunday { 413 | return 7 414 | } 415 | 416 | return int(day) 417 | } 418 | 419 | func (m *Moment) DayOfYear() int { 420 | return m.GetTime().YearDay() 421 | } 422 | 423 | // Day of Year with zero padding 424 | func (m *Moment) dayOfYearZero() string { 425 | day := m.GetTime().YearDay() 426 | 427 | if day < 10 { 428 | return fmt.Sprintf("00%d", day) 429 | } 430 | 431 | if day < 100 { 432 | return fmt.Sprintf("0%d", day) 433 | } 434 | 435 | return fmt.Sprintf("%d", day) 436 | } 437 | 438 | // todo panic? 439 | func (m *Moment) Weekday(index int) string { 440 | if index > 6 { 441 | panic("Weekday index must be between 0 and 6") 442 | } 443 | 444 | return time.Weekday(index).String() 445 | } 446 | 447 | func (m *Moment) Week() int { 448 | return 0 449 | } 450 | 451 | // Is this the week number where as ISOWeekYear is the number of weeks in the year? 452 | // @see http://stackoverflow.com/questions/18478741/get-weeks-in-year 453 | func (m *Moment) ISOWeek() int { 454 | _, week := m.GetTime().ISOWeek() 455 | 456 | return week 457 | } 458 | 459 | // @todo Consider language support 460 | func (m *Moment) Month() time.Month { 461 | return m.GetTime().Month() 462 | } 463 | 464 | func (m *Moment) Quarter() (quarter int) { 465 | quarter = 4 466 | 467 | switch m.Month() { 468 | case time.January, time.February, time.March: 469 | quarter = 1 470 | case time.April, time.May, time.June: 471 | quarter = 2 472 | case time.July, time.August, time.September: 473 | quarter = 3 474 | } 475 | 476 | return 477 | } 478 | 479 | func (m *Moment) Year() int { 480 | return m.GetTime().Year() 481 | } 482 | 483 | // @see comments for ISOWeek 484 | func (m *Moment) WeekYear() { 485 | 486 | } 487 | 488 | func (m *Moment) ISOWeekYear() { 489 | 490 | } 491 | 492 | /** 493 | * Manipulate 494 | * 495 | */ 496 | func (m *Moment) Add(key string, value int) *Moment { 497 | switch key { 498 | case "years", "year", "y": 499 | m.AddYears(value) 500 | case "months", "month", "M": 501 | m.AddMonths(value) 502 | case "weeks", "week", "w": 503 | m.AddWeeks(value) 504 | case "days", "day", "d": 505 | m.AddDays(value) 506 | case "hours", "hour", "h": 507 | m.AddHours(value) 508 | case "minutes", "minute", "m": 509 | m.AddMinutes(value) 510 | case "seconds", "second", "s": 511 | m.AddSeconds(value) 512 | case "milliseconds", "millisecond", "ms": 513 | 514 | } 515 | 516 | return m 517 | } 518 | 519 | // Carbon 520 | func (m *Moment) AddSeconds(seconds int) *Moment { 521 | return m.addTime(time.Second * time.Duration(seconds)) 522 | } 523 | 524 | // Carbon 525 | func (m *Moment) AddMinutes(minutes int) *Moment { 526 | return m.addTime(time.Minute * time.Duration(minutes)) 527 | } 528 | 529 | // Carbon 530 | func (m *Moment) AddHours(hours int) *Moment { 531 | return m.addTime(time.Hour * time.Duration(hours)) 532 | } 533 | 534 | // Carbon 535 | func (m *Moment) AddDay() *Moment { 536 | return m.AddDays(1) 537 | } 538 | 539 | // Carbon 540 | func (m *Moment) AddDays(days int) *Moment { 541 | m.time = m.GetTime().AddDate(0, 0, days) 542 | 543 | return m 544 | } 545 | 546 | // Carbon 547 | func (m *Moment) AddWeeks(weeks int) *Moment { 548 | return m.AddDays(weeks * 7) 549 | } 550 | 551 | // Carbon 552 | func (m *Moment) AddMonths(months int) *Moment { 553 | m.time = m.GetTime().AddDate(0, months, 0) 554 | 555 | return m 556 | } 557 | 558 | // Carbon 559 | func (m *Moment) AddYears(years int) *Moment { 560 | m.time = m.GetTime().AddDate(years, 0, 0) 561 | 562 | return m 563 | } 564 | 565 | func (m *Moment) addTime(d time.Duration) *Moment { 566 | m.time = m.GetTime().Add(d) 567 | 568 | return m 569 | } 570 | 571 | func (m *Moment) Subtract(key string, value int) *Moment { 572 | switch key { 573 | case "years", "year", "y": 574 | m.SubYears(value) 575 | case "months", "month", "M": 576 | m.SubMonths(value) 577 | case "weeks", "week", "w": 578 | m.SubWeeks(value) 579 | case "days", "day", "d": 580 | m.SubDays(value) 581 | case "hours", "hour", "h": 582 | m.SubHours(value) 583 | case "minutes", "minute", "m": 584 | m.SubMinutes(value) 585 | case "seconds", "second", "s": 586 | m.SubSeconds(value) 587 | case "milliseconds", "millisecond", "ms": 588 | 589 | } 590 | 591 | return m 592 | } 593 | 594 | // Carbon 595 | func (m *Moment) SubSeconds(seconds int) *Moment { 596 | return m.addTime(time.Second * time.Duration(seconds*-1)) 597 | } 598 | 599 | // Carbon 600 | func (m *Moment) SubMinutes(minutes int) *Moment { 601 | return m.addTime(time.Minute * time.Duration(minutes*-1)) 602 | } 603 | 604 | // Carbon 605 | func (m *Moment) SubHours(hours int) *Moment { 606 | return m.addTime(time.Hour * time.Duration(hours*-1)) 607 | } 608 | 609 | // Carbon 610 | func (m *Moment) SubDay() *Moment { 611 | return m.SubDays(1) 612 | } 613 | 614 | // Carbon 615 | func (m *Moment) SubDays(days int) *Moment { 616 | return m.AddDays(days * -1) 617 | } 618 | 619 | func (m *Moment) SubWeeks(weeks int) *Moment { 620 | return m.SubDays(weeks * 7) 621 | } 622 | 623 | // Carbon 624 | func (m *Moment) SubMonths(months int) *Moment { 625 | return m.AddMonths(months * -1) 626 | } 627 | 628 | // Carbon 629 | func (m *Moment) SubYears(years int) *Moment { 630 | return m.AddYears(years * -1) 631 | } 632 | 633 | // Carbon 634 | func (m *Moment) Today() *Moment { 635 | return m.Now() 636 | } 637 | 638 | // Carbon 639 | func (m *Moment) Tomorrow() *Moment { 640 | return m.Today().AddDay() 641 | } 642 | 643 | // Carbon 644 | func (m *Moment) Yesterday() *Moment { 645 | return m.Today().SubDay() 646 | } 647 | 648 | func (m *Moment) StartOf(key string) *Moment { 649 | switch key { 650 | case "year", "y": 651 | m.StartOfYear() 652 | case "month", "M": 653 | m.StartOfMonth() 654 | case "week", "w": 655 | m.StartOfWeek() 656 | case "day", "d": 657 | m.StartOfDay() 658 | case "hour", "h": 659 | if m.Minute() > 0 { 660 | m.SubMinutes(m.Minute()) 661 | } 662 | 663 | if m.Second() > 0 { 664 | m.SubSeconds(m.Second()) 665 | } 666 | case "minute", "m": 667 | if m.Second() > 0 { 668 | m.SubSeconds(m.Second()) 669 | } 670 | case "second", "s": 671 | 672 | } 673 | 674 | return m 675 | } 676 | 677 | // Carbon 678 | func (m *Moment) StartOfDay() *Moment { 679 | if m.Hour() > 0 { 680 | m.SubHours(m.Hour()) 681 | } 682 | 683 | return m.StartOf("hour") 684 | } 685 | 686 | // @todo ISO8601 Starts on Monday 687 | func (m *Moment) StartOfWeek() *Moment { 688 | return m.GoBackTo(time.Monday).StartOfDay() 689 | } 690 | 691 | // Carbon 692 | func (m *Moment) StartOfMonth() *Moment { 693 | return m.SetDay(1).StartOfDay() 694 | } 695 | 696 | // Carbon 697 | func (m *Moment) StartOfYear() *Moment { 698 | return m.SetMonth(time.January).SetDay(1).StartOfDay() 699 | } 700 | 701 | // Carbon 702 | func (m *Moment) EndOf(key string) *Moment { 703 | switch key { 704 | case "year", "y": 705 | m.EndOfYear() 706 | case "month", "M": 707 | m.EndOfMonth() 708 | case "week", "w": 709 | m.EndOfWeek() 710 | case "day", "d": 711 | m.EndOfDay() 712 | case "hour", "h": 713 | if m.Minute() < 59 { 714 | m.AddMinutes(59 - m.Minute()) 715 | } 716 | case "minute", "m": 717 | if m.Second() < 59 { 718 | m.AddSeconds(59 - m.Second()) 719 | } 720 | case "second", "s": 721 | 722 | } 723 | 724 | return m 725 | } 726 | 727 | // Carbon 728 | func (m *Moment) EndOfDay() *Moment { 729 | if m.Hour() < 23 { 730 | m.AddHours(23 - m.Hour()) 731 | } 732 | 733 | return m.EndOf("hour") 734 | } 735 | 736 | // @todo ISO8601 Ends on Sunday 737 | func (m *Moment) EndOfWeek() *Moment { 738 | return m.GoTo(time.Sunday).EndOfDay() 739 | } 740 | 741 | // Carbon 742 | func (m *Moment) EndOfMonth() *Moment { 743 | return m.SetDay(m.DaysInMonth()).EndOfDay() 744 | } 745 | 746 | // Carbon 747 | func (m *Moment) EndOfYear() *Moment { 748 | return m.GoToMonth(time.December).EndOfMonth() 749 | } 750 | 751 | // Custom 752 | func (m *Moment) GoTo(day time.Weekday) *Moment { 753 | if m.Day() == day { 754 | return m 755 | } 756 | 757 | var diff int 758 | if diff = int(day) - int(m.Day()); diff > 0 { 759 | return m.AddDays(diff) 760 | } 761 | 762 | return m.AddDays(7 + diff) 763 | } 764 | 765 | // Custom 766 | func (m *Moment) GoBackTo(day time.Weekday) *Moment { 767 | if m.Day() == day { 768 | return m 769 | } 770 | 771 | var diff int 772 | if diff = int(day) - int(m.Day()); diff > 0 { 773 | return m.SubDays(7 - diff) 774 | } 775 | 776 | return m.SubDays(diff * -1) 777 | } 778 | 779 | // Custom 780 | func (m *Moment) GoToMonth(month time.Month) *Moment { 781 | if m.Month() == month { 782 | return m 783 | } 784 | 785 | var diff int 786 | if diff = int(month - m.Month()); diff > 0 { 787 | return m.AddMonths(diff) 788 | } 789 | 790 | return m.AddMonths(12 + diff) 791 | } 792 | 793 | // Custom 794 | func (m *Moment) GoBackToMonth(month time.Month) *Moment { 795 | if m.Month() == month { 796 | return m 797 | } 798 | 799 | var diff int 800 | if diff = int(month) - int(m.Month()); diff > 0 { 801 | return m.SubMonths(12 - diff) 802 | } 803 | 804 | return m.SubMonths(diff * -1) 805 | } 806 | 807 | func (m *Moment) SetSecond(seconds int) *Moment { 808 | if seconds >= 0 && seconds <= 60 { 809 | return m.AddSeconds(seconds - m.Second()) 810 | } 811 | 812 | return m 813 | } 814 | 815 | func (m *Moment) SetMinute(minute int) *Moment { 816 | if minute >= 0 && minute <= 60 { 817 | return m.AddMinutes(minute - m.Minute()) 818 | } 819 | 820 | return m 821 | } 822 | 823 | func (m *Moment) SetHour(hour int) *Moment { 824 | if hour >= 0 && hour <= 23 { 825 | return m.AddHours(hour - m.Hour()) 826 | } 827 | 828 | return m 829 | } 830 | 831 | // Custom 832 | func (m *Moment) SetDay(day int) *Moment { 833 | if m.DayOfMonth() == day { 834 | return m 835 | } 836 | 837 | return m.AddDays(day - m.DayOfMonth()) 838 | } 839 | 840 | // Custom 841 | func (m *Moment) SetMonth(month time.Month) *Moment { 842 | if m.Month() > month { 843 | return m.GoBackToMonth(month) 844 | } 845 | 846 | return m.GoToMonth(month) 847 | } 848 | 849 | // Custom 850 | func (m *Moment) SetYear(year int) *Moment { 851 | if m.Year() == year { 852 | return m 853 | } 854 | 855 | return m.AddYears(year - m.Year()) 856 | } 857 | 858 | // UTC Mode. @see http://momentjs.com/docs/#/parsing/utc/ 859 | func (m *Moment) UTC() *Moment { 860 | return m 861 | } 862 | 863 | // http://momentjs.com/docs/#/manipulating/timezone-offset/ 864 | func (m *Moment) Zone() int { 865 | _, offset := m.GetTime().Zone() 866 | 867 | return (offset / 60) * -1 868 | } 869 | 870 | /** 871 | * Display 872 | * 873 | */ 874 | func (m *Moment) Format(layout string) string { 875 | format := m.Convert(layout) 876 | hasCustom := false 877 | 878 | formatted := m.GetTime().Format(format) 879 | 880 | if strings.Contains(formatted, "", fmt.Sprintf("%d", m.Unix()), -1) 883 | formatted = strings.Replace(formatted, "", fmt.Sprintf("%d", m.ISOWeek()), -1) 884 | formatted = strings.Replace(formatted, "", fmt.Sprintf("%d", m.DayOfWeek()), -1) 885 | formatted = strings.Replace(formatted, "", fmt.Sprintf("%d", m.DayOfWeekISO()), -1) 886 | formatted = strings.Replace(formatted, "", fmt.Sprintf("%d", m.DayOfYear()), -1) 887 | formatted = strings.Replace(formatted, "", fmt.Sprintf("%d", m.Quarter()), -1) 888 | formatted = strings.Replace(formatted, "", m.dayOfYearZero(), -1) 889 | formatted = strings.Replace(formatted, "", fmt.Sprintf("%d", m.Hour()), -1) 890 | } 891 | 892 | // This has to happen after time.Format 893 | if hasCustom && strings.Contains(formatted, "") { 894 | regex := regexp.MustCompile("([0-9]+)(?:)") 895 | 896 | formatted = regex.ReplaceAllStringFunc(formatted, func(n string) string { 897 | ordinal, _ := strconv.Atoi(strings.Replace(n, "", "", 1)) 898 | return m.ordinal(ordinal) 899 | }) 900 | } 901 | 902 | return formatted 903 | } 904 | 905 | func (m *Moment) FormatGo(layout string) string { 906 | return m.GetTime().Format(layout) 907 | } 908 | 909 | // From Dmytro Shteflyuk @https://groups.google.com/forum/#!topic/golang-nuts/l8NhI74jl-4 910 | func (m *Moment) ordinal(x int) string { 911 | suffix := "th" 912 | switch x % 10 { 913 | case 1: 914 | if x%100 != 11 { 915 | suffix = "st" 916 | } 917 | case 2: 918 | if x%100 != 12 { 919 | suffix = "nd" 920 | } 921 | case 3: 922 | if x%100 != 13 { 923 | suffix = "rd" 924 | } 925 | } 926 | 927 | return strconv.Itoa(x) + suffix 928 | } 929 | 930 | func (m *Moment) FromNow() Diff { 931 | now := new(Moment) 932 | now.Now() 933 | 934 | return m.From(now) 935 | } 936 | 937 | // Carbon 938 | func (m *Moment) From(f *Moment) Diff { 939 | return m.GetDiff(f) 940 | } 941 | 942 | /** 943 | * Difference 944 | * 945 | */ 946 | func (m *Moment) Diff(t *Moment, unit string) int { 947 | diff := m.GetDiff(t) 948 | 949 | switch unit { 950 | case "years": 951 | return diff.InYears() 952 | case "months": 953 | return diff.InMonths() 954 | case "weeks": 955 | return diff.InWeeks() 956 | case "days": 957 | return diff.InDays() 958 | case "hours": 959 | return diff.InHours() 960 | case "minutes": 961 | return diff.InMinutes() 962 | case "seconds": 963 | return diff.InSeconds() 964 | } 965 | 966 | return 0 967 | } 968 | 969 | // Custom 970 | func (m *Moment) GetDiff(t *Moment) Diff { 971 | duration := m.GetTime().Sub(t.GetTime()) 972 | 973 | return Diff{duration} 974 | } 975 | 976 | /** 977 | * Display 978 | * 979 | */ 980 | func (m *Moment) ValueOf() int64 { 981 | return m.Unix() * 1000 982 | } 983 | 984 | func (m *Moment) Unix() int64 { 985 | return m.GetTime().Unix() 986 | } 987 | 988 | func (m *Moment) DaysInMonth() int { 989 | days := 31 990 | switch m.Month() { 991 | case time.April, time.June, time.September, time.November: 992 | days = 30 993 | break 994 | case time.February: 995 | days = 28 996 | if m.IsLeapYear() { 997 | days = 29 998 | } 999 | break 1000 | } 1001 | 1002 | return days 1003 | } 1004 | 1005 | // or ToSlice? 1006 | func (m *Moment) ToArray() []int { 1007 | return []int{ 1008 | m.Year(), 1009 | int(m.Month()), 1010 | m.DayOfMonth(), 1011 | m.Hour(), 1012 | m.Minute(), 1013 | m.Second(), 1014 | } 1015 | } 1016 | 1017 | /** 1018 | * Query 1019 | * 1020 | */ 1021 | func (m *Moment) IsBefore(t Moment) bool { 1022 | return m.GetTime().Before(t.GetTime()) 1023 | } 1024 | 1025 | func (m *Moment) IsSame(t *Moment, layout string) bool { 1026 | return m.Format(layout) == t.Format(layout) 1027 | } 1028 | 1029 | func (m *Moment) IsAfter(t Moment) bool { 1030 | return m.GetTime().After(t.GetTime()) 1031 | } 1032 | 1033 | // Carbon 1034 | func (m *Moment) IsToday() bool { 1035 | today := m.Clone().Today() 1036 | 1037 | return m.Year() == today.Year() && m.Month() == today.Month() && m.Day() == today.Day() 1038 | } 1039 | 1040 | // Carbon 1041 | func (m *Moment) IsTomorrow() bool { 1042 | tomorrow := m.Clone().Tomorrow() 1043 | 1044 | return m.Year() == tomorrow.Year() && m.Month() == tomorrow.Month() && m.Day() == tomorrow.Day() 1045 | } 1046 | 1047 | // Carbon 1048 | func (m *Moment) IsYesterday() bool { 1049 | yesterday := m.Clone().Yesterday() 1050 | 1051 | return m.Year() == yesterday.Year() && m.Month() == yesterday.Month() && m.Day() == yesterday.Day() 1052 | } 1053 | 1054 | // Carbon 1055 | func (m *Moment) IsWeekday() bool { 1056 | return !m.IsWeekend() 1057 | } 1058 | 1059 | // Carbon 1060 | func (m *Moment) IsWeekend() bool { 1061 | return m.DayOfWeek() == time.Sunday || m.DayOfWeek() == time.Saturday 1062 | } 1063 | 1064 | func (m *Moment) IsLeapYear() bool { 1065 | year := m.Year() 1066 | return year%4 == 0 && (year%100 != 0 || year%400 == 0) 1067 | } 1068 | 1069 | // Custom 1070 | func (m *Moment) Range(start Moment, end Moment) bool { 1071 | return m.IsAfter(start) && m.IsBefore(end) 1072 | } 1073 | -------------------------------------------------------------------------------- /moment_parser.go: -------------------------------------------------------------------------------- 1 | package moment 2 | 3 | import ( 4 | "regexp" 5 | "strings" 6 | ) 7 | 8 | type MomentParser struct{} 9 | 10 | /* 11 | + S (makes any number before it ordinal) 12 | + stdDayOfYear 1,2,365 13 | + stdDayOfYearZero 001, 002, 365 14 | + stdDayOfWeek w 0, 1, 2 numeric day of the week (0 = sunday) 15 | + stdDayOfWeekISO N 1 = Monday 16 | + stdWeekOfYear W Iso week number of year 17 | + stdUnix U 18 | + stdQuarter 19 | */ 20 | 21 | // Thanks to https://github.com/fightbulc/moment.php for replacement keys and regex 22 | var moment_replacements = map[string]string{ 23 | "M": "1", // stdNumMonth 1 2 ... 11 12 24 | "Mo": "1", // stdNumMonth 1st 2nd ... 11th 12th 25 | "MM": "01", // stdZeroMonth 01 02 ... 11 12 26 | "MMM": "Jan", // stdMonth Jan Feb ... Nov Dec 27 | "MMMM": "January", // stdLongMonth January February ... November December 28 | "D": "2", // stdDay 1 2 ... 30 30 29 | "Do": "2", // stdDay 1st 2nd ... 30th 31st @todo support st nd th etch 30 | "DD": "02", // stdZeroDay 01 02 ... 30 31 31 | "DDD": "", // Day of the year 1 2 ... 364 365 32 | "DDDo": "", // Day of the year 1st 2nd ... 364th 365th 33 | "DDDD": "", // Day of the year 001 002 ... 364 365 @todo**** 34 | "d": "", // Numeric representation of day of the week 0 1 ... 5 6 35 | "do": "", // 0th 1st ... 5th 6th 36 | "dd": "Mon", // ***Su Mo ... Fr Sa @todo 37 | "ddd": "Mon", // Sun Mon ... Fri Sat 38 | "dddd": "Monday", // stdLongWeekDay Sunday Monday ... Friday Saturday 39 | "e": "", // Numeric representation of day of the week 0 1 ... 5 6 @todo 40 | "E": "", // ISO-8601 numeric representation of the day of the week (added in PHP 5.1.0) 1 2 ... 6 7 @todo 41 | "w": "", // 1 2 ... 52 53 42 | "wo": "", // 1st 2nd ... 52nd 53rd 43 | "ww": "", // ***01 02 ... 52 53 @todo 44 | "W": "", // 1 2 ... 52 53 45 | "Wo": "", // 1st 2nd ... 52nd 53rd 46 | "WW": "", // ***01 02 ... 52 53 @todo 47 | "YY": "06", // stdYear 70 71 ... 29 30 48 | "YYYY": "2006", // stdLongYear 1970 1971 ... 2029 2030 49 | // "gg" : "o", // ISO-8601 year number 70 71 ... 29 30 @todo 50 | // "gggg" : "o", // ***1970 1971 ... 2029 2030 @todo 51 | // "GG" : "o", //70 71 ... 29 30 @todo 52 | // "GGGG" : "o", // ***1970 1971 ... 2029 2030 @todo 53 | "Q": "", 54 | "A": "PM", // stdPM AM PM 55 | "a": "pm", // stdpm am pm 56 | "H": "", // stdHour 0 1 ... 22 23 57 | "HH": "15", // 00 01 ... 22 23 58 | "h": "3", // stdHour12 1 2 ... 11 12 59 | "hh": "03", // stdZeroHour12 01 02 ... 11 12 60 | "m": "4", // stdZeroMinute 0 1 ... 58 59 61 | "mm": "04", // stdZeroMinute 00 01 ... 58 59 62 | "s": "5", // stdSecond 0 1 ... 58 59 63 | "ss": "05", // stdZeroSecond ***00 01 ... 58 59 64 | // "S" : "", //0 1 ... 8 9 65 | // "SS" : "", //0 1 ... 98 99 66 | // "SSS" : "", //0 1 ... 998 999 67 | "z": "MST", //EST CST ... MST PST 68 | "zz": "MST", //EST CST ... MST PST 69 | "Z": "Z07:00", // stdNumColonTZ -07:00 -06:00 ... +06:00 +07:00 70 | "ZZ": "-0700", // stdNumTZ -0700 -0600 ... +0600 +0700 71 | "X": "", // Seconds since unix epoch 1360013296 72 | "LT": "3:04 PM", // 8:30 PM 73 | "L": "01/02/2006", //09/04/1986 74 | "l": "1/2/2006", //9/4/1986 75 | "LL": "January 2 2006", //September 4th 1986 the php s flag isn't supported 76 | "ll": "Jan 2 2006", //Sep 4 1986 77 | "LLL": "January 2 2006 3:04 PM", //September 4th 1986 8:30 PM @todo the php s flag isn't supported 78 | "lll": "Jan 2 2006 3:04 PM", //Sep 4 1986 8:30 PM 79 | "LLLL": "Monday, January 2 2006 3:04 PM", //Thursday, September 4th 1986 8:30 PM the php s flag isn't supported 80 | "llll": "Mon, Jan 2 2006 3:04 PM", //Thu, Sep 4 1986 8:30 PM 81 | } 82 | 83 | func (p *MomentParser) Convert(layout string) string { 84 | pattern := regexp.MustCompile("(LT|LL?L?L?|l{1,4}|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|SS?S?|X|zz?|ZZ?|Q)") 85 | 86 | var match [][]string 87 | if match = pattern.FindAllStringSubmatch(layout, -1); match == nil { 88 | return layout 89 | } 90 | 91 | for i := range match { 92 | if replace, ok := moment_replacements[match[i][0]]; ok { 93 | layout = strings.Replace(layout, match[i][0], replace, 1) 94 | } 95 | } 96 | 97 | return layout 98 | } 99 | -------------------------------------------------------------------------------- /moment_parser_test.go: -------------------------------------------------------------------------------- 1 | package moment 2 | 3 | import ( 4 | . "github.com/smartystreets/goconvey/convey" 5 | "testing" 6 | ) 7 | 8 | func TestMomentParser(t *testing.T) { 9 | parser := new(MomentParser) 10 | 11 | Convey("Given moment month formats", t, func() { 12 | Convey("It should generate the correct golang formats for months", func() { 13 | So(parser.Convert("M"), ShouldEqual, "1") 14 | So(parser.Convert("Mo"), ShouldEqual, "1") 15 | So(parser.Convert("MM"), ShouldEqual, "01") 16 | So(parser.Convert("MMM"), ShouldEqual, "Jan") 17 | So(parser.Convert("MMMM"), ShouldEqual, "January") 18 | }) 19 | }) 20 | 21 | Convey("Given moment day of month formats", t, func() { 22 | Convey("It should generate the correct golang formats for days", func() { 23 | So(parser.Convert("D"), ShouldEqual, "2") 24 | So(parser.Convert("Do"), ShouldEqual, "2") 25 | So(parser.Convert("DD"), ShouldEqual, "02") 26 | }) 27 | }) 28 | 29 | Convey("Given moment day of year formats", t, func() { 30 | Convey("It should generate the correct golang formats for days", func() { 31 | So(parser.Convert("DDD"), ShouldEqual, "") 32 | So(parser.Convert("DDDo"), ShouldEqual, "") 33 | So(parser.Convert("DDDD"), ShouldEqual, "") 34 | }) 35 | }) 36 | 37 | Convey("Given moment day of week formats", t, func() { 38 | Convey("It should generate the correct golang formats for days", func() { 39 | So(parser.Convert("d"), ShouldEqual, "") 40 | So(parser.Convert("do"), ShouldEqual, "") 41 | // So(parser.Convert("dd"), ShouldEqual, "Mo") 42 | So(parser.Convert("ddd"), ShouldEqual, "Mon") 43 | So(parser.Convert("dddd"), ShouldEqual, "Monday") 44 | 45 | // Day of week locale 46 | So(parser.Convert("e"), ShouldEqual, "") 47 | So(parser.Convert("E"), ShouldEqual, "") 48 | }) 49 | }) 50 | 51 | Convey("Given moment week of year formats", t, func() { 52 | Convey("It should generate the correct golang formats for week of year", func() { 53 | So(parser.Convert("w"), ShouldEqual, "") 54 | So(parser.Convert("wo"), ShouldEqual, "") 55 | // So(parser.Convert("ww"), ShouldEqual, "@todo") 56 | So(parser.Convert("W"), ShouldEqual, "") 57 | So(parser.Convert("Wo"), ShouldEqual, "") 58 | // So(parser.Convert("WW"), ShouldEqual, "@todo") 59 | }) 60 | }) 61 | 62 | Convey("Given moment year formats", t, func() { 63 | Convey("It should generate the correct golang formats for years", func() { 64 | So(parser.Convert("YY"), ShouldEqual, "06") 65 | So(parser.Convert("YYYY"), ShouldEqual, "2006") 66 | }) 67 | }) 68 | 69 | Convey("Given moment week year formats", t, func() { 70 | Convey("It should generate the correct golang formats for week years", func() { 71 | // So(parser.Convert("gg"), ShouldEqual, "@todo") 72 | // So(parser.Convert("gggg"), ShouldEqual, "@todo") 73 | 74 | // ISO 75 | // So(parser.Convert("GG"), ShouldEqual, "@todo") 76 | // So(parser.Convert("GGGG"), ShouldEqual, "@todo") 77 | }) 78 | }) 79 | 80 | Convey("Given moment hour formats", t, func() { 81 | Convey("It should generate the correct golang formats for hours", func() { 82 | So(parser.Convert("H"), ShouldEqual, "") 83 | So(parser.Convert("HH"), ShouldEqual, "15") 84 | 85 | So(parser.Convert("h"), ShouldEqual, "3") 86 | So(parser.Convert("hh"), ShouldEqual, "03") 87 | }) 88 | }) 89 | 90 | Convey("Given moment minute formats", t, func() { 91 | Convey("It should generate the correct golang formats for minutes", func() { 92 | So(parser.Convert("m"), ShouldEqual, "4") 93 | So(parser.Convert("mm"), ShouldEqual, "04") 94 | }) 95 | }) 96 | 97 | Convey("Given moment second formats", t, func() { 98 | Convey("It should generate the correct golang formats for seconds", func() { 99 | So(parser.Convert("s"), ShouldEqual, "5") 100 | So(parser.Convert("ss"), ShouldEqual, "05") 101 | }) 102 | }) 103 | 104 | Convey("Given moment localized formats", t, func() { 105 | Convey("It should generate the correct golang formats", func() { 106 | So(parser.Convert("LT"), ShouldEqual, "3:04 PM") 107 | So(parser.Convert("L"), ShouldEqual, "01/02/2006") 108 | So(parser.Convert("l"), ShouldEqual, "1/2/2006") 109 | So(parser.Convert("LL"), ShouldEqual, "January 2 2006") 110 | So(parser.Convert("ll"), ShouldEqual, "Jan 2 2006") 111 | So(parser.Convert("LLL"), ShouldEqual, "January 2 2006 3:04 PM") 112 | So(parser.Convert("lll"), ShouldEqual, "Jan 2 2006 3:04 PM") 113 | So(parser.Convert("LLLL"), ShouldEqual, "Monday, January 2 2006 3:04 PM") 114 | So(parser.Convert("llll"), ShouldEqual, "Mon, Jan 2 2006 3:04 PM") 115 | }) 116 | }) 117 | 118 | Convey("Given moment timezone formats", t, func() { 119 | Convey("It should generate the correct golang timezone formats", func() { 120 | So(parser.Convert("z"), ShouldEqual, "MST") 121 | So(parser.Convert("zz"), ShouldEqual, "MST") 122 | So(parser.Convert("Z"), ShouldEqual, "Z07:00") 123 | So(parser.Convert("ZZ"), ShouldEqual, "-0700") 124 | }) 125 | }) 126 | 127 | Convey("Given moment misc formats", t, func() { 128 | Convey("It should generate the correct golang formats", func() { 129 | So(parser.Convert("Q"), ShouldEqual, "") 130 | So(parser.Convert("A"), ShouldEqual, "PM") 131 | So(parser.Convert("a"), ShouldEqual, "pm") 132 | 133 | So(parser.Convert("X"), ShouldEqual, "") 134 | }) 135 | }) 136 | 137 | Convey("Given a moment format", t, func() { 138 | Convey("It should generate the correct golang time format", func() { 139 | So(parser.Convert("YYYY-MM-DD"), ShouldEqual, "2006-01-02") 140 | So(parser.Convert("YYYY/MM/DD"), ShouldEqual, "2006/01/02") 141 | }) 142 | }) 143 | } 144 | -------------------------------------------------------------------------------- /strftime_parser.go: -------------------------------------------------------------------------------- 1 | package moment 2 | 3 | import ( 4 | "regexp" 5 | "strings" 6 | ) 7 | 8 | type StrftimeParser struct{} 9 | 10 | // Not implemented 11 | // U 12 | // C 13 | 14 | var strftime_replacements = map[string]string{ 15 | "%m": "01", // stdZeroMonth 01 02 ... 11 12 16 | "%b": "Jan", // stdMonth Jan Feb ... Nov Dec 17 | "%h": "Jan", 18 | "%B": "January", // stdLongMonth January February ... November December 19 | "%e": "2", // stdDay 1 2 ... 30 30 20 | "%d": "02", // stdZeroDay 01 02 ... 30 31 21 | "%j": "", // Day of the year ***001 002 ... 364 365 @todo**** 22 | "%w": "", // Numeric representation of day of the week 0 1 ... 5 6 23 | "%u": "", // ISO-8601 numeric representation of the day of the week (added in PHP 5.1.0) 1 2 ... 6 7 @todo 24 | "%a": "Mon", // Sun Mon ... Fri Sat 25 | "%A": "Monday", // stdLongWeekDay Sunday Monday ... Friday Saturday 26 | "%V": "", // ***01 02 ... 52 53 @todo begin with zeros 27 | "%g": "06", // stdYear 70 71 ... 29 30 28 | "%y": "06", 29 | "%G": "2006", // stdLongYear 1970 1971 ... 2029 2030 30 | "%Y": "2006", 31 | "%p": "PM", // stdPM AM PM 32 | "%P": "pm", // stdpm am pm 33 | "%k": "15", // stdHour 0 1 ... 22 23 34 | "%H": "15", // 00 01 ... 22 23 35 | "%l": "3", // stdHour12 1 2 ... 11 12 36 | "%I": "03", // stdZeroHour12 01 02 ... 11 12 37 | "%M": "04", // stdZeroMinute 00 01 ... 58 59 38 | "%S": "05", // stdZeroSecond ***00 01 ... 58 59 39 | "%Z": "MST", //EST CST ... MST PST 40 | "%z": "-0700", // stdNumTZ -0700 -0600 ... +0600 +0700 41 | "%s": "", // Seconds since unix epoch 1360013296 42 | "%r": "03:04:05 PM", 43 | "%R": "15:04", 44 | "%T": "15:04:05", 45 | "%D": "01/02/06", 46 | "%F": "2006-01-02", 47 | "%X": "15:04:05", 48 | "%x": "01/02/06", 49 | } 50 | 51 | func (p *StrftimeParser) Convert(layout string) string { 52 | pattern := regexp.MustCompile("%[mbhBedjwuaAVgyGYpPkHlIMSZzsTrRTDFXx]") 53 | 54 | var match [][]string 55 | if match = pattern.FindAllStringSubmatch(layout, -1); match == nil { 56 | return layout 57 | } 58 | 59 | for i := range match { 60 | if replace, ok := strftime_replacements[match[i][0]]; ok { 61 | layout = strings.Replace(layout, match[i][0], replace, 1) 62 | } 63 | } 64 | 65 | return layout 66 | } 67 | -------------------------------------------------------------------------------- /strftime_parser_test.go: -------------------------------------------------------------------------------- 1 | package moment 2 | 3 | /* 4 | import ( 5 | . "github.com/smartystreets/goconvey/convey" 6 | "testing" 7 | ) 8 | 9 | func TestStrftimeParser(t *testing.T) { 10 | 11 | Convey("Given strftime year formats", t, func() { 12 | 13 | Convey("It should generate the correct golang formats for years", nil) 14 | 15 | }) 16 | 17 | Convey("Given strftime month formats", t, func() { 18 | 19 | Convey("It should generate the correct golang formats for months", nil) 20 | 21 | }) 22 | 23 | Convey("Given strftime day formats", t, func() { 24 | 25 | Convey("It should generate the correct golang formats for days", nil) 26 | 27 | }) 28 | 29 | Convey("Given strftime hour formats", t, func() { 30 | 31 | Convey("It should generate the correct golang formats for hours", nil) 32 | 33 | }) 34 | 35 | Convey("Given strftime minute formats", t, func() { 36 | 37 | Convey("It should generate the correct golang formats for minutes", nil) 38 | 39 | }) 40 | 41 | Convey("Given strftime second formats", t, func() { 42 | 43 | Convey("It should generate the correct golang formats for seconds", nil) 44 | 45 | }) 46 | 47 | Convey("Given strftime convenience formats", t, func() { 48 | 49 | Convey("It should generate the correct convenience golang formats", nil) 50 | 51 | }) 52 | 53 | Convey("Given strftime misc formats", t, func() { 54 | 55 | Convey("It should generate the correct golang formats", nil) 56 | 57 | }) 58 | 59 | Convey("Given a strftime moment format", t, func() { 60 | 61 | Convey("It should generate the correct golang time format", nil) 62 | 63 | }) 64 | 65 | } 66 | */ 67 | --------------------------------------------------------------------------------