Add ScheduleArgs interface for autoscale policies
diff --git a/rackspace/autoscale/v1/policies/fixtures.go b/rackspace/autoscale/v1/policies/fixtures.go
index 67967b0..9334740 100644
--- a/rackspace/autoscale/v1/policies/fixtures.go
+++ b/rackspace/autoscale/v1/policies/fixtures.go
@@ -6,6 +6,7 @@
 	"fmt"
 	"net/http"
 	"testing"
+	"time"
 
 	th "github.com/rackspace/gophercloud/testhelper"
 	"github.com/rackspace/gophercloud/testhelper/client"
@@ -81,7 +82,7 @@
     "cooldown": 0,
     "name": "one time",
     "args": {
-      "at": "2020-04-01T23:00:00.000Z"
+      "at": "2020-04-01T23:00:00Z"
     },
     "type": "schedule",
     "change": -1
@@ -145,9 +146,7 @@
 		Type:            Schedule,
 		AdjustmentType:  Change,
 		AdjustmentValue: float64(-1),
-		Args: map[string]interface{}{
-			"at": "2020-04-01T23:00:00.000Z",
-		},
+		Schedule:        At(time.Date(2020, time.April, 01, 23, 0, 0, 0, time.UTC)),
 	}
 
 	// SundayAfternoonPolicy is a Policy corresponding to the third result in PolicyListBody.
@@ -157,9 +156,7 @@
 		Type:            Schedule,
 		AdjustmentType:  DesiredCapacity,
 		AdjustmentValue: float64(2),
-		Args: map[string]interface{}{
-			"cron": "59 15 * * 0",
-		},
+		Schedule:        Cron("59 15 * * 0"),
 	}
 )
 
diff --git a/rackspace/autoscale/v1/policies/requests.go b/rackspace/autoscale/v1/policies/requests.go
index 52061a1..7aadf98 100644
--- a/rackspace/autoscale/v1/policies/requests.go
+++ b/rackspace/autoscale/v1/policies/requests.go
@@ -9,11 +9,12 @@
 
 // Validation errors returned by create or update operations.
 var (
-	ErrNoName            = errors.New("Policy name cannot by empty.")
-	ErrNoArgs            = errors.New("Args cannot be nil for schedule policies.")
+	ErrNoName            = errors.New("Policy name cannot be empty.")
+	ErrNoSchedule        = errors.New("Schedule cannot be nil for schedule policies.")
 	ErrCooldownRange     = errors.New("Cooldown is out of range (0, 86400).")
 	ErrUnknownType       = errors.New("Unknown policy type.")
 	ErrUnknownAdjustment = errors.New("Unknown adjustment type.")
+	ErrEmptyCron         = errors.New("Cron argument cannot be empty.")
 )
 
 // List returns all scaling policies for a group.
@@ -57,8 +58,9 @@
 	// an integer.
 	AdjustmentValue float64
 
-	// Additional configuration options for some types of policy.
-	Args map[string]interface{}
+	// Value determining Schedule policy behavior, or nil for Webhook policies.
+	// This should be an appropriately configured Cron or an At value.
+	Schedule ScheduleArgs
 }
 
 // ToPolicyCreateMap converts a slice of CreateOpt structs into a map for use
@@ -71,8 +73,8 @@
 			return nil, ErrNoName
 		}
 
-		if o.Type == Schedule && o.Args == nil {
-			return nil, ErrNoArgs
+		if o.Type == Schedule && o.Schedule == nil {
+			return nil, ErrNoSchedule
 		}
 
 		if ok := validateType(o.Type); !ok {
@@ -95,8 +97,14 @@
 			return nil, err
 		}
 
-		if o.Args != nil {
-			policy["args"] = o.Args
+		if o.Schedule != nil {
+			args, err := o.Schedule.ToPolicyArgs()
+
+			if err != nil {
+				return nil, err
+			}
+
+			policy["args"] = args
 		}
 
 		policies = append(policies, policy)
@@ -161,8 +169,9 @@
 	// an integer.
 	AdjustmentValue float64
 
-	// Additional configuration options for some types of policy.
-	Args map[string]interface{}
+	// Value determining Schedule policy behavior, or nil for Webhook policies.
+	// This should be an appropriately configured Cron or an At value.
+	Schedule ScheduleArgs
 }
 
 // ToPolicyUpdateMap converts an UpdateOpts struct into a map for use as the
@@ -172,8 +181,8 @@
 		return nil, ErrNoName
 	}
 
-	if opts.Type == Schedule && opts.Args == nil {
-		return nil, ErrNoArgs
+	if opts.Type == Schedule && opts.Schedule == nil {
+		return nil, ErrNoSchedule
 	}
 
 	if ok := validateType(opts.Type); !ok {
@@ -196,8 +205,14 @@
 		return nil, err
 	}
 
-	if opts.Args != nil {
-		policy["args"] = opts.Args
+	if opts.Schedule != nil {
+		args, err := opts.Schedule.ToPolicyArgs()
+
+		if err != nil {
+			return nil, err
+		}
+
+		policy["args"] = args
 	}
 
 	return policy, nil
diff --git a/rackspace/autoscale/v1/policies/requests_test.go b/rackspace/autoscale/v1/policies/requests_test.go
index 3f7bb88..88a52a7 100644
--- a/rackspace/autoscale/v1/policies/requests_test.go
+++ b/rackspace/autoscale/v1/policies/requests_test.go
@@ -2,6 +2,7 @@
 
 import (
 	"testing"
+	"time"
 
 	"github.com/rackspace/gophercloud/pagination"
 	th "github.com/rackspace/gophercloud/testhelper"
@@ -53,6 +54,7 @@
 	defer th.TeardownHTTP()
 	HandlePolicyCreateSuccessfully(t)
 
+	oneTime := time.Date(2020, time.April, 01, 23, 0, 0, 0, time.UTC)
 	client := client.ServiceClient()
 	opts := CreateOpts{
 		{
@@ -67,18 +69,14 @@
 			Type:            Schedule,
 			AdjustmentType:  Change,
 			AdjustmentValue: -1,
-			Args: map[string]interface{}{
-				"at": "2020-04-01T23:00:00.000Z",
-			},
+			Schedule:        At(oneTime),
 		},
 		{
 			Name:            "sunday afternoon",
 			Type:            Schedule,
 			AdjustmentType:  DesiredCapacity,
 			AdjustmentValue: 2,
-			Args: map[string]interface{}{
-				"cron": "59 15 * * 0",
-			},
+			Schedule:        Cron("59 15 * * 0"),
 		},
 	}
 
diff --git a/rackspace/autoscale/v1/policies/results.go b/rackspace/autoscale/v1/policies/results.go
index 95d03ca..99d8b1a 100644
--- a/rackspace/autoscale/v1/policies/results.go
+++ b/rackspace/autoscale/v1/policies/results.go
@@ -1,6 +1,8 @@
 package policies
 
 import (
+	"time"
+
 	"github.com/mitchellh/mapstructure"
 
 	"github.com/rackspace/gophercloud"
@@ -87,6 +89,44 @@
 	DesiredCapacity AdjustmentType = "desiredCapacity"
 )
 
+// ScheduleArgs is implemented by types that can be converted into arguments for
+// policies with type Schedule.
+type ScheduleArgs interface {
+	ToPolicyArgs() (map[string]string, error)
+}
+
+// At satisfies the ScheduleArgs interface and can be used to configure a policy
+// to execute a particular time.
+type At time.Time
+
+// ToPolicyArgs returns a key and value for use in constructing arguments to
+// schedule policies.
+func (at At) ToPolicyArgs() (map[string]string, error) {
+	t := time.Time(at)
+
+	args := make(map[string]string)
+	args["at"] = t.UTC().Format(time.RFC3339)
+
+	return args, nil
+}
+
+// Cron satisfies the ScheduleArgs interface and can be used to configure a
+// policy that executes at regular intervals.
+type Cron string
+
+// ToPolicyArgs returns a key and value for use in constructing arguments to
+// schedule policies.
+func (cron Cron) ToPolicyArgs() (map[string]string, error) {
+	if cron == "" {
+		return nil, ErrEmptyCron
+	}
+
+	args := make(map[string]string)
+	args["cron"] = string(cron)
+
+	return args, nil
+}
+
 // Policy represents a scaling policy.
 type Policy struct {
 	// UUID for the policy.
@@ -107,8 +147,9 @@
 	// The numeric value of the adjustment in capacity.
 	AdjustmentValue float64
 
-	// Additional configuration options for some types of policy.
-	Args map[string]interface{}
+	// Arguments determining Schedule policy behavior, or nil for Webhook
+	// policies.
+	Schedule ScheduleArgs
 }
 
 // This is an intermediate representation of the exported Policy type.  The
@@ -126,7 +167,7 @@
 	DesiredCapacity interface{} `mapstructure:"desiredCapacity"`
 
 	// Additional configuration options for schedule policies.
-	Args map[string]interface{} `mapstructure:"args"`
+	Args map[string]string `mapstructure:"args"`
 }
 
 // Assemble a Policy from the intermediate policy struct.
@@ -138,7 +179,14 @@
 	policy.Type = p.Type
 	policy.Cooldown = p.Cooldown
 
-	policy.Args = p.Args
+	if cron, ok := p.Args["cron"]; ok {
+		policy.Schedule = Cron(cron)
+	} else if at, ok := p.Args["at"]; ok {
+		// Set an At schedule if the "at" argument parses as an RFC3339 time.
+		if t, err := time.Parse(time.RFC3339, at); err == nil {
+			policy.Schedule = At(t)
+		}
+	}
 
 	if v, ok := p.Change.(float64); ok {
 		policy.AdjustmentType = Change